fix(ci): run ruff format, and record run 5 at 90% pair-correct

CI was red on `ruff format --check`: 23 files. I ran `ruff check` after every
change this session and never once ran `ruff format --check`, though CI runs
both. Lint passing is not formatting passing, and I treated one as evidence for
the other for the whole session.

Formatting applied. The full CI sequence now reproduces locally: format, lint,
mypy over 94 files, 735 tests, three gates byte-identical, compileall, and the
semantic job's 16 marked tests.

Also records run 5 of the pipeline measurement, which the vampi fix lifted as
predicted:

                       hunter (K=5)   pipeline (K=1)
  pair-correct              76.7%          90.0%
  leak                      12.3%           6.7%
  detection                    --          96.7%
  cost                     ~$0.69        $0.0068

27 of 30 pairs. Five runs, each step from a measurement contradicting an
assumption: 73.3 (no pipeline path) -> 73.3 (routing fixed, IDENTICAL, which
exposed the discarded verdict) -> 80.0 (residual question matched to its
arbiter) -> 86.7 (aiohttp settings) -> 90.0 (dominance question no longer
contradicting its own evidence).

One miss left: dsvpwa-dsvpwa-main, whose setting is an argparse --ssl default
rather than a security-setting call. Out of reach before the settings work and
still is; contorting the arbiter around one pair would be worse than the miss.

