mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-18 04:29:27 +00:00
JVM startup dominated a scan: a call per region per family put a ten-line file at 275s over three regions, and would have put a thousand regions at roughly fifty hours. The queries themselves are milliseconds once the CPG is loaded, so the cost was almost entirely process starts. scan_repository() is now collect -> batch -> arbitrate. The three .sc scripts accept a `requests` JSON map and answer all of them in one run, keeping the single-request form so the committed measurements stay reproducible byte-for-byte. Arbiters are untouched: each is handed a CpgResult whose `run` returns its own prefetched rows. The batcher builds parameters by calling request_params() extracted from taint.py, dominance.py and config_value.py rather than reconstructing them. A second copy that drifts from the arbiter's is the exact shape of the five family-assumption bugs the predecessor spec spent a session finding; there is now one definition per family and the batcher borrows it. Regions are also deduplicated by (path, function), collapsing the nameless entry-point rows that were producing `model:injection:?:10`. Measured on the same ten-line file: 275s -> 106s, three duplicate findings -> one `model:injection:ping:10`, rung unchanged at model_entailed. Gates byte-identical. Server mode is recorded as task 2.3, deliberately sequenced after the repository measurement: whether the remaining ~100s floor is worth a process model, a weakened subprocess seam and a Scala-evaluating HTTP endpoint depends on a build-time-to-query-time ratio only 2.2 produces. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
91 lines
4.3 KiB
Python
91 lines
4.3 KiB
Python
"""Guard dominance: the arbiter for the bugs that never crash (model-grounded-detection, Req 6.2).
|
|
|
|
An access-control bug is the *absence* of a guard. There is no flow to follow and no crash to reproduce, so
|
|
neither taint reachability nor an execution oracle reaches this class at all — which is precisely the gap
|
|
Clearwing's crash ladder leaves open and this project exists to close.
|
|
|
|
What the model *can* establish is a consistency violation: an obligated operation that no discharging guard
|
|
governs, in a file where its siblings are governed by one. That asymmetry is the evidence, and it is
|
|
deterministic — no model call, and the same verdict on every run over one CPG.
|
|
|
|
The rungs are deliberately asymmetric:
|
|
|
|
ENTAILED unguarded here, guarded on a sibling — the file contradicts itself, and the model can say so
|
|
CORROBORATED unguarded here, and no sibling is guarded either — the obligation is real but there is
|
|
nothing to be inconsistent *with*. A whole module may be unauthenticated by design, and
|
|
calling that entailed would assert a policy the model cannot see.
|
|
(none) guarded, or no obligated operation at all
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping, Sequence
|
|
|
|
from ..cpg.backend import CpgResult
|
|
from .ladder import Rung, Verdict
|
|
from .specs import DominanceSpec
|
|
|
|
|
|
def request_params(spec: DominanceSpec, *, function: str = "") -> dict[str, object]:
|
|
"""The query parameters this arbiter sends. Shared with the batcher; see the note in `taint.request_params`."""
|
|
return {"operations": spec.operations, "dischargers": spec.dischargers, "function": function}
|
|
|
|
|
|
def verdict(cpg: CpgResult, spec: DominanceSpec, *, function: str = "") -> Verdict | None:
|
|
"""The verdict for the obligated operation in ``function``, or ``None`` when there is nothing to say."""
|
|
rows = cpg.run("dominance", request_params(spec, function=function))
|
|
operations = _operations(rows)
|
|
if not operations:
|
|
return None
|
|
|
|
here = [row for row in operations if not function or row["opMethod"] == function]
|
|
if not here:
|
|
return None
|
|
unguarded = [row for row in here if not row["guards"]]
|
|
if not unguarded:
|
|
return None # every operation in this function is governed; nothing to report
|
|
|
|
# Siblings are the obligated operations *elsewhere* in the graph. Sorted so the witness is stable.
|
|
siblings = sorted(
|
|
(row for row in operations if row["opMethod"] not in {r["opMethod"] for r in here}),
|
|
key=lambda row: (row["opMethod"], row["opLine"]),
|
|
)
|
|
# Count guarded sibling *methods*, not rows: one method can hold several obligated operations, and
|
|
# naming it twice makes the witness read as more corroboration than the file actually contains.
|
|
guarded_methods = sorted({str(row["opMethod"]) for row in siblings if row["guards"]})
|
|
target = min(unguarded, key=lambda row: (row["opMethod"], row["opLine"]))
|
|
|
|
if guarded_methods:
|
|
witness = (
|
|
f"{target['opMethod']} line {target['opLine']}: no discharging guard, "
|
|
f"while {len(guarded_methods)} sibling handler(s) are guarded "
|
|
f"({', '.join(guarded_methods[:3])})"
|
|
)
|
|
return Verdict(rung=Rung.ENTAILED, family=spec.family, witness=witness)
|
|
|
|
witness = f"{target['opMethod']} line {target['opLine']}: obligated operation with no discharging guard, and no guarded sibling"
|
|
return Verdict(rung=Rung.CORROBORATED, family=spec.family, witness=witness)
|
|
|
|
|
|
def _operations(rows: object) -> list[dict[str, object]]:
|
|
"""The query's rows. A non-list (an engine failure) yields nothing, never a false absence."""
|
|
if not isinstance(rows, Sequence) or isinstance(rows, (str, bytes)):
|
|
return []
|
|
kept: list[dict[str, object]] = []
|
|
for row in rows:
|
|
if not isinstance(row, Mapping):
|
|
continue
|
|
guards = row.get("dominatingGuards")
|
|
kept.append(
|
|
{
|
|
"operation": str(row.get("operation", "")),
|
|
"opLine": str(row.get("opLine", "")),
|
|
"opMethod": str(row.get("opMethod", "")),
|
|
"guards": [str(g) for g in guards] if isinstance(guards, Sequence) and not isinstance(guards, str) else [],
|
|
}
|
|
)
|
|
return kept
|
|
|
|
|
|
__all__ = ["request_params", "verdict"]
|