feat(pre-push-safety-net): compare targeted base and head evidence

This commit is contained in:
Marius Ciepluch
2026-09-13 12:42:15 +02:00
parent 6ba2b06353
commit eb320730b6
11 changed files with 2077 additions and 18 deletions
+17
View File
@@ -197,3 +197,20 @@ unshipped test-path records. Runtime origin remains unspecified without declarat
readable Python graph and exercises the production taint, dominance and configuration
batch queries. Each must return the actual source filename in its census. This
checks query compilation and census transport, not security-property coverage.
## Targeted novelty comparison smoke
After rebuilding the image, run a declared JavaScript guard-removal comparison:
```bash
docker run --rm -i --network none --entrypoint python openultrasast:dev - --case guard < ops/smoke_delta.py
```
Use `--case movement` for unchanged JavaScript backlog shifted by comments, or
`--case connection` for a PHP input newly reaching an unchanged operation. Every
case verifies immutable base/head bytes, runs the existing driver and targeted
comparison under one 900-second lab budget, and checks that dirty checkout/index
state survives. Output contains an evidence JSON object followed by a completion
JSON object; success requires exit zero and `verified: true` in that final object.
These declared regions exercise comparison mechanics; full replay, normal alert
admission and hook latency are separate evaluation work.
+138
View File
@@ -0,0 +1,138 @@
"""Real Git/Joern novelty controls; no hook admission or latency claim."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import subprocess
import tempfile
import time
from dataclasses import asdict
from pathlib import Path
from openultrasast.cpg.backend import JoernBackend
from openultrasast.model.contracts import ExecutionBudget
from openultrasast.model.regions import ScanRegion
from openultrasast.model.scan import ScanBudget, scan_repository
from openultrasast.push.policy import compare_targeted_base, semantics_digest
from openultrasast.push.snapshot import SnapshotAdapter
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--case", choices=("guard", "movement", "connection"), required=True)
args = parser.parse_args()
before = (
b"export function handler(req) {\n"
b" if (!authenticate(req)) return;\n"
b" deleteUser(req.params.id);\n} // handler\n"
b"export function sibling(other) {\n"
b" if (!authenticate(other)) return;\n"
b" deleteUser(other.params.id);\n} // sibling\n"
)
after = before.replace(b" if (!authenticate(req)) return;\n", b"", 1)
language, family, filename = "javascript", "access_control", "api.js"
expected = "worsened"
if args.case == "movement":
before = after
after = b"// documentation moved this existing operation\n\n" + before
expected = "unchanged"
elif args.case == "connection":
language, family, filename = "php", "injection", "api.php"
before = b'<?php\nfunction handler($req) {\n $value = "fixed";\n eval($value);\n}\n'
after = before.replace(b'$value = "fixed";', b"$value = $req;")
expected = "new"
started = time.monotonic()
budget = ExecutionBudget(started + 900, 2.0)
report: dict[str, object] = {
"case": args.case,
"purpose": (
"Real targeted comparison with declared regions and Git context; no automatic context projection, full replay or admission"
),
"shared_budget_seconds": 900,
}
with tempfile.TemporaryDirectory(prefix="ousast-delta-smoke-") as temporary:
repo = Path(temporary)
def git(*parts: str) -> bytes:
return subprocess.run(["git", "-C", str(repo), *parts], check=True, capture_output=True, timeout=10).stdout
git("init")
git("config", "user.name", "Delta smoke")
git("config", "user.email", "[email protected]")
git("config", "core.hooksPath", os.devnull)
source = repo / filename
source.write_bytes(before)
git("add", filename)
git("commit", "-m", "base")
base_oid = git("rev-parse", "HEAD").decode().strip()
source.write_bytes(after)
git("add", filename)
git("commit", "-m", args.case)
head_oid = git("rev-parse", "HEAD").decode().strip()
source.write_bytes(b"uncommitted content\n")
index = (repo / ".git/index").read_bytes()
status = git("status", "--porcelain=v1", "-z")
adapter = SnapshotAdapter(repo)
(comparison,) = adapter.resolve_updates(f"refs/heads/topic {head_oid} refs/heads/topic {base_oid}\n").comparisons
context = adapter.compare(comparison, declaration_paths=(), budget=budget)
regions = (ScanRegion(filename, "handler", language, (family,), 1.0, "entry_point"),)
backend = JoernBackend(heap_mb=1024)
semantics = semantics_digest(engine_identity="joern-4.0.625/frontend-retention-v1", options={"max_regions": 1})
with adapter.materialize(base_oid, budget=budget) as base_snapshot, adapter.materialize(head_oid, budget=budget) as head_snapshot:
assert (base_snapshot.root / filename).read_bytes() == before and before
assert (head_snapshot.root / filename).read_bytes() == after and after
head = scan_repository(
head_snapshot.root,
regions,
backend=backend,
budget=ScanBudget(max_regions=1),
execution_budget=budget,
ranking_mode="evidence",
unit="delta-smoke",
population_complete=True,
)
assert head.findings and head.scope and head.question_outcomes, head
assert not head.degradations and all(q.status == "completed" for q in head.question_outcomes), head
delta = compare_targeted_base(
base_snapshot.root,
head=head,
head_regions=regions,
base_regions=regions,
context=context,
backend=backend,
execution_budget=budget,
scan_budget=ScanBudget(max_regions=1),
head_semantics=semantics,
base_semantics=semantics,
)
report.update(
{
"comparison": comparison.to_payload(),
"context": context.to_payload(),
"base_input": {"bytes": len(before), "sha256": hashlib.sha256(before).hexdigest()},
"head_input": {"bytes": len(after), "sha256": hashlib.sha256(after).hexdigest()},
"head": asdict(head),
"delta": asdict(delta),
"expected_novelty": expected,
}
)
# Emit evidence before assertions so a failed instrument cannot look like a clean zero.
print(json.dumps(report, indent=2), flush=True)
assert delta.base_scan and delta.base_scan.question_outcomes, delta
assert not delta.base_scan.degradations, delta
assert all(q.status == "completed" for q in delta.base_scan.question_outcomes), delta
assert delta.candidates and all(c.novelty == expected for c in delta.candidates), delta
assert not delta.coverage_reasons, delta
assert not base_snapshot.root.exists() and not head_snapshot.root.exists()
assert source.read_bytes() == b"uncommitted content\n"
assert (repo / ".git/index").read_bytes() == index
assert git("status", "--porcelain=v1", "-z") == status
# A separate completion record distinguishes all assertions passing from partial JSON.
print(json.dumps({"verified": True, "case": args.case, "elapsed_seconds": round(time.monotonic() - started, 2)}))
if __name__ == "__main__":
main()