The caveat is recorded beside the number: 30 pairs of one slice, and the slice
iterated against all session. The +16% overfitting gap against vfc-js is why
contributor-scan Req 8.1/8.2 exist.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-08 19:46:58 +02:00
co-authored by Claude Opus 5
parent 14ca0a96ce
commit fe45081b43
24 changed files with 397 additions and 213 deletions
@@ -1,47 +1,31 @@
{ {
"correction": "The three silent config pairs were reported as corpus defects on a similarity measure (23%, 45%, 53%). That threshold came from the OWASP minimal-fix analysis and does not apply to this slice: vibe-py pairs a vulnerable function with a DIFFERENT, SAFE function from the same repository, so low similarity is the design. djangoat-views-user-view is 36% similar and dsvpwa-handlers-do-bdr 34%, both pair_correct. Two of the three were a settings-table gap and now split.", "caveat": "30 pairs of ONE slice, and the slice iterated against all session. The +16% overfitting gap against vfc-js is why contributor-scan Req 8.1 and 8.2 exist. A contributor-facing tool has to hold up on a repository nobody tuned against, which is what that spec's Req 4 and 10 are for.",
"cost_note": "Four full runs of this measurement cost roughly $0.04 in total.", "cost_usd": 0.0068,
"cost_usd": 0.0067, "detection_rate": 0.9667,
"detection_rate": 0.9333,
"division_of_labour": {
"model_corroborated": 2,
"model_entailed": 11,
"note": "The model arbitrates 13 of 28 findings, up from 9 of 24 in run 1. Both findings gained in run 4 came from the MODEL, not the LLM, and cost fell from $0.0126 to $0.0067 as work moved onto the free deterministic path. That is the payoff curve for fact-table coverage, measured: closing a gap moves findings out of the LLM's column into the model's, improving precision and cost at once.",
"suspicion": 15
},
"generated": "2026-09-08", "generated": "2026-09-08",
"hunter_baseline": { "hunter_baseline": {
"leak_rate": 0.123, "leak_rate": 0.123,
"note": "deepseek-v4-flash K=5, 2026-09-06-learning-families.json", "note": "deepseek-v4-flash K=5, 2026-09-06-learning-families.json",
"pair_correct": 0.767 "pair_correct": 0.767
}, },
"known_limit": "permissive_values is a flat list of literals with no keyword association, so it holds both True and False. debug=True is dangerous while httponly=True is safe and the model cannot tell them apart. It did not bite here -- run 4's leak is identical to run 3's -- but it is a real limit of the fact schema.",
"leak_rate": 0.0667, "leak_rate": 0.0667,
"model": "deepseek-v4-flash", "model": "deepseek-v4-flash",
"outcomes": { "outcomes": {
"both_flagged": 2, "both_flagged": 2,
"both_silent": 2, "both_silent": 1,
"pair_correct": 26 "pair_correct": 27
}, },
"pair_correct_rate": 0.8667, "pair_correct_rate": 0.9,
"pairs": 30, "pairs": 30,
"reading": "The wired pipeline beats the LLM hunter on both axes: 86.7% pair-correct against 76.7%, and 6.7% leak against 12.3% -- at one fifth of the model calls (K=1 against K=5) and ~100x cheaper ($0.0067 against ~$0.69). The earlier 'detection has regressed' reading was taken with the proposer disconnected, an arbiter judged against a detector, and before four wiring bugs were found.", "reading": "The wired pipeline beats the LLM hunter on both axes: 90.0% pair-correct against 76.7%, and 6.7% leak against 12.3% -- at one fifth of the model calls (K=1 against K=5) and ~100x cheaper ($0.0068 against ~$0.69). 27 of 30 pairs correct, one miss remaining. The earlier 'detection has regressed' reading was taken with the proposer disconnected -- an arbiter judged against a detector -- and before five wiring bugs were found.",
"remaining_misses": { "remaining_miss": "dsvpwa-dsvpwa-main: its setting is an argparse default (--ssl defaulting to 0), a CLI default rather than a security-setting call. Predicted out of reach before the settings work and still is; contorting the arbiter around one pair would be worse than the miss.",
"both_flagged": {
"cause": "the leak: LLM proposals the model cannot contradict",
"family": "injection",
"pairs": 2
},
"dsvpwa-dsvpwa-main": "its setting is an argparse default (--ssl defaulting to 0), a CLI default rather than a security-setting call. Predicted before the settings change and unchanged by it.",
"vampi-users-update-password": "the dominance arbiter returns corroborated for it in isolation; still undiagnosed at pipeline level."
},
"rows": [ "rows": [
{ {
"family": "injection", "family": "injection",
"fixed_findings": 2, "fixed_findings": 2,
"outcome": "both_flagged", "outcome": "both_flagged",
"pair": "damn-vulnerable-graphql-application-views-mutate", "pair": "damn-vulnerable-graphql-application-views-mutate",
"vuln_findings": 4, "vuln_findings": 3,
"vuln_rungs": [ "vuln_rungs": [
"suspicion" "suspicion"
] ]
@@ -51,7 +35,7 @@
"fixed_findings": 0, "fixed_findings": 0,
"outcome": "pair_correct", "outcome": "pair_correct",
"pair": "damn-vulnerable-graphql-application-views-resolve-pastes", "pair": "damn-vulnerable-graphql-application-views-resolve-pastes",
"vuln_findings": 3, "vuln_findings": 4,
"vuln_rungs": [ "vuln_rungs": [
"suspicion" "suspicion"
] ]
@@ -119,7 +103,7 @@
"fixed_findings": 0, "fixed_findings": 0,
"outcome": "pair_correct", "outcome": "pair_correct",
"pair": "dsvpwa-handlers-do-bdr", "pair": "dsvpwa-handlers-do-bdr",
"vuln_findings": 4, "vuln_findings": 3,
"vuln_rungs": [ "vuln_rungs": [
"suspicion" "suspicion"
] ]
@@ -227,10 +211,12 @@
{ {
"family": "access_control", "family": "access_control",
"fixed_findings": 0, "fixed_findings": 0,
"outcome": "both_silent", "outcome": "pair_correct",
"pair": "vampi-users-update-password", "pair": "vampi-users-update-password",
"vuln_findings": 0, "vuln_findings": 1,
"vuln_rungs": [] "vuln_rungs": [
"model_corroborated"
]
}, },
{ {
"family": "injection", "family": "injection",
@@ -335,30 +321,31 @@
], ],
"run_history": [ "run_history": [
{ {
"leak": 0.067, "note": "absence and config families had no pipeline path to their arbiters",
"note": "access_control and config_secrets had no pipeline path to their arbiters",
"pair_correct": 0.733, "pair_correct": 0.733,
"run": 1 "run": 1
}, },
{ {
"leak": 0.067, "note": "routing fixed -- IDENTICAL result, which was the clue: the arbiter was reached and its verdict discarded",
"note": "family routing fixed -- IDENTICAL result, which was the clue: the arbiter was reached and its verdict discarded",
"pair_correct": 0.733, "pair_correct": 0.733,
"run": 2 "run": 2
}, },
{ {
"leak": 0.067, "note": "residual question phrased for the arbiter that raised it",
"note": "residual question phrased for the arbiter that raised it; 2 of 3 access_control pairs converted",
"pair_correct": 0.8, "pair_correct": 0.8,
"run": 3 "run": 3
}, },
{ {
"leak": 0.067, "note": "aiohttp Application(debug=True) and RedisStorage(httponly=False) added to the settings table",
"note": "aiohttp Application/RedisStorage added to the settings table; 2 more config pairs converted, leak unchanged",
"pair_correct": 0.867, "pair_correct": 0.867,
"run": 4 "run": 4
},
{
"note": "the dominance question no longer contradicts its own evidence line",
"pair_correct": 0.9,
"run": 5
} }
], ],
"seconds": 1029.0, "seconds": 1213.5,
"slice": "vibe-py" "slice": "vibe-py"
} }
+1 -3
View File
@@ -234,9 +234,7 @@ def run_round(
regressed, regressed,
small, small,
) )
outcome = _outcome( outcome = _outcome(round_index, accepted=accepted, reason=reason, edits=edits, before=before, after=after, degradations=refusals)
round_index, accepted=accepted, reason=reason, edits=edits, before=before, after=after, degradations=refusals
)
return RoundOutcome( return RoundOutcome(
**{ **{
**outcome.__dict__, **outcome.__dict__,
+14 -9
View File
@@ -24,6 +24,7 @@ class FamilyGaps(TypedDict):
pairs: int pairs: int
reasons: dict[str, int] reasons: dict[str, int]
# Ascending, matching `ladder.Rung`. A pair is split when the vulnerable side outranks its fix. # Ascending, matching `ladder.Rung`. A pair is split when the vulnerable side outranks its fix.
_ORDER = {"none": 0, "": 0, "suspicion": 0, "model_corroborated": 1, "model_entailed": 2, "execution_confirmed": 3} _ORDER = {"none": 0, "": 0, "suspicion": 0, "model_corroborated": 1, "model_entailed": 2, "execution_confirmed": 3}
@@ -82,15 +83,18 @@ class CalibrationReport:
def to_markdown(self) -> str: def to_markdown(self) -> str:
"""Per-slice rows first; there is no headline here that is not backed by them (Req 9.3, 11.1).""" """Per-slice rows first; there is no headline here that is not backed by them (Req 9.3, 11.1)."""
lines = ["## Model calibration", "", "Coverage is a pair the model *splits*: a strictly higher rung on", lines = [
"the vulnerable side than on its fix. Everything else is a named gap.", "", "## Model calibration",
"| slice | pairs | covered | gaps | coverage |", "|---|---|---|---|---|"] "",
"Coverage is a pair the model *splits*: a strictly higher rung on",
"the vulnerable side than on its fix. Everything else is a named gap.",
"",
"| slice | pairs | covered | gaps | coverage |",
"|---|---|---|---|---|",
]
for slice_name, block in sorted(self.per_slice.items()): for slice_name, block in sorted(self.per_slice.items()):
pairs = block["pairs"] or 1 pairs = block["pairs"] or 1
lines.append( lines.append(f"| {slice_name} | {block['pairs']} | {block['covered']} | {block['gaps']} | {block['covered'] / pairs:.0%} |")
f"| {slice_name} | {block['pairs']} | {block['covered']} | {block['gaps']} | "
f"{block['covered'] / pairs:.0%} |"
)
by_family = self.gaps_by_family() by_family = self.gaps_by_family()
if by_family: if by_family:
lines += ["", "### Gaps by family", ""] lines += ["", "### Gaps by family", ""]
@@ -116,8 +120,9 @@ def calibrate(outcomes: Iterable[PairOutcome]) -> CalibrationReport:
if split: if split:
covered.append(outcome.pair) covered.append(outcome.pair)
else: else:
gaps.append(ModelGap(pair=outcome.pair, family=outcome.family, slice=outcome.slice, gaps.append(
reason=outcome.reason or _default_reason(outcome))) ModelGap(pair=outcome.pair, family=outcome.family, slice=outcome.slice, reason=outcome.reason or _default_reason(outcome))
)
return CalibrationReport( return CalibrationReport(
covered=tuple(sorted(covered)), covered=tuple(sorted(covered)),
gaps=tuple(sorted(gaps, key=lambda g: (g.family, g.pair))), gaps=tuple(sorted(gaps, key=lambda g: (g.family, g.pair))),
+14 -6
View File
@@ -79,11 +79,14 @@ def judge(
said_vulnerable = _ask(client, model, spec, answer.witness, function) said_vulnerable = _ask(client, model, spec, answer.witness, function)
if said_vulnerable is None: if said_vulnerable is None:
return Verdict(rung=Rung.SUSPICION, family=spec.family, witness=answer.witness, return Verdict(rung=Rung.SUSPICION, family=spec.family, witness=answer.witness, contradiction="the judge returned no usable answer")
contradiction="the judge returned no usable answer")
if not said_vulnerable: if not said_vulnerable:
return Verdict(rung=Rung.SUSPICION, family=spec.family, witness=answer.witness, return Verdict(
contradiction="the judge held the sanitizer on this path sufficient") rung=Rung.SUSPICION,
family=spec.family,
witness=answer.witness,
contradiction="the judge held the sanitizer on this path sufficient",
)
return answer # CORROBORATED: a real flow the judge agrees is not neutralised return answer # CORROBORATED: a real flow the judge agrees is not neutralised
@@ -96,8 +99,13 @@ def _ask(client: Any, model: str, spec: TaintSpec, witness: str, function: str)
line = tail.split(",")[0].strip(") ") line = tail.split(",")[0].strip(") ")
prompt = redact_secrets( prompt = redact_secrets(
QUESTION.format( QUESTION.format(
language=spec.language, source=source.strip(), sink=sink.strip(), line=line or "?", language=spec.language,
function=function or "?", sanitized="yes", family=spec.family, source=source.strip(),
sink=sink.strip(),
line=line or "?",
function=function or "?",
sanitized="yes",
family=spec.family,
) )
) )
try: try:
+3 -7
View File
@@ -64,9 +64,7 @@ class ModelFinding:
contradiction: str = "" contradiction: str = ""
def _arbitrate( def _arbitrate(cpg: CpgResult, spec: TaintSpec | DominanceSpec | ConfigSpec, *, function: str, parameter_sources: bool) -> Verdict | None:
cpg: CpgResult, spec: TaintSpec | DominanceSpec | ConfigSpec, *, function: str, parameter_sources: bool
) -> Verdict | None:
"""Route a family to the arbiter that can actually decide it. """Route a family to the arbiter that can actually decide it.
Dispatching on the spec type rather than on a family name keeps this honest: a family without an arbiter Dispatching on the spec type rather than on a family name keeps this honest: a family without an arbiter
@@ -96,8 +94,7 @@ def scan_region(
if answer is not None and answer.rung is Rung.ENTAILED: if answer is not None and answer.rung is Rung.ENTAILED:
# The graph decided. No model call, and no candidate needs asking about: this region has its finding. # The graph decided. No model call, and no candidate needs asking about: this region has its finding.
return [ModelFinding(site=_site(function, answer.witness), family=spec.family, rung=Rung.ENTAILED, return [ModelFinding(site=_site(function, answer.witness), family=spec.family, rung=Rung.ENTAILED, witness=answer.witness)]
witness=answer.witness)]
if answer is not None: if answer is not None:
# A real flow through a sanitizer. Whether it suffices is the residual, and the model HAS coverage # A real flow through a sanitizer. Whether it suffices is the residual, and the model HAS coverage
@@ -106,8 +103,7 @@ def scan_region(
return [] return []
said = _ask(client, model, residual_question(spec, answer.witness, function)) said = _ask(client, model, residual_question(spec, answer.witness, function))
if said: if said:
return [ModelFinding(site=_site(function, answer.witness), family=spec.family, return [ModelFinding(site=_site(function, answer.witness), family=spec.family, rung=Rung.CORROBORATED, witness=answer.witness)]
rung=Rung.CORROBORATED, witness=answer.witness)]
return [] return []
# The model resolved nothing. It is SILENT, not negative — so the enumerator's candidates are put to the # The model resolved nothing. It is SILENT, not negative — so the enumerator's candidates are put to the
+5 -4
View File
@@ -56,15 +56,16 @@ def render(rows: Sequence[SliceRow], *, development: str = "", deciding: str = "
total_pairs = sum(row.pairs for row in rows) total_pairs = sum(row.pairs for row in rows)
total_covered = sum(row.covered for row in rows) total_covered = sum(row.covered for row in rows)
lines += ["", f"Across every slice: {total_covered}/{total_pairs} = {total_covered / total_pairs:.0%} " lines += [
"— read the rows above it, not this line."] "",
f"Across every slice: {total_covered}/{total_pairs} = {total_covered / total_pairs:.0%} — read the rows above it, not this line.",
]
gap = overfitting_gap(rows, development=development, deciding=deciding) gap = overfitting_gap(rows, development=development, deciding=deciding)
if gap is not None: if gap is not None:
lines += [ lines += [
"", "",
f"**Overfitting gap: {gap:+.0%}** — `{development}` (development) minus `{deciding}`, " f"**Overfitting gap: {gap:+.0%}** — `{development}` (development) minus `{deciding}`, which is the deciding real-world slice.",
f"which is the deciding real-world slice.",
] ]
elif development or deciding: elif development or deciding:
lines += ["", f"**Overfitting gap: not measurable** — `{development}` or `{deciding}` is absent from this run."] lines += ["", f"**Overfitting gap: not measurable** — `{development}` or `{deciding}` is absent from this run."]
+1
View File
@@ -190,6 +190,7 @@ def taint_specs(
scoped = (facts if facts is not None else load_facts()).for_language(language) scoped = (facts if facts is not None else load_facts()).for_language(language)
families = taxonomy if taxonomy is not None else load_families() families = taxonomy if taxonomy is not None else load_families()
sources = tuple(sorted({pattern for fact in scoped.sources for pattern in fact.patterns})) sources = tuple(sorted({pattern for fact in scoped.sources for pattern in fact.patterns}))
# A shape qualifier means "this sink is safe in this form", never "this call cleans the value". # A shape qualifier means "this sink is safe in this form", never "this call cleans the value".
def _is_shape(fact: object) -> bool: def _is_shape(fact: object) -> bool:
return bool(getattr(fact, "parameterized", False)) or getattr(fact, "literal_format_arg", None) is not None return bool(getattr(fact, "parameterized", False)) or getattr(fact, "literal_format_arg", None) is not None
+4 -2
View File
@@ -67,7 +67,8 @@ def _flows(rows: object, *, function: str) -> list[dict[str, object]]:
continue continue
if function and not _in_scope(row, function): if function and not _in_scope(row, function):
continue continue
kept.append({ kept.append(
{
"sink": str(row.get("sink", "")), "sink": str(row.get("sink", "")),
"sinkArity": row.get("sinkArity"), "sinkArity": row.get("sinkArity"),
"sinkArg0Literal": row.get("sinkArg0Literal"), "sinkArg0Literal": row.get("sinkArg0Literal"),
@@ -77,7 +78,8 @@ def _flows(rows: object, *, function: str) -> list[dict[str, object]]:
"source": str(row.get("source", "")), "source": str(row.get("source", "")),
"sanitized": bool(row.get("sanitized", False)), "sanitized": bool(row.get("sanitized", False)),
"length": _as_int(row.get("length")), "length": _as_int(row.get("length")),
}) }
)
return kept return kept
-1
View File
@@ -100,7 +100,6 @@ _DEFAULT_PROVENANCE = {"sast": "synthetic", "local": "human", "github": "human",
_MANIFEST_MECHANISM = "source_reaches_sink" # cheat-sheet fixtures referenced via expected_from _MANIFEST_MECHANISM = "source_reaches_sink" # cheat-sheet fixtures referenced via expected_from
class CatalogError(ValueError): class CatalogError(ValueError):
"""Raised when a pair catalog violates the label schema.""" """Raised when a pair catalog violates the label schema."""
+15 -2
View File
@@ -44,8 +44,21 @@ def test_the_seam_imports_nothing_outside_the_standard_library() -> None:
imported.add(node.module.split(".")[0]) imported.add(node.module.split(".")[0])
elif isinstance(node, ast.Import): elif isinstance(node, ast.Import):
imported.update(alias.name.split(".")[0] for alias in node.names) imported.update(alias.name.split(".")[0] for alias in node.names)
stdlib = {"__future__", "json", "os", "re", "shutil", "subprocess", "tempfile", "dataclasses", "pathlib", "typing", stdlib = {
"collections", "logging", "hashlib"} "__future__",
"json",
"os",
"re",
"shutil",
"subprocess",
"tempfile",
"dataclasses",
"pathlib",
"typing",
"collections",
"logging",
"hashlib",
}
assert imported <= stdlib, f"the seam imports outside the standard library: {sorted(imported - stdlib)}" assert imported <= stdlib, f"the seam imports outside the standard library: {sorted(imported - stdlib)}"
assert "import jpype" not in source and "py4j" not in source, "Joern is reached by subprocess, never in-process" assert "import jpype" not in source and "py4j" not in source, "Joern is reached by subprocess, never in-process"
-2
View File
@@ -100,5 +100,3 @@ def test_retrieval_package_is_bounded_by_character_budget() -> None:
assert package.role == "verifier" assert package.role == "verifier"
assert package.hits assert package.hits
assert package.truncated is True assert package.truncated is True
+8 -4
View File
@@ -48,12 +48,14 @@ def test_flagging_both_sides_equally_is_a_gap_not_a_success() -> None:
def test_gaps_are_grouped_by_family_with_their_reasons() -> None: def test_gaps_are_grouped_by_family_with_their_reasons() -> None:
from openultrasast.model.calibrate import calibrate from openultrasast.model.calibrate import calibrate
report = calibrate([ report = calibrate(
[
_outcome("a", family="path", vuln="none", reason="sink_not_in_table"), _outcome("a", family="path", vuln="none", reason="sink_not_in_table"),
_outcome("b", family="path", vuln="none", reason="sink_not_in_table"), _outcome("b", family="path", vuln="none", reason="sink_not_in_table"),
_outcome("c", family="injection", vuln="none", reason="source_unmodelled"), _outcome("c", family="injection", vuln="none", reason="source_unmodelled"),
_outcome("d", family="injection"), _outcome("d", family="injection"),
]) ]
)
by_family = report.gaps_by_family() by_family = report.gaps_by_family()
assert set(by_family) == {"path", "injection"} assert set(by_family) == {"path", "injection"}
assert by_family["path"]["pairs"] == 2 assert by_family["path"]["pairs"] == 2
@@ -65,10 +67,12 @@ def test_the_report_is_per_slice_and_names_the_deciding_one() -> None:
"""Req 9.3/11.1: the real-world slice decides; a development slice must not stand in for the corpus.""" """Req 9.3/11.1: the real-world slice decides; a development slice must not stand in for the corpus."""
from openultrasast.model.calibrate import calibrate from openultrasast.model.calibrate import calibrate
report = calibrate([ report = calibrate(
[
_outcome("a", slice_name="vibe-py"), _outcome("a", slice_name="vibe-py"),
_outcome("b", slice_name="vfc-js", vuln="none", reason="sink_not_in_table"), _outcome("b", slice_name="vfc-js", vuln="none", reason="sink_not_in_table"),
]) ]
)
assert set(report.per_slice) == {"vibe-py", "vfc-js"} assert set(report.per_slice) == {"vibe-py", "vfc-js"}
assert report.per_slice["vibe-py"]["covered"] == 1 assert report.per_slice["vibe-py"]["covered"] == 1
assert report.per_slice["vfc-js"]["gaps"] == 1 assert report.per_slice["vfc-js"]["gaps"] == 1
+31 -17
View File
@@ -14,9 +14,9 @@ from pathlib import Path
def _spec(): # type: ignore[no-untyped-def] def _spec(): # type: ignore[no-untyped-def]
from openultrasast.model.specs import ConfigSpec from openultrasast.model.specs import ConfigSpec
return ConfigSpec(family="config_secrets", language="python", return ConfigSpec(
settings=("CORS", "set_cookie", "app.run"), family="config_secrets", language="python", settings=("CORS", "set_cookie", "app.run"), permissive=("'*'", '"*"', "True", "0.0.0.0")
permissive=("'*'", '"*"', "True", "0.0.0.0")) )
def _cpg(rows): # type: ignore[no-untyped-def] def _cpg(rows): # type: ignore[no-untyped-def]
@@ -29,8 +29,7 @@ def test_a_permissive_literal_is_entailed() -> None:
from openultrasast.model.config_value import verdict from openultrasast.model.config_value import verdict
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
rows = [{"setting": 'CORS(app, origins="*")', "line": "3", "method": "create_app", rows = [{"setting": 'CORS(app, origins="*")', "line": "3", "method": "create_app", "literalArgs": ['"*"'], "args": ["app", '"*"']}]
"literalArgs": ['"*"'], "args": ["app", '"*"']}]
answer = verdict(_cpg(rows), _spec(), function="create_app") answer = verdict(_cpg(rows), _spec(), function="create_app")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
assert "*" in answer.witness and "CORS" in answer.witness assert "*" in answer.witness and "CORS" in answer.witness
@@ -39,8 +38,15 @@ def test_a_permissive_literal_is_entailed() -> None:
def test_a_restrictive_literal_yields_no_finding() -> None: def test_a_restrictive_literal_yields_no_finding() -> None:
from openultrasast.model.config_value import verdict from openultrasast.model.config_value import verdict
rows = [{"setting": 'CORS(app, origins="https://app.example")', "line": "3", "method": "create_app", rows = [
"literalArgs": ['"https://app.example"'], "args": ["app", '"https://app.example"']}] {
"setting": 'CORS(app, origins="https://app.example")',
"line": "3",
"method": "create_app",
"literalArgs": ['"https://app.example"'],
"args": ["app", '"https://app.example"'],
}
]
assert verdict(_cpg(rows), _spec(), function="create_app") is None assert verdict(_cpg(rows), _spec(), function="create_app") is None
@@ -49,8 +55,15 @@ def test_a_non_literal_argument_is_corroborated_not_entailed() -> None:
from openultrasast.model.config_value import verdict from openultrasast.model.config_value import verdict
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
rows = [{"setting": "CORS(app, origins=configured_origins)", "line": "3", "method": "create_app", rows = [
"literalArgs": [], "args": ["app", "configured_origins"]}] {
"setting": "CORS(app, origins=configured_origins)",
"line": "3",
"method": "create_app",
"literalArgs": [],
"args": ["app", "configured_origins"],
}
]
answer = verdict(_cpg(rows), _spec(), function="create_app") answer = verdict(_cpg(rows), _spec(), function="create_app")
assert answer is not None and answer.rung is Rung.CORROBORATED assert answer is not None and answer.rung is Rung.CORROBORATED
@@ -72,8 +85,7 @@ def test_a_failed_query_is_not_read_as_a_safe_configuration() -> None:
def test_the_verdict_is_deterministic() -> None: def test_the_verdict_is_deterministic() -> None:
from openultrasast.model.config_value import verdict from openultrasast.model.config_value import verdict
rows = [{"setting": 'set_cookie("s", secure=False)', "line": "9", "method": "login", rows = [{"setting": 'set_cookie("s", secure=False)', "line": "9", "method": "login", "literalArgs": ["True", "0.0.0.0"], "args": ["x"]}]
"literalArgs": ["True", "0.0.0.0"], "args": ["x"]}]
cpg = _cpg(rows) cpg = _cpg(rows)
assert verdict(cpg, _spec(), function="login") == verdict(cpg, _spec(), function="login") assert verdict(cpg, _spec(), function="login") == verdict(cpg, _spec(), function="login")
@@ -90,8 +102,12 @@ def test_the_settings_table_covers_the_frameworks_in_the_corpus() -> None:
from openultrasast.model.specs import config_specs from openultrasast.model.specs import config_specs
settings = set(config_specs(language="python")["config_secrets"].settings) settings = set(config_specs(language="python")["config_secrets"].settings)
for framework, call in (("aiohttp", "session_setup"), ("aiohttp", "EncryptedCookieStorage"), for framework, call in (
("django", "SECURE_SSL_REDIRECT"), ("flask", "set_cookie")): ("aiohttp", "session_setup"),
("aiohttp", "EncryptedCookieStorage"),
("django", "SECURE_SSL_REDIRECT"),
("flask", "set_cookie"),
):
assert call in settings, f"{framework}'s {call} is not a modelled security setting" assert call in settings, f"{framework}'s {call} is not a modelled security setting"
@@ -102,8 +118,7 @@ def test_a_weak_algorithm_is_entailed_by_its_literal() -> None:
from openultrasast.model.specs import config_specs from openultrasast.model.specs import config_specs
spec = config_specs(language="python")["config_secrets"] spec = config_specs(language="python")["config_secrets"]
rows = [{"setting": 'hashlib.new("md5")', "line": "4", "method": "digest", rows = [{"setting": 'hashlib.new("md5")', "line": "4", "method": "digest", "literalArgs": ['"md5"'], "args": ['"md5"']}]
"literalArgs": ['"md5"'], "args": ['"md5"']}]
answer = verdict(_cpg(rows), spec, function="digest") answer = verdict(_cpg(rows), spec, function="digest")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
assert "md5" in answer.witness.lower() assert "md5" in answer.witness.lower()
@@ -114,6 +129,5 @@ def test_a_strong_algorithm_yields_no_finding() -> None:
from openultrasast.model.specs import config_specs from openultrasast.model.specs import config_specs
spec = config_specs(language="python")["config_secrets"] spec = config_specs(language="python")["config_secrets"]
rows = [{"setting": 'hashlib.new("sha256")', "line": "4", "method": "digest", rows = [{"setting": 'hashlib.new("sha256")', "line": "4", "method": "digest", "literalArgs": ['"sha256"'], "args": ['"sha256"']}]
"literalArgs": ['"sha256"'], "args": ['"sha256"']}]
assert verdict(_cpg(rows), spec, function="digest") is None assert verdict(_cpg(rows), spec, function="digest") is None
+1 -2
View File
@@ -31,8 +31,7 @@ def _cpg(rows): # type: ignore[no-untyped-def]
def _op(method: str, guards: list[str], line: int = 10) -> dict[str, object]: def _op(method: str, guards: list[str], line: int = 10) -> dict[str, object]:
return {"operation": "Note.query.filter_by(id=nid)", "opLine": str(line), "opMethod": method, return {"operation": "Note.query.filter_by(id=nid)", "opLine": str(line), "opMethod": method, "dominatingGuards": guards}
"dominatingGuards": guards}
def test_an_unguarded_operation_among_guarded_siblings_is_entailed() -> None: def test_an_unguarded_operation_among_guarded_siblings_is_entailed() -> None:
+25 -21
View File
@@ -19,8 +19,7 @@ from pathlib import Path
def _spec(): # type: ignore[no-untyped-def] def _spec(): # type: ignore[no-untyped-def]
from openultrasast.model.specs import TaintSpec from openultrasast.model.specs import TaintSpec
return TaintSpec(family="injection", language="python", sources=("request.args",), return TaintSpec(family="injection", language="python", sources=("request.args",), sinks=("os.system",), sanitizers=("escape",))
sinks=("os.system",), sanitizers=("escape",))
def _cpg(rows): # type: ignore[no-untyped-def] def _cpg(rows): # type: ignore[no-untyped-def]
@@ -29,8 +28,9 @@ def _cpg(rows): # type: ignore[no-untyped-def]
return CpgResult(cpg_path=Path("cpg.bin"), run=lambda q, p: rows) return CpgResult(cpg_path=Path("cpg.bin"), run=lambda q, p: rows)
ENTAILING_ROW = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", ENTAILING_ROW = [
"source": "request.args['c']", "sanitized": False, "length": 3}] {"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sanitized": False, "length": 3}
]
class _Client: class _Client:
@@ -75,8 +75,7 @@ def test_a_claim_the_model_supports_advances_to_corroborated() -> None:
from openultrasast.model.judge import judge from openultrasast.model.judge import judge
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sanitized": True, "length": 3}]
"source": "request.args['c']", "sanitized": True, "length": 3}]
client = _Client('{"vulnerable": true, "family": "injection"}') client = _Client('{"vulnerable": true, "family": "injection"}')
answer = judge(_cpg(rows), _spec(), function="run", client=client, model="m") answer = judge(_cpg(rows), _spec(), function="run", client=client, model="m")
assert answer.rung is Rung.CORROBORATED assert answer.rung is Rung.CORROBORATED
@@ -88,8 +87,7 @@ def test_the_llm_declining_leaves_a_corroborated_flow_at_suspicion() -> None:
from openultrasast.model.judge import judge from openultrasast.model.judge import judge
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sanitized": True, "length": 3}]
"source": "request.args['c']", "sanitized": True, "length": 3}]
client = _Client('{"vulnerable": false, "family": "injection"}') client = _Client('{"vulnerable": false, "family": "injection"}')
answer = judge(_cpg(rows), _spec(), function="run", client=client, model="m") answer = judge(_cpg(rows), _spec(), function="run", client=client, model="m")
assert answer.rung is Rung.SUSPICION assert answer.rung is Rung.SUSPICION
@@ -101,8 +99,9 @@ def test_without_a_client_the_verdict_still_stands_on_its_own() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
assert judge(_cpg(ENTAILING_ROW), _spec(), function="run", client=None, model="").rung is Rung.ENTAILED assert judge(_cpg(ENTAILING_ROW), _spec(), function="run", client=None, model="").rung is Rung.ENTAILED
sanitized = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", sanitized = [
"source": "request.args['c']", "sanitized": True, "length": 3}] {"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sanitized": True, "length": 3}
]
answer = judge(_cpg(sanitized), _spec(), function="run", client=None, model="") answer = judge(_cpg(sanitized), _spec(), function="run", client=None, model="")
assert answer.rung is Rung.SUSPICION and "endpoint" in answer.contradiction.lower() assert answer.rung is Rung.SUSPICION and "endpoint" in answer.contradiction.lower()
@@ -111,8 +110,7 @@ def test_the_question_is_bounded_typed_and_offers_no_tools() -> None:
"""Req 8.1: the LLM answers one question about a site it was handed; it never searches for the site.""" """Req 8.1: the LLM answers one question about a site it was handed; it never searches for the site."""
from openultrasast.model.judge import judge from openultrasast.model.judge import judge
rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sanitized": True, "length": 3}]
"source": "request.args['c']", "sanitized": True, "length": 3}]
client = _Client() client = _Client()
judge(_cpg(rows), _spec(), function="run", client=client, model="m") judge(_cpg(rows), _spec(), function="run", client=client, model="m")
call = client.calls[0] call = client.calls[0]
@@ -126,8 +124,16 @@ def test_the_prompt_is_redacted() -> None:
"""Req 11.4: every prompt passes through redaction.""" """Req 11.4: every prompt passes through redaction."""
from openultrasast.model.judge import judge from openultrasast.model.judge import judge
rows = [{"sink": "connect(pw)", "sinkLine": "4", "sinkMethod": "run", rows = [
"source": "AKIA" + "IOSFODNN7" + "EXAMPLE", "sanitized": True, "length": 2}] {
"sink": "connect(pw)",
"sinkLine": "4",
"sinkMethod": "run",
"source": "AKIA" + "IOSFODNN7" + "EXAMPLE",
"sanitized": True,
"length": 2,
}
]
client = _Client() client = _Client()
judge(_cpg(rows), _spec(), function="run", client=client, model="m") judge(_cpg(rows), _spec(), function="run", client=client, model="m")
text = " ".join(str(m.get("content", "")) for m in client.calls[0]["messages"]) # type: ignore[index,union-attr] text = " ".join(str(m.get("content", "")) for m in client.calls[0]["messages"]) # type: ignore[index,union-attr]
@@ -142,13 +148,11 @@ def test_there_is_no_k_run_averaging_anywhere_in_the_judge() -> None:
tree = ast.parse(source) tree = ast.parse(source)
# Check CODE, not prose: the docstring is allowed to name the machinery it replaced. # Check CODE, not prose: the docstring is allowed to name the machinery it replaced.
names = { names = (
node.id for node in ast.walk(tree) if isinstance(node, ast.Name) {node.id for node in ast.walk(tree) if isinstance(node, ast.Name)}
} | { | {node.attr for node in ast.walk(tree) if isinstance(node, ast.Attribute)}
node.attr for node in ast.walk(tree) if isinstance(node, ast.Attribute) | {node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)}
} | { )
node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)
}
for banned in ("k_runs", "majority", "vote", "votes", "average", "mean", "acceptance", "floor", "budget"): for banned in ("k_runs", "majority", "vote", "votes", "average", "mean", "acceptance", "floor", "budget"):
assert banned not in names, f"{banned} has no place in the judge" assert banned not in names, f"{banned} has no place in the judge"
+13 -3
View File
@@ -15,9 +15,19 @@ def _finding(**kw: object): # type: ignore[no-untyped-def]
from openultrasast.findings import StaticFinding from openultrasast.findings import StaticFinding
base = dict( base = dict(
finding_id="r1:app.py:1", path="app.py", title="t", severity="high", confidence="medium", finding_id="r1:app.py:1",
evidence_level="static_corroboration", rationale="why", line=1, function_name="run", path="app.py",
reachability_status="unknown", reachability_evidence=[], reachability_conditions=[], tags=[], title="t",
severity="high",
confidence="medium",
evidence_level="static_corroboration",
rationale="why",
line=1,
function_name="run",
reachability_status="unknown",
reachability_evidence=[],
reachability_conditions=[],
tags=[],
ranking_priority=1.0, ranking_priority=1.0,
) )
base.update(kw) base.update(kw)
+52 -21
View File
@@ -24,8 +24,7 @@ from pathlib import Path
def _spec(): # type: ignore[no-untyped-def] def _spec(): # type: ignore[no-untyped-def]
from openultrasast.model.specs import TaintSpec from openultrasast.model.specs import TaintSpec
return TaintSpec(family="injection", language="python", sources=("request.args",), return TaintSpec(family="injection", language="python", sources=("request.args",), sinks=("os.system",), sanitizers=("escape",))
sinks=("os.system",), sanitizers=("escape",))
def _cpg(rows): # type: ignore[no-untyped-def] def _cpg(rows): # type: ignore[no-untyped-def]
@@ -46,10 +45,28 @@ class _Client:
return ChatResponse(content=self._answer) return ChatResponse(content=self._answer)
ENTAILING = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", ENTAILING = [
"sanitized": False, "length": 3, "inLabeledScope": True}] {
SANITIZED = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "request.args['c']", "sink": "os.system(cmd)",
"sanitized": True, "length": 3, "inLabeledScope": True}] "sinkLine": "4",
"sinkMethod": "run",
"source": "request.args['c']",
"sanitized": False,
"length": 3,
"inLabeledScope": True,
}
]
SANITIZED = [
{
"sink": "os.system(cmd)",
"sinkLine": "4",
"sinkMethod": "run",
"source": "request.args['c']",
"sanitized": True,
"length": 3,
"inLabeledScope": True,
}
]
def test_an_entailed_site_becomes_a_finding_with_no_model_call() -> None: def test_an_entailed_site_becomes_a_finding_with_no_model_call() -> None:
@@ -85,8 +102,14 @@ def test_a_candidate_the_model_cannot_see_is_reported_at_suspicion() -> None:
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
client = _Client() client = _Client()
findings = scan_region(_cpg([]), _spec(), function="run", client=client, model="m", findings = scan_region(
candidates=({"id": "app.py:9:raw_query", "text": "db.raw(q)", "line": 9},)) _cpg([]),
_spec(),
function="run",
client=client,
model="m",
candidates=({"id": "app.py:9:raw_query", "text": "db.raw(q)", "line": 9},),
)
assert client.calls == 1, "one bounded question per candidate" assert client.calls == 1, "one bounded question per candidate"
assert len(findings) == 1 and findings[0].rung is Rung.SUSPICION assert len(findings) == 1 and findings[0].rung is Rung.SUSPICION
assert findings[0].site == "app.py:9:raw_query" assert findings[0].site == "app.py:9:raw_query"
@@ -96,8 +119,14 @@ def test_a_candidate_the_judge_declines_is_not_reported() -> None:
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
client = _Client('{"vulnerable": false, "why": "constant"}') client = _Client('{"vulnerable": false, "why": "constant"}')
findings = scan_region(_cpg([]), _spec(), function="run", client=client, model="m", findings = scan_region(
candidates=({"id": "app.py:9:x", "text": "db.raw('literal')", "line": 9},)) _cpg([]),
_spec(),
function="run",
client=client,
model="m",
candidates=({"id": "app.py:9:x", "text": "db.raw('literal')", "line": 9},),
)
assert findings == [] assert findings == []
@@ -107,8 +136,9 @@ def test_without_a_client_only_what_the_model_establishes_is_reported() -> None:
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
assert scan_region(_cpg(ENTAILING), _spec(), function="run", client=None, model="", candidates=())[0].rung is Rung.ENTAILED assert scan_region(_cpg(ENTAILING), _spec(), function="run", client=None, model="", candidates=())[0].rung is Rung.ENTAILED
assert scan_region(_cpg([]), _spec(), function="run", client=None, model="", assert (
candidates=({"id": "a:1:x", "text": "t", "line": 1},)) == [] scan_region(_cpg([]), _spec(), function="run", client=None, model="", candidates=({"id": "a:1:x", "text": "t", "line": 1},)) == []
)
def test_the_candidate_budget_is_bounded() -> None: def test_the_candidate_budget_is_bounded() -> None:
@@ -132,8 +162,7 @@ def test_the_candidate_budget_is_bounded() -> None:
def _dominance_spec(): # type: ignore[no-untyped-def] def _dominance_spec(): # type: ignore[no-untyped-def]
from openultrasast.model.specs import DominanceSpec from openultrasast.model.specs import DominanceSpec
return DominanceSpec(family="access_control", language="python", return DominanceSpec(family="access_control", language="python", operations=("filter_by",), dischargers=("current_user",))
operations=("filter_by",), dischargers=("current_user",))
def _config_spec(): # type: ignore[no-untyped-def] def _config_spec(): # type: ignore[no-untyped-def]
@@ -146,9 +175,10 @@ def test_an_absence_family_is_arbitrated_by_dominance_not_taint() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
rows = [{"operation": "Note.query.filter_by(id=n)", "opLine": "9", "opMethod": "leaky", "dominatingGuards": []}, rows = [
{"operation": "Note.query.filter_by(id=n)", "opLine": "3", "opMethod": "safe", {"operation": "Note.query.filter_by(id=n)", "opLine": "9", "opMethod": "leaky", "dominatingGuards": []},
"dominatingGuards": ["current_user.id"]}] {"operation": "Note.query.filter_by(id=n)", "opLine": "3", "opMethod": "safe", "dominatingGuards": ["current_user.id"]},
]
findings = scan_region(_cpg(rows), _dominance_spec(), function="leaky", client=None, model="", candidates=()) findings = scan_region(_cpg(rows), _dominance_spec(), function="leaky", client=None, model="", candidates=())
assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED
assert findings[0].family == "access_control" assert findings[0].family == "access_control"
@@ -158,8 +188,7 @@ def test_a_configuration_family_is_arbitrated_by_constant_abstraction() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
rows = [{"setting": 'CORS(app, origins="*")', "line": "3", "method": "create_app", rows = [{"setting": 'CORS(app, origins="*")', "line": "3", "method": "create_app", "literalArgs": ['"*"'], "args": ["app"]}]
"literalArgs": ['"*"'], "args": ["app"]}]
findings = scan_region(_cpg(rows), _config_spec(), function="create_app", client=None, model="", candidates=()) findings = scan_region(_cpg(rows), _config_spec(), function="create_app", client=None, model="", candidates=())
assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED
assert findings[0].family == "config_secrets" assert findings[0].family == "config_secrets"
@@ -191,8 +220,10 @@ def test_a_corroborated_absence_finding_survives_a_judge_that_agrees() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.pipeline import scan_region from openultrasast.model.pipeline import scan_region
rows = [{"operation": "q.filter_by(id=n)", "opLine": "9", "opMethod": "leaky", "dominatingGuards": []}, rows = [
{"operation": "q.filter_by(id=n)", "opLine": "3", "opMethod": "safe", "dominatingGuards": ["current_user"]}] {"operation": "q.filter_by(id=n)", "opLine": "9", "opMethod": "leaky", "dominatingGuards": []},
{"operation": "q.filter_by(id=n)", "opLine": "3", "opMethod": "safe", "dominatingGuards": ["current_user"]},
]
findings = scan_region(_cpg(rows), _dominance_spec(), function="leaky", client=_Client(), model="m", candidates=()) findings = scan_region(_cpg(rows), _dominance_spec(), function="leaky", client=_Client(), model="m", candidates=())
assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED assert len(findings) == 1 and findings[0].rung is Rung.ENTAILED
+15 -4
View File
@@ -62,10 +62,21 @@ def test_every_finding_shows_its_rung() -> None:
from openultrasast.model.report import render_findings from openultrasast.model.report import render_findings
finding = StaticFinding( finding = StaticFinding(
finding_id="r:app.py:1", path="app.py", title="t", severity="high", confidence="medium", finding_id="r:app.py:1",
evidence_level="static_corroboration", rationale="why", line=1, function_name="run", path="app.py",
reachability_status="unknown", reachability_evidence=[], reachability_conditions=[], tags=[], title="t",
ranking_priority=1.0, rung=Rung.ENTAILED, severity="high",
confidence="medium",
evidence_level="static_corroboration",
rationale="why",
line=1,
function_name="run",
reachability_status="unknown",
reachability_evidence=[],
reachability_conditions=[],
tags=[],
ranking_priority=1.0,
rung=Rung.ENTAILED,
) )
text = render_findings([finding]) text = render_findings([finding])
assert "model_entailed" in text and "app.py" in text assert "model_entailed" in text and "app.py" in text
+10 -3
View File
@@ -26,7 +26,7 @@ def test_the_guard_classifier_still_recognises_each_kind() -> None:
assert classify_guard_text('cur.execute("SELECT * FROM t WHERE id = %s", (uid,))') == "parameterized_call" assert classify_guard_text('cur.execute("SELECT * FROM t WHERE id = %s", (uid,))') == "parameterized_call"
assert classify_guard_text("if not current_user.is_authenticated: abort(403)") == "auth_check" assert classify_guard_text("if not current_user.is_authenticated: abort(403)") == "auth_check"
assert classify_guard_text('if name not in ALLOWED: abort(400)') == "allowlist_test" assert classify_guard_text("if name not in ALLOWED: abort(400)") == "allowlist_test"
assert classify_guard_text("if len(buf) > 64: return") == "bounds_test" assert classify_guard_text("if len(buf) > 64: return") == "bounds_test"
assert classify_guard_text("if value is None: return") == "null_test" assert classify_guard_text("if value is None: return") == "null_test"
assert classify_guard_text("page = int(raw)") == "type_change" assert classify_guard_text("page = int(raw)") == "type_change"
@@ -50,8 +50,15 @@ def test_the_seeded_families_and_the_named_gaps_are_both_exact() -> None:
seeded |= {spec.family for spec in dominance_specs(language=language).values()} seeded |= {spec.family for spec in dominance_specs(language=language).values()}
deferred = {"unknown", "memory"} # the declined bucket and the deferred execution tier deferred = {"unknown", "memory"} # the declined bucket and the deferred execution tier
assert seeded == { assert seeded == {
"injection", "deserialization", "config_secrets", "access_control", "injection",
"path", "output_encoding", "untrusted_destination", "prototype", "memory", "deserialization",
"config_secrets",
"access_control",
"path",
"output_encoding",
"untrusted_destination",
"prototype",
"memory",
} }
gaps = {family.id for family in taxonomy.families} - seeded - deferred gaps = {family.id for family in taxonomy.families} - seeded - deferred
assert gaps == set(), f"a family lost its seed: {sorted(gaps)}" assert gaps == set(), f"a family lost its seed: {sorted(gaps)}"
+124 -38
View File
@@ -71,8 +71,12 @@ def test_the_verdict_is_restricted_to_the_labeled_function() -> None:
rows = [ rows = [
{ {
"sink": "os.system(x)", "sinkLine": "40", "sinkMethod": "elsewhere", "sink": "os.system(x)",
"source": "request.args['x']", "sanitized": False, "length": 2, "sinkLine": "40",
"sinkMethod": "elsewhere",
"source": "request.args['x']",
"sanitized": False,
"length": 2,
} }
] ]
assert verdict(_cpg(rows), _spec(), function="run") is None, "a flow in another function does not judge this one" assert verdict(_cpg(rows), _spec(), function="run") is None, "a flow in another function does not judge this one"
@@ -141,10 +145,18 @@ def _shape_spec(): # type: ignore[no-untyped-def]
def test_a_parameterised_sink_call_is_not_a_vulnerability_even_though_the_flow_reaches_it() -> None: def test_a_parameterised_sink_call_is_not_a_vulnerability_even_though_the_flow_reaches_it() -> None:
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{ rows = [
"sink": 'db.execute("select ... = %s", (name,))', "sinkLine": "5", "sinkMethod": "run", {
"source": "request.args['name']", "sanitized": False, "length": 4, "sinkArity": 2, "sinkArg0Literal": True, "sink": 'db.execute("select ... = %s", (name,))',
}] "sinkLine": "5",
"sinkMethod": "run",
"source": "request.args['name']",
"sanitized": False,
"length": 4,
"sinkArity": 2,
"sinkArg0Literal": True,
}
]
assert verdict(_cpg(rows), _shape_spec(), function="run") is None, "a bound query is the fix, not the bug" assert verdict(_cpg(rows), _shape_spec(), function="run") is None, "a bound query is the fix, not the bug"
@@ -152,10 +164,18 @@ def test_an_interpolated_sink_call_of_the_same_name_is_entailed() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{ rows = [
"sink": 'db.execute("select ... \'" + name + "\'")', "sinkLine": "5", "sinkMethod": "run", {
"source": "request.args['name']", "sanitized": False, "length": 4, "sinkArity": 1, "sinkArg0Literal": False, "sink": 'db.execute("select ... \'" + name + "\'")',
}] "sinkLine": "5",
"sinkMethod": "run",
"source": "request.args['name']",
"sanitized": False,
"length": 4,
"sinkArity": 1,
"sinkArg0Literal": False,
}
]
answer = verdict(_cpg(rows), _shape_spec(), function="run") answer = verdict(_cpg(rows), _shape_spec(), function="run")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
@@ -165,10 +185,18 @@ def test_the_shape_test_only_applies_to_sinks_declared_safe_in_that_shape() -> N
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{ rows = [
"sink": "os.system(full)", "sinkLine": "7", "sinkMethod": "run", {
"source": "request.args['cmd']", "sanitized": False, "length": 5, "sinkArity": 1, "sinkArg0Literal": False, "sink": "os.system(full)",
}] "sinkLine": "7",
"sinkMethod": "run",
"source": "request.args['cmd']",
"sanitized": False,
"length": 5,
"sinkArity": 1,
"sinkArg0Literal": False,
}
]
answer = verdict(_cpg(rows), _spec(), function="run") # os.system is not in safe_shape_sinks answer = verdict(_cpg(rows), _spec(), function="run") # os.system is not in safe_shape_sinks
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
@@ -178,8 +206,7 @@ def test_a_row_without_shape_fields_is_judged_on_the_flow_alone() -> None:
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{"sink": "db.execute(q)", "sinkLine": "5", "sinkMethod": "run", "source": "request.args['n']", rows = [{"sink": "db.execute(q)", "sinkLine": "5", "sinkMethod": "run", "source": "request.args['n']", "sanitized": False, "length": 3}]
"sanitized": False, "length": 3}]
answer = verdict(_cpg(rows), _shape_spec(), function="run") answer = verdict(_cpg(rows), _shape_spec(), function="run")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
@@ -225,10 +252,17 @@ def test_a_sink_in_a_nested_closure_is_in_scope_when_the_query_says_so() -> None
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{ rows = [
"sink": "fs.readFileSync(possibleFilename)", "sinkLine": "6", "sinkMethod": "<lambda>0", {
"source": "req", "sanitized": False, "length": 4, "inLabeledScope": True, "sink": "fs.readFileSync(possibleFilename)",
}] "sinkLine": "6",
"sinkMethod": "<lambda>0",
"source": "req",
"sanitized": False,
"length": 4,
"inLabeledScope": True,
}
]
answer = verdict(_cpg(rows), _spec(), function="requestListener") answer = verdict(_cpg(rows), _spec(), function="requestListener")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
assert "<lambda>0" not in answer.witness, "the witness names the flow, not the synthetic closure name" assert "<lambda>0" not in answer.witness, "the witness names the flow, not the synthetic closure name"
@@ -237,10 +271,17 @@ def test_a_sink_in_a_nested_closure_is_in_scope_when_the_query_says_so() -> None
def test_a_sink_in_an_unrelated_function_stays_out_of_scope() -> None: def test_a_sink_in_an_unrelated_function_stays_out_of_scope() -> None:
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{ rows = [
"sink": "fs.readFileSync('/etc/' + name)", "sinkLine": "11", "sinkMethod": "unrelated", {
"source": "name", "sanitized": False, "length": 2, "inLabeledScope": False, "sink": "fs.readFileSync('/etc/' + name)",
}] "sinkLine": "11",
"sinkMethod": "unrelated",
"source": "name",
"sanitized": False,
"length": 2,
"inLabeledScope": False,
}
]
assert verdict(_cpg(rows), _spec(), function="requestListener") is None assert verdict(_cpg(rows), _spec(), function="requestListener") is None
@@ -248,8 +289,9 @@ def test_rows_without_the_scope_flag_fall_back_to_the_method_name() -> None:
"""A row from an engine that cannot report scope must not silently become in-scope.""" """A row from an engine that cannot report scope must not silently become in-scope."""
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{"sink": "os.system(x)", "sinkLine": "9", "sinkMethod": "<lambda>0", rows = [
"source": "request.args['x']", "sanitized": False, "length": 2}] {"sink": "os.system(x)", "sinkLine": "9", "sinkMethod": "<lambda>0", "source": "request.args['x']", "sanitized": False, "length": 2}
]
assert verdict(_cpg(rows), _spec(), function="run") is None assert verdict(_cpg(rows), _spec(), function="run") is None
@@ -266,12 +308,33 @@ def test_a_flow_from_a_real_source_outranks_a_shorter_one_from_a_bare_parameter(
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [ rows = [
{"sink": "fs.readFileSync(p)", "sinkLine": "6", "sinkMethod": "<lambda>0", "source": "res", {
"sanitized": False, "length": 3, "inLabeledScope": True}, "sink": "fs.readFileSync(p)",
{"sink": "fs.readFileSync(p)", "sinkLine": "6", "sinkMethod": "<lambda>0", "source": "this", "sinkLine": "6",
"sanitized": False, "length": 7, "inLabeledScope": True}, "sinkMethod": "<lambda>0",
{"sink": "fs.readFileSync(p)", "sinkLine": "6", "sinkMethod": "<lambda>0", "source": "res",
"source": "request.args['f']", "sanitized": False, "length": 11, "inLabeledScope": True}, "sanitized": False,
"length": 3,
"inLabeledScope": True,
},
{
"sink": "fs.readFileSync(p)",
"sinkLine": "6",
"sinkMethod": "<lambda>0",
"source": "this",
"sanitized": False,
"length": 7,
"inLabeledScope": True,
},
{
"sink": "fs.readFileSync(p)",
"sinkLine": "6",
"sinkMethod": "<lambda>0",
"source": "request.args['f']",
"sanitized": False,
"length": 11,
"inLabeledScope": True,
},
] ]
answer = verdict(_cpg(rows), _spec(), function="requestListener") answer = verdict(_cpg(rows), _spec(), function="requestListener")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
@@ -291,10 +354,24 @@ def test_a_modelled_source_flow_decides_even_when_a_parameter_flow_is_unsanitize
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [ rows = [
{"sink": "fs.readFileSync(safe)", "sinkLine": "41", "sinkMethod": "<lambda>0", "source": "res", {
"sanitized": False, "length": 3, "inLabeledScope": True}, "sink": "fs.readFileSync(safe)",
{"sink": "fs.readFileSync(safe)", "sinkLine": "41", "sinkMethod": "<lambda>0", "sinkLine": "41",
"source": "request.args['f']", "sanitized": True, "length": 22, "inLabeledScope": True}, "sinkMethod": "<lambda>0",
"source": "res",
"sanitized": False,
"length": 3,
"inLabeledScope": True,
},
{
"sink": "fs.readFileSync(safe)",
"sinkLine": "41",
"sinkMethod": "<lambda>0",
"source": "request.args['f']",
"sanitized": True,
"length": 22,
"inLabeledScope": True,
},
] ]
answer = verdict(_cpg(rows), _spec(), function="requestListener") answer = verdict(_cpg(rows), _spec(), function="requestListener")
assert answer is not None and answer.rung is Rung.CORROBORATED, "a sanitized real flow is not an entailment" assert answer is not None and answer.rung is Rung.CORROBORATED, "a sanitized real flow is not an entailment"
@@ -305,7 +382,16 @@ def test_parameter_flows_still_decide_when_no_modelled_source_reaches_the_sink()
from openultrasast.model.ladder import Rung from openultrasast.model.ladder import Rung
from openultrasast.model.taint import verdict from openultrasast.model.taint import verdict
rows = [{"sink": "os.system(cmd)", "sinkLine": "4", "sinkMethod": "run", "source": "username", rows = [
"sanitized": False, "length": 2, "inLabeledScope": True}] {
"sink": "os.system(cmd)",
"sinkLine": "4",
"sinkMethod": "run",
"source": "username",
"sanitized": False,
"length": 2,
"inLabeledScope": True,
}
]
answer = verdict(_cpg(rows), _spec(), function="run") answer = verdict(_cpg(rows), _spec(), function="run")
assert answer is not None and answer.rung is Rung.ENTAILED assert answer is not None and answer.rung is Rung.ENTAILED
+1 -2
View File
@@ -18,8 +18,7 @@ IDENTICAL_TWINS = {
# `owasp` added 2026-09-08: 40 generated BenchmarkJava xss pairs, the only harvested category the model was # `owasp` added 2026-09-08: 40 generated BenchmarkJava xss pairs, the only harvested category the model was
# shown to distinguish before vendoring. This guard exists so a corpus change is never silent — updating it # shown to distinguish before vendoring. This guard exists so a corpus change is never silent — updating it
# is the acknowledgement. # is the acknowledgement.
VENDORED_PER_SLICE = {"local": 3, "github": 6, "sast": 11, "vibe-py": 35, "vfc-js": 17, "vfc": 176, VENDORED_PER_SLICE = {"local": 3, "github": 6, "sast": 11, "vibe-py": 35, "vfc-js": 17, "vfc": 176, "agent-vfc": 29, "owasp": 40}
"agent-vfc": 29, "owasp": 40}
def test_identical_twins_are_unscorable_and_no_row_is_dropped() -> None: def test_identical_twins_are_unscorable_and_no_row_is_dropped() -> None:
+1 -2
View File
@@ -403,8 +403,7 @@ def test_the_vendored_count_is_unchanged_by_the_re_harvest() -> None:
per_slice: dict[str, int] = {} per_slice: dict[str, int] = {}
for case in cases: for case in cases:
per_slice[case.slice] = per_slice.get(case.slice, 0) + 1 per_slice[case.slice] = per_slice.get(case.slice, 0) + 1
assert per_slice == {"agent-vfc": 29, "github": 6, "local": 3, "owasp": 40, "sast": 11, "vfc": 176, assert per_slice == {"agent-vfc": 29, "github": 6, "local": 3, "owasp": 40, "sast": 11, "vfc": 176, "vfc-js": 17, "vibe-py": 35}
"vfc-js": 17, "vibe-py": 35}
def test_prose_inside_a_decorator_never_grants_a_guard(tmp_path: Path) -> None: def test_prose_inside_a_decorator_never_grants_a_guard(tmp_path: Path) -> None:
-2
View File
@@ -263,5 +263,3 @@ def test_markdown_report_includes_four_overlay_labels(tmp_path: Path) -> None:
assert "demoted" in text assert "demoted" in text
assert "unadjudicated" in text assert "unadjudicated" in text
assert "coverage" in text assert "coverage" in text
+18 -4
View File
@@ -53,8 +53,14 @@ def test_no_surviving_module_imports_a_removed_one() -> None:
def test_the_removed_modules_are_gone_from_the_tree() -> None: def test_the_removed_modules_are_gone_from_the_tree() -> None:
assert not (SRC / "learning").exists(), "the learning package is removed; taxonomy/candidates/endpoint live under model/" assert not (SRC / "learning").exists(), "the learning package is removed; taxonomy/candidates/endpoint live under model/"
for relative in ("semantic/mechanisms.py", "semantic/variants.py", "semantic/variant_search.py", for relative in (
"semantic/prove_budget.py", "semantic/loo.py", "semantic/seed.py"): "semantic/mechanisms.py",
"semantic/variants.py",
"semantic/variant_search.py",
"semantic/prove_budget.py",
"semantic/loo.py",
"semantic/seed.py",
):
assert not (SRC / relative).exists(), f"{relative} still exists" assert not (SRC / relative).exists(), f"{relative} still exists"
@@ -69,8 +75,16 @@ def test_the_mechanism_lever_is_gone_from_the_retained_improve_loop() -> None:
""" """
evolve = (SRC / "improve" / "evolve.py").read_text() evolve = (SRC / "improve" / "evolve.py").read_text()
validator = (SRC / "improve" / "validator.py").read_text() validator = (SRC / "improve" / "validator.py").read_text()
for symbol in ("MechanismEdit", "MechanismStore", "apply_mechanism_edits", "evaluate_mechanism_profiles", for symbol in (
"propose_mechanism_edits", "validate_mechanism", "mechanism_store", "mechanism_candidates"): "MechanismEdit",
"MechanismStore",
"apply_mechanism_edits",
"evaluate_mechanism_profiles",
"propose_mechanism_edits",
"validate_mechanism",
"mechanism_store",
"mechanism_candidates",
):
assert symbol not in evolve, f"{symbol} survives in improve/evolve.py" assert symbol not in evolve, f"{symbol} survives in improve/evolve.py"
assert symbol not in validator, f"{symbol} survives in improve/validator.py" assert symbol not in validator, f"{symbol} survives in improve/validator.py"
assert "def run_improvement(" in evolve and "def propose_status_edits(" in evolve, "the rule-status loop must survive" assert "def run_improvement(" in evolve and "def propose_status_edits(" in evolve, "the rule-status loop must survive"