feat(pre-push-safety-net): exclude vendor inputs and partition frontend coverage

This commit is contained in:
Marius Ciepluch
2026-09-13 11:53:29 +02:00
parent 51fabef9ea
commit 6ba2b06353
29 changed files with 1996 additions and 72 deletions
+16 -5
View File
@@ -181,8 +181,19 @@ unsupported Go partition is reported explicitly. One ranker decision and one reg
limit cover both supported frontends. The shared 900-second budget is a lab allowance;
this check establishes neither production hook latency nor framework admission.
Current status (2026-09-13): this is a failing regression on Joern 4.0.625. The
projected JavaScript test file is readable but absent from the resulting graph.
See `benchmarks/measurements/2026-09-13-partition-retention-blocker.json` and the
proposed contributor-scan frontend-retention prerequisite. An exit 1 is expected
until that engine contract is repaired; do not treat it as a passing partition smoke.
Stock Joern 4.0.625 fails the retention check: the projected test file is readable
but absent from the graph. The approved source adaptation fixes both test filters;
see `frontend-retention/README.md` for build pins and inclusion policy, and
`benchmarks/measurements/2026-09-13-frontend-retention-smoke.json` for passing
foundation verification. The earlier failing evidence remains in
`benchmarks/measurements/2026-09-13-partition-retention-blocker.json`.
Setting `OUSAST_SMOKE_INCLUDE_TESTS=0` in the container explicitly disables the
backend inclusion policy; that negative control must fail the retention assertion.
The expanded task 3.4 smoke also verifies first-party browser-path retention and
unshipped test-path records. Runtime origin remains unspecified without declarations.
`ops/smoke_partition_census.py`, using the same container invocation, builds a small
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.
+4
View File
@@ -95,6 +95,10 @@ def main() -> None:
enriched = result.change_context
assert enriched is not None and enriched.relationships, "changed context did not reach any question"
assert result.scope is not None and result.scope.selected
invalid_census = {"cpg_empty", "files_unparsed", "partition_file_census_unavailable", "partition_file_census_incomplete"}
assert not invalid_census.intersection(result.scope.unresolved_boundaries), result.scope
assert not any(d.get("reason") in invalid_census for d in result.degradations), result.degradations
assert result.question_outcomes and all(outcome.status == "completed" for outcome in result.question_outcomes), result
selected = {question.identity for question in result.scope.selected}
assert any(relation.target in selected for relation in enriched.relationships)
assert result.findings, "controlled unchanged sink has no witness"
+54
View File
@@ -0,0 +1,54 @@
"""Compile all production batch censuses on a readable real Python graph."""
from __future__ import annotations
import hashlib
import json
import tempfile
import time
from pathlib import Path
from openultrasast.cpg.backend import JoernBackend
from openultrasast.model.contracts import ExecutionBudget
def main() -> None:
budget = ExecutionBudget(time.monotonic() + 600, 2.0)
results = {}
with tempfile.TemporaryDirectory(prefix="ousast-census-smoke-") as temporary:
root = Path(temporary)
source = root / "probe.py"
source.write_text("def handler(value):\n return value\n")
data = source.read_bytes()
assert data, "source input unreadable"
backend = JoernBackend(heap_mb=1024, execution_budget=budget)
graph = backend.build(root, language="python")
assert graph is not None and graph.run_batch is not None, "real Python graph unavailable"
try:
for kind in ("taint", "dominance", "config"):
answer = graph.run_batch(kind, {"probe": {"file": "probe.py", "function": "handler"}})
assert isinstance(answer, dict) and "probe" in answer, f"{kind} batch unanswered"
census = answer.get("__census__")
assert isinstance(census, list) and census, f"{kind} census missing"
names = census[0].get("file_names")
assert isinstance(names, list) and any(str(p).endswith("probe.py") for p in names), census
assert int(census[0]["methods"]) > 0, census
results[kind] = census
finally:
graph.cleanup()
print(
json.dumps(
{
"purpose": "Real Python batch schema and query compilation; no security property admission claim",
"source_bytes": len(data),
"source_sha256": hashlib.sha256(data).hexdigest(),
"shared_budget_seconds": 600,
"censuses": results,
},
indent=2,
)
)
if __name__ == "__main__":
main()
+11
View File
@@ -39,6 +39,7 @@ def main() -> None:
sources = {
"src/api.php": "<?php\nfunction php_handler($req) { eval($req); }\n",
"server/api.js": "function js_handler(req) { eval(req.query.code); }\n",
"browser/client.js": "function browser_marker() { return 42; }\n",
"tests/probe.php": "<?php\nfunction php_test_marker() { return 42; }\n",
"tests/probe.js": "function js_test_marker() { return 42; }\n",
"vendor/library.php": "<?php\nfunction vendor_php_marker($req) { eval($req); }\n",
@@ -91,6 +92,8 @@ def main() -> None:
assert any(str(filename).endswith(path) for filename in files), witness
assert method in methods, witness
assert "php_handler" not in methods, witness
assert any(str(path).endswith("browser/client.js") for path in files), witness
assert "browser_marker" in methods, witness
else:
raise AssertionError(f"unexpected frontend: {language}")
records.append({"frontend": language, "input_root": str(root), "graph_witness": witness})
@@ -116,12 +119,20 @@ def main() -> None:
)
assert {record["frontend"] for record in records} == {"php", "javascript"}, records
assert result.scope is not None
invalid_census = {"cpg_empty", "files_unparsed", "partition_file_census_unavailable", "partition_file_census_incomplete"}
assert not invalid_census.intersection(result.scope.unresolved_boundaries), result.scope
assert not any(d.get("reason") in invalid_census for d in result.degradations), result.degradations
assert [q.identity.function for q in result.scope.selected] == ["php_handler"], result.scope
assert {q.identity.function for q in result.scope.deferred} == {"js_handler", "php_test_marker", "js_test_marker"}, result.scope
assert len(result.question_outcomes) == 1, result.question_outcomes
assert result.question_outcomes[0].identity == result.scope.selected[0].identity
assert all("vendor" not in q.identity.path and "node_modules" not in q.identity.path for q in result.scope.selected), result.scope
assert any(p.language == "go" and p.status == "unsupported" for p in result.partitions), result.partitions
javascript = next(p for p in result.partitions if p.language == "javascript")
assert set(javascript.unshipped_paths) == {"tests/probe.js", *suffix_tests}, javascript
assert javascript.runtime == "unspecified", "runtime role needs declared evidence"
php = next(p for p in result.partitions if p.language == "php")
assert php.unshipped_paths == ("tests/probe.php",), php
assert all(not Path(record["input_root"]).exists() for record in records), "partition scratch leaked"
report = {
"purpose": "Real mixed PHP/JavaScript graph census and one global ranker budget; no admission or latency claim",