mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
222 lines
9.1 KiB
Python
222 lines
9.1 KiB
Python
"""Synthetic witnesses test containment; no detector admission or real provider calls."""
|
|||
|
|
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
from dataclasses import replace
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from test_push_report import report_with
|
||
|
|
|
||
|
|
from openultrasast.config import ModelConfig, ResolvedConfig
|
||
|
|
from openultrasast.model.contracts import ExecutionBudget
|
||
|
|
from openultrasast.push.assistance import assist
|
||
|
|
from openultrasast.tool_hunter import ChatResponse
|
||
|
|
|
||
|
|
|
||
|
|
def settings():
|
||
|
|
return ResolvedConfig(models=ModelConfig(hunter="control-model", chat_base_url="http://127.0.0.1:1", chat_api_key_env="CONTROL_KEY"))
|
||
|
|
|
||
|
|
|
||
|
|
def budget(seconds=2):
|
||
|
|
return ExecutionBudget(time.monotonic() + seconds, 0.3)
|
||
|
|
|
||
|
|
|
||
|
|
class Client:
|
||
|
|
def __init__(self, log, response, delay=0):
|
||
|
|
self.log, self.response, self.delay = log, response, delay
|
||
|
|
self.usage = []
|
||
|
|
|
||
|
|
def complete(self, **kwargs):
|
||
|
|
self.log.write_text(json.dumps(kwargs))
|
||
|
|
time.sleep(self.delay)
|
||
|
|
self.usage.append({"prompt_tokens": 12, "completion_tokens": 6})
|
||
|
|
return ChatResponse(content=self.response)
|
||
|
|
|
||
|
|
|
||
|
|
def test_default_never_resolves_endpoint(monkeypatch):
|
||
|
|
import openultrasast.model.endpoint as endpoint
|
||
|
|
|
||
|
|
def unexpected(*args, **kwargs):
|
||
|
|
raise AssertionError("default must not resolve a provider")
|
||
|
|
|
||
|
|
monkeypatch.setattr(endpoint, "resolve_chat_endpoint", unexpected)
|
||
|
|
report = report_with()
|
||
|
|
assert assist(report, config=None, budget=budget()) is report
|
||
|
|
|
||
|
|
|
||
|
|
def test_only_existing_witness_can_be_selected_and_input_redacted(tmp_path):
|
||
|
|
report = report_with()
|
||
|
|
secret = "sk-" + "z" * 24
|
||
|
|
defect = report.admission.defects[0]
|
||
|
|
report = replace(
|
||
|
|
report,
|
||
|
|
admission=replace(
|
||
|
|
report.admission, defects=(replace(defect, witnesses=(defect.witnesses[0], "request -> exec; api_key = " + secret)),)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
client = Client(tmp_path / "request", '{"selections":[{"defect_id":"defect-0","witness_index":1}]}')
|
||
|
|
result = assist(report, config=settings(), budget=budget(), client=client)
|
||
|
|
assert secret not in client.log.read_text()
|
||
|
|
assert "REDACTED" in client.log.read_text()
|
||
|
|
assert result.result == report.result and result.admission == report.admission and result.scans == report.scans
|
||
|
|
assert result.model_assistance["status"] == "completed"
|
||
|
|
assert result.model_assistance["selections"] == {"defect-0": 1}
|
||
|
|
assert result.model_assistance["usage"][0]["prompt_tokens"] == 12
|
||
|
|
assert result.model_assistance["cost_usd"] is None
|
||
|
|
assert result.provenance["model"] == "control-model"
|
||
|
|
assert result.model_assistance["prompt_sha256"]
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize(
|
||
|
|
"reply",
|
||
|
|
[
|
||
|
|
'{"rung":"execution_confirmed","finding":"invented"}',
|
||
|
|
'{"selections":[{"defect_id":"new-defect","witness_index":0}]}',
|
||
|
|
'{"selections":[{"defect_id":"defect-0","witness_index":999}]}',
|
||
|
|
],
|
||
|
|
)
|
||
|
|
def test_misleading_response_cannot_create_claims(tmp_path, reply):
|
||
|
|
report = report_with()
|
||
|
|
result = assist(report, config=settings(), budget=budget(), client=Client(tmp_path / "request", reply))
|
||
|
|
assert result.result == report.result and result.admission == report.admission
|
||
|
|
assert result.model_assistance["status"] == "invalid_selection"
|
||
|
|
assert result.model_assistance["selections"] == {}
|
||
|
|
assert reply not in json.dumps(dict(result.model_assistance))
|
||
|
|
|
||
|
|
|
||
|
|
def test_delayed_model_cannot_extend_deadline(tmp_path):
|
||
|
|
report = report_with()
|
||
|
|
start = time.monotonic()
|
||
|
|
result = assist(report, config=settings(), budget=budget(0.15), client=Client(tmp_path / "request", "{}", 5))
|
||
|
|
assert time.monotonic() - start < 0.6
|
||
|
|
assert result.result == report.result
|
||
|
|
assert result.model_assistance["status"] == "deadline_exhausted"
|
||
|
|
assert result.model_assistance["cost_usd"] is None
|
||
|
|
assert json.loads((tmp_path / "request").read_text())["timeout_seconds"] <= 0.15
|
||
|
|
|
||
|
|
|
||
|
|
def test_unadmitted_candidates_never_sent(tmp_path):
|
||
|
|
report = report_with(admitted=False)
|
||
|
|
client = Client(tmp_path / "request", "{}")
|
||
|
|
result = assist(report, config=settings(), budget=budget(), client=client)
|
||
|
|
assert not client.log.exists()
|
||
|
|
assert result.result == report.result
|
||
|
|
assert result.model_assistance["status"] == "no_admitted_witnesses"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("priced", [False, True])
|
||
|
|
def test_existing_endpoint_adapter_and_cost_receipt(tmp_path, monkeypatch, priced):
|
||
|
|
import threading
|
||
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||
|
|
|
||
|
|
import openultrasast.model.endpoint as endpoints
|
||
|
|
|
||
|
|
if priced:
|
||
|
|
monkeypatch.setitem(endpoints._PRICES, "control-model", endpoints.Prices(1, 2, 3))
|
||
|
|
requests = []
|
||
|
|
|
||
|
|
class Handler(BaseHTTPRequestHandler):
|
||
|
|
def do_POST(self):
|
||
|
|
requests.append(json.loads(self.rfile.read(int(self.headers["Content-Length"]))))
|
||
|
|
data = json.dumps(
|
||
|
|
{
|
||
|
|
"choices": [{"message": {"content": '{"selections":[{"defect_id":"defect-0","witness_index":0}]}'}}],
|
||
|
|
"usage": {"prompt_tokens": 12, "completion_tokens": 6},
|
||
|
|
}
|
||
|
|
).encode()
|
||
|
|
self.send_response(200)
|
||
|
|
self.send_header("Content-Length", str(len(data)))
|
||
|
|
self.end_headers()
|
||
|
|
self.wfile.write(data)
|
||
|
|
|
||
|
|
def log_message(self, *args):
|
||
|
|
pass
|
||
|
|
|
||
|
|
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
||
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||
|
|
thread.start()
|
||
|
|
monkeypatch.delenv(endpoints.CLIENT_ENV, raising=False)
|
||
|
|
monkeypatch.setenv("CONTROL_KEY", "local-control-only")
|
||
|
|
config = tmp_path / "models.toml"
|
||
|
|
config.write_text(
|
||
|
|
f'[models]\nhunter="control-model"\nchat_base_url="http://127.0.0.1:{server.server_port}"\nchat_api_key_env="CONTROL_KEY"\n'
|
||
|
|
)
|
||
|
|
try:
|
||
|
|
report = report_with()
|
||
|
|
result = assist(report, config=config, budget=budget(3))
|
||
|
|
assert len(requests) == 1
|
||
|
|
assert requests[0]["model"] == "control-model"
|
||
|
|
assert result.model_assistance["status"] == "completed"
|
||
|
|
assert result.model_assistance["completed_calls"] == 1
|
||
|
|
assert result.model_assistance["usage"] == [{"prompt_tokens": 12, "completion_tokens": 6}]
|
||
|
|
assert result.model_assistance["cost_status"] == ("recorded" if priced else "unknown")
|
||
|
|
assert result.model_assistance["cost_usd"] == (pytest.approx(42 / 1_000_000) if priced else None)
|
||
|
|
assert "local-control-only" not in json.dumps(dict(result.model_assistance))
|
||
|
|
assert result.result == report.result
|
||
|
|
finally:
|
||
|
|
server.shutdown()
|
||
|
|
server.server_close()
|
||
|
|
thread.join()
|
||
|
|
|
||
|
|
|
||
|
|
def test_selected_witness_rendering_redacts_known_secret(tmp_path):
|
||
|
|
from openultrasast.push.report import render_report
|
||
|
|
|
||
|
|
report = report_with()
|
||
|
|
defect = report.admission.defects[0]
|
||
|
|
secret = "sk-" + "q" * 24
|
||
|
|
disposition = report.admission.dispositions[0]
|
||
|
|
disposition = replace(disposition, candidate=replace(disposition.candidate, delta=replace(disposition.candidate.delta, witness=secret)))
|
||
|
|
report = replace(
|
||
|
|
report, admission=replace(report.admission, defects=(replace(defect, witnesses=(secret,)),), dispositions=(disposition,))
|
||
|
|
)
|
||
|
|
result = assist(
|
||
|
|
report,
|
||
|
|
config=settings(),
|
||
|
|
budget=budget(),
|
||
|
|
client=Client(tmp_path / "request", '{"selections":[{"defect_id":"defect-0","witness_index":0}]}'),
|
||
|
|
)
|
||
|
|
assert secret not in render_report(result, artifact=None)
|
||
|
|
assert "REDACTED" in render_report(result, artifact=None)
|
||
|
|
|
||
|
|
|
||
|
|
def test_explicit_config_does_not_read_project_dotenv(tmp_path, monkeypatch):
|
||
|
|
import openultrasast.config as configuration
|
||
|
|
from openultrasast.cli import main
|
||
|
|
|
||
|
|
def forbidden(*args, **kwargs):
|
||
|
|
raise AssertionError("push must not read project .env")
|
||
|
|
|
||
|
|
monkeypatch.setattr(configuration, "load_dotenv", forbidden)
|
||
|
|
monkeypatch.setattr("openultrasast.cli.load_dotenv", forbidden)
|
||
|
|
config = tmp_path / "config.toml"
|
||
|
|
config.write_text('[models]\nhunter="test"\nchat_base_url="http://127.0.0.1:1"\n')
|
||
|
|
result = assist(report_with(admitted=False), config=config, budget=budget())
|
||
|
|
assert result.model_assistance["status"] == "no_admitted_witnesses"
|
||
|
|
with pytest.raises(SystemExit) as stopped:
|
||
|
|
main(["pre-push", "--help"])
|
||
|
|
assert stopped.value.code == 0
|
||
|
|
|
||
|
|
|
||
|
|
def test_selected_witness_keeps_its_location_and_change_binding():
|
||
|
|
from openultrasast.push.report import render_report
|
||
|
|
|
||
|
|
report = report_with()
|
||
|
|
defect = report.admission.defects[0]
|
||
|
|
disposition = report.admission.dispositions[0]
|
||
|
|
delta = disposition.candidate.delta
|
||
|
|
second = replace(delta, witness="second request -> exec", head_operation=replace(delta.head_operation, line=9))
|
||
|
|
report = replace(
|
||
|
|
report,
|
||
|
|
admission=replace(
|
||
|
|
report.admission,
|
||
|
|
defects=(replace(defect, witnesses=(*defect.witnesses, second.witness), locations=(*defect.locations, ("handler.src", 9))),),
|
||
|
|
dispositions=(*report.admission.dispositions, replace(disposition, candidate=replace(disposition.candidate, delta=second))),
|
||
|
|
),
|
||
|
|
model_assistance={"selections": {"defect-0": 1}},
|
||
|
|
)
|
||
|
|
rendered = render_report(report, artifact=None)
|
||
|
|
assert "handler.src:9" in rendered
|
||
|
|
assert "second request -> exec" in rendered
|
||
|
|
assert report.result.actionable_defect_ids == ("defect-0",)
|