mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
fix(model-grounded-detection): two modelling bugs the first live Joern run exposed
Both were found by running the real engine, and neither was visible offline.
1. A sink was its own sanitizer. The shipped facts mark some sanitizers with a
*shape* qualifier -- parameterized ("execute(sql, params) binds rather than
interpolates") and literal_format_arg ("printf with a constant format string
is safe"). These describe a safe FORM OF THE SINK CALL, not a call that
cleans a value on the path. Flattening them into the sanitizer list made
`execute` both the injection sink and its own sanitizer, so every SQL flow
would have reported as already-clean and nothing could ever be entailed.
Fixed as a class, not an instance: any fact carrying a shape qualifier is
excluded from `sanitizers` and kept in `safe_shape_sinks` for the shape test
task 4.2 will model properly. Fixing only the Python `parameterized` case
would have left C's entire printf family -- which the generalised test then
caught immediately.
2. labeled_family lost its deterministic derivation in group 1. The deleted
classifier resolved a pair's family by declared label, else obligation, else
mechanism, else CWE, and only asked an LLM when every link failed -- which
for a labeled corpus was never. Group 1 reduced this to the declared label
alone, which silently emptied every family whose pairs are labeled by CWE:
the injection slice went to zero pairs. The chain is restored, still with no
model call, and the family census again matches the committed entailment
ceiling exactly (injection 50, memory 176, access_control 8, ...).
Also: the Joern seam now runs in the CPG's own scratch directory, because
`joern --script` writes a `workspace/` beside the working directory and would
otherwise leave one inside the repository being analysed.
The taint query itself was corrected against the live engine: the sink is the
call's ARGUMENTS (data flows into arguments, not into the call node) and
reachableByFlows is called on the sink with the source. With that, the
three-hop chain our flat IR could not resolve is found:
request.args["cmd"] -> cmd -> "echo " + cmd -> full -> os.system(full)
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
co-authored by
Claude Opus 5
parent
2b973dc0db
commit
f59e0f5b5a
@@ -99,3 +99,30 @@ def test_the_specs_module_exposes_no_writer() -> None:
|
||||
]
|
||||
assert not writers, f"specs must be read-only, found writers: {writers}"
|
||||
assert "open(" not in SPECS.read_text().replace("# ", ""), "specs must not open a file for writing"
|
||||
|
||||
|
||||
def test_a_sink_is_never_its_own_sanitizer() -> None:
|
||||
"""A parameterized fact names a safe sink *shape*, not a cleansing call on the path.
|
||||
|
||||
Two shipped facts have this shape: `parameterized_execute` ("execute with bound parameters is safe") and
|
||||
C's `literal_format` ("printf with a constant format string is safe"). Flattened into the sanitizer list
|
||||
each makes its sink its own sanitizer, so every flow through it reports as already-clean and nothing is
|
||||
ever entailed. Both were caught on the first live Joern run — the second only because the first was fixed
|
||||
as a class rather than as an instance.
|
||||
"""
|
||||
from openultrasast.model.specs import taint_specs
|
||||
|
||||
for language in ("python", "javascript", "java", "c"):
|
||||
for spec in taint_specs(language=language).values():
|
||||
overlap = set(spec.sinks) & set(spec.sanitizers)
|
||||
assert not overlap, f"{language}/{spec.family}: sink is its own sanitizer: {sorted(overlap)}"
|
||||
|
||||
|
||||
def test_safe_shape_sinks_are_kept_for_the_shape_test() -> None:
|
||||
from openultrasast.model.specs import taint_specs
|
||||
|
||||
spec = taint_specs(language="python")["injection"]
|
||||
assert "execute" in spec.safe_shape_sinks, "the safe-shape names must survive for a later shape test"
|
||||
assert "execute" not in spec.sanitizers
|
||||
c_memory = taint_specs(language="c").get("memory")
|
||||
assert c_memory is not None and "printf" in c_memory.safe_shape_sinks
|
||||
|
||||
Reference in New Issue
Block a user