mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
ChangeContext records the paths Git reports as added. An entailed, undischarged operation in an added file, with complete head evidence and no identical operation in any base answer, is classified new (file_added_in_head) instead of base_incomplete; admission accepts that reason as change evidence. Unpaired add/delete pushes and every other unknown comparison keep their vetoes. An engine finding whose novelty is established and whose only vetoes are capability vetoes is shown as an advisory engine finding, not an alert. Requirement 9.4. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
97 lines
3.8 KiB
Python
97 lines
3.8 KiB
Python
"""A file new in head is `new`, not `comparison unknown` (task 17.4, Requirement 9.4)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import replace
|
|
|
|
from test_push_admission import control
|
|
from test_push_policy import delta, scan
|
|
from test_push_report import budget, report_with
|
|
|
|
from openultrasast.model.contracts import ChangeContext, ChangedSpan
|
|
from openultrasast.push.policy import admit_candidates
|
|
from openultrasast.push.report import deliver_report
|
|
|
|
|
|
def added(*, boundaries=(), listed=True):
|
|
return ChangeContext(
|
|
"base",
|
|
"head",
|
|
("a.js",),
|
|
(),
|
|
(ChangedSpan("a.js", 1, 6, "head"),),
|
|
(),
|
|
(),
|
|
(),
|
|
tuple(boundaries),
|
|
added_paths=("a.js",) if listed else (),
|
|
)
|
|
|
|
|
|
def test_snapshot_records_added_paths_and_not_modified_ones(tmp_path):
|
|
import json
|
|
|
|
from test_push_runner import history
|
|
|
|
from openultrasast.cpg.backend import NullBackend
|
|
from openultrasast.push.runner import replay
|
|
|
|
root, _, base, git = history(tmp_path)
|
|
(root / "debug.js").write_text("eval(x)\n")
|
|
(root / "api.js").write_text("changed\n")
|
|
git("add", "debug.js", "api.js")
|
|
git("commit", "-m", "add and modify")
|
|
head = git("rev-parse", "HEAD")
|
|
replay(root, base=base, head=head, artifact=tmp_path / "r.json", backend=NullBackend())
|
|
context = json.loads((tmp_path / "r.json").read_text())["change_context"]
|
|
assert context["added_paths"] == [b"debug.js".hex()]
|
|
|
|
|
|
def test_operation_in_an_added_file_is_new_without_any_base_answer():
|
|
result = delta(scan(), None, added())
|
|
assert (result.novelty, result.reason) == ("new", "file_added_in_head")
|
|
assert result.change_evidence == ("head:a.js:1-6",)
|
|
|
|
|
|
def test_the_same_shape_without_the_added_fact_keeps_its_veto():
|
|
assert delta(scan(), None, added(listed=False)).novelty == "unknown"
|
|
|
|
|
|
def test_unpaired_add_and_delete_stays_unknown():
|
|
result = delta(scan(), None, added(boundaries=("unmatched_added_deleted_path_correspondence",)))
|
|
assert (result.novelty, result.reason) == ("unknown", "change_context_unresolved")
|
|
|
|
|
|
def test_an_operation_already_in_the_base_elsewhere_is_movement_not_new():
|
|
result = delta(scan(), scan(path="b.js", line=9), added())
|
|
assert (result.novelty, result.reason) == ("unknown", "operation_moved_within_change")
|
|
|
|
|
|
def test_unentailed_or_discharged_claims_in_added_files_are_not_new():
|
|
assert delta(scan(sanitized=True), None, added()).novelty == "unknown"
|
|
|
|
|
|
def test_admission_accepts_the_added_file_reason():
|
|
candidate, capability, _ = control()
|
|
new_file = replace(candidate, delta=replace(candidate.delta, reason="file_added_in_head"))
|
|
assert len(admit_candidates((new_file,), capabilities=(capability,)).defects) == 1
|
|
unqualified = admit_candidates((new_file,))
|
|
assert unqualified.dispositions[0].reasons == ("capability_unavailable",)
|
|
|
|
|
|
def test_unqualified_new_engine_finding_is_shown_as_advisory_not_alert(tmp_path):
|
|
report = report_with(1, admitted=False)
|
|
delivered = deliver_report(report, tmp_path / "r.json", execution_budget=budget())
|
|
assert delivered.exit_code == 0 and delivered.result.actionable_defect_ids == ()
|
|
assert "Engine: 1 finding(s) this push introduced. Advisory" in delivered.text
|
|
assert "- injection at handler.src:4 (new): request.input -> exec(command)" in delivered.text
|
|
|
|
|
|
def test_a_comparison_veto_keeps_an_engine_finding_out_of_the_terminal(tmp_path):
|
|
report = report_with(1, admitted=False)
|
|
disposition = report.admission.dispositions[0]
|
|
vetoed = replace(disposition, reasons=(*disposition.reasons, "comparison_unknown"))
|
|
report = replace(report, admission=replace(report.admission, dispositions=(vetoed,)))
|
|
delivered = deliver_report(report, tmp_path / "r.json", execution_budget=budget())
|
|
assert "Engine:" not in delivered.text
|