fix(contributor-scan): retain declared Gruntfile and report census gaps honestly

This commit is contained in:
Marius Ciepluch
2026-09-14 08:07:06 +02:00
parent 88216c5072
commit 7c1c2d4475
18 changed files with 1048 additions and 25 deletions
+6
View File
@@ -29,3 +29,9 @@ def test_builder_rejects_unrecognized_dependency_before_download(tmp_path):
builder.build(tmp_path)
assert jar.read_bytes() == b"different dependency"
assert not (tmp_path / "ousast-frontend-retention-v1").exists()
def test_build_config_retention_is_explicit_and_overrides_ambient_policy(monkeypatch):
monkeypatch.setenv("OUSAST_INCLUDE_BUILD_CONFIGS", "0")
assert JoernBackend()._jvm_env()["OUSAST_INCLUDE_BUILD_CONFIGS"] == "1"
assert JoernBackend(include_build_configs=False)._jvm_env()["OUSAST_INCLUDE_BUILD_CONFIGS"] == "0"
+14 -5
View File
@@ -1,5 +1,7 @@
from pathlib import Path
import pytest
from openultrasast.cpg.backend import CpgResult
from openultrasast.model.regions import ScanRegion
from openultrasast.model.scan import ScanBudget, scan_repository
@@ -153,7 +155,8 @@ def test_unsupported_only_population_is_named(tmp_path):
assert "frontend_unsupported" in result.scope.unresolved_boundaries
def test_partial_partition_census_never_completes_questions(tmp_path):
@pytest.mark.parametrize("census", [[{"methods": 2, "files": 1}], [], ["invalid"], None])
def test_partial_partition_census_never_completes_questions(tmp_path, census):
for name in ("a.py", "b.py"):
(tmp_path / name).write_text("print('readable')")
@@ -162,13 +165,14 @@ def test_partial_partition_census_never_completes_questions(tmp_path):
return CpgResult(
Path("graph"),
lambda q, p: [],
run_batch=lambda q, reqs: {**{rid: [] for rid in reqs}, "__census__": [{"methods": 2, "files": 1}]},
run_batch=lambda q, reqs: {**{rid: [] for rid in reqs}, **({"__census__": census} if census is not None else {})},
)
region = ScanRegion(path="a.py", function="f", language="python", families=("injection",), rank=1.0, source="entry_point")
result = scan_repository(tmp_path, [region], backend=Backend(), budget=ScanBudget(tiering=False))
assert result.question_outcomes[0].status == "unresolved"
assert "cpg_empty" in result.scope.unresolved_boundaries
assert "partition_file_census_unavailable" in result.scope.unresolved_boundaries
assert "cpg_empty" not in result.scope.unresolved_boundaries
def test_projection_refuses_substituted_ancestor(tmp_path):
@@ -286,7 +290,9 @@ def test_synthetic_census_node_cannot_mask_missing_source(tmp_path):
graph = build_partitions(tmp_path, build, None)
try:
result = graph.run_batch("taint", {"q": {"file": "a.php"}})
assert result["__census__"][0]["files"] == 0
assert result["__census__"][0]["files"] == 2
assert result["__census__"][0]["methods"] == 3
assert result["__census__"][0]["missing_file_names"] == ["b.php"]
assert "partition_file_census_incomplete" in graph.execution_diagnostics()
finally:
graph.cleanup()
@@ -331,7 +337,10 @@ def test_unnamed_sharded_census_remains_explicitly_incomplete(tmp_path):
)
try:
result = graph.run_batch("taint", {"q": {"file": "app.php"}})
assert result["__census__"] == [{"methods": 0, "files": 0, "shards": 2}]
assert result["__census__"][0]["methods"] == 20
assert result["__census__"][0]["files"] == 20
assert result["__census__"][0]["shards"] == 2
assert result["__census__"][0]["census_failure"] == "partition_file_census_unavailable"
assert "partition_file_census_unavailable" in graph.execution_diagnostics()
finally:
graph.cleanup()
+18 -2
View File
@@ -463,7 +463,15 @@ def test_tier_zero_pairs_are_not_asked_and_nothing_else_is_skipped(tmp_path: Pat
issued.append({"kind": kind, "count": len(requests)})
return {rid: [] for rid in requests}
return CpgResult(cpg_path=Path("cpg.bin"), run=lambda q, p: [], run_batch=batch)
names = [str(p) for p in root.rglob("*.py")]
return CpgResult(
cpg_path=Path("cpg.bin"),
run=lambda q, p: [],
run_batch=lambda kind, requests: {
"__census__": [{"methods": len(names), "files": len(names), "file_names": names}],
**batch(kind, requests),
},
)
region = ScanRegion(
path="a.py", function="run", language="python", families=("injection", "path", "deserialization"), rank=0.9, source="entry_point"
@@ -521,7 +529,15 @@ def test_the_budget_is_spent_by_evidence_when_asked(tmp_path: Path) -> None:
self.taint_files.append(sorted({str(p.get("file", "")) for p in requests.values()}))
return {rid: [] for rid in requests}
return CpgResult(cpg_path=Path("cpg.bin"), run=lambda q, p: [], run_batch=batch)
names = [str(p) for p in root.rglob("*.py")]
return CpgResult(
cpg_path=Path("cpg.bin"),
run=lambda q, p: [],
run_batch=lambda kind, requests: {
"__census__": [{"methods": len(names), "files": len(names), "file_names": names}],
**batch(kind, requests),
},
)
top = ScanRegion(path="top.py", function="handle", language="python", families=("injection",), rank=1.0, source="entry_point")
low = ScanRegion(path="low.py", function="run", language="python", families=("injection",), rank=0.3, source="entry_point")
+6 -1
View File
@@ -26,7 +26,12 @@ class Backend:
assert any(p.read_bytes() for p in root.rglob("*") if p.is_file())
if self.fail:
return None
return SimpleNamespace(cpg_path=Path("test.cpg"), run_batch=self.batch, cleanup=lambda: None)
names = [str(p) for p in root.rglob("*") if p.is_file()]
def batch(kind, requests):
return {"__census__": [{"methods": len(names), "files": len(names), "file_names": names}], **self.batch(kind, requests)}
return SimpleNamespace(cpg_path=Path("test.cpg"), run_batch=batch, cleanup=lambda: None)
def batch(self, kind, requests):
if any(p.get("evidenceOnly") == "true" for p in requests.values()):
+8 -1
View File
@@ -117,7 +117,14 @@ def test_targeted_base_uses_real_driver_same_deadline(tmp_path):
assert (root / "a.js").read_bytes()
seen.append(execution_budget)
rows = json.loads(scan(line=5, sanitized=True).question_outcomes[0].raw_rows_json)
return CpgResult(Path("graph"), lambda kind, params: rows)
return CpgResult(
Path("graph"),
lambda kind, params: rows,
run_batch=lambda kind, requests: {
**{rid: rows for rid in requests},
"__census__": [{"methods": 1, "files": 1, "file_names": [str(root / "a.js")]}],
},
)
region = ScanRegion("a.js", "handler", "javascript", ("injection",), 1.0, "entry_point")
budget = ExecutionBudget(time.monotonic() + 30, 1.0)