Same CPG, only the search changes. Baseline (shipped path spec) resolves 0 prompt->write_text flows on banks; injecting the two annotations an LLM steer would supply (parameter_sources=True + write_text/read_text path sinks) makes the engine resolve prompt -> path-join -> prompt_file.write_text(prompt.raw). First measured instance of "#2 guided by #1" closing on an unseen target; fix needed exactly the param-source + file-write-sink annotations. Files the instrument bug the first MVP hit: single-query cpg.run("taint", request_params(...)) is dead -- request_params emits 5 keys (guards/fixedOrigin/ originAnchors/quotedSanitizers/fieldParameterSources) absent from taint.sc's exec signature; both single-query sub-routes reject them (silent None). Batch path (run_batch->requestsFile JSON) unaffected; push/scan route uses batch. Co-Authored-By: Claude Opus 4.8 <[email protected]>
4.8 KiB
BUG: taint.sc single-query path broken by exec-signature skew
Filed: 2026-10-12 · Repo HEAD at filing: 7ab5c2a · Severity: high (silent zero) · Status: OPEN
Summary
Any call on the single-query taint path — cpg.run("taint", request_params(...)) — fails on both
of its sub-routes, returns None, and is read upstream as "no flows / clean". request_params() emits five
keys that taint.sc's @main def exec(...) does not declare, and the single-query dispatch passes every
param straight to the script. The batch path (run_batch → requestsFile JSON) is unaffected, which is
why the engine-on scans ran while the single-query MVP did not.
This is exactly the failure shape AGENTS.md warns about: a tool that could not read/execute its input reports a plausible zero that is indistinguishable from a clean result.
Mechanism
request_params() (src/openultrasast/model/taint.py) always emits, among others:
fieldParameterSources(line 54)fixedOrigin(line 65)originAnchors(line 66)quotedSanitizers(line 67)guards(line 68)
taint.sc's signature (src/openultrasast/cpg/queries/taint.sc:41 @main def exec(...)) declares none
of those five. Its 19 params are: cpgFile, sources, sinks, sanitizers, function, file, callDepth, boundedSinks, parameterSources, fieldSources, evidenceOnly, hookCallbacks, dispatchApply, dispatchValue, contextFilter, contextPaths, requests, requestsFile, trace.
backend.query() (src/openultrasast/cpg/backend.py:1266) dispatches a single query two ways, both of
which choke on the undeclared keys:
- Disposable / CLI route (lines 1276–1278): appends
--param {key}={value}for every param. joern errorsUnknown arguments: --fixedOrigin --guards --quotedSanitizers --originAnchors --fieldParameterSources(and, as a knock-on,Missing argument: --cpgFile). Observed verbatim viabanks_debug.pyon 2026-10-12. - Session route (lines 1321–1326): defines
def ousast_taint(...)by renaming@main def exec(and then callsousast_taint(key = value, ...)with named args. The same five keys are not parameters of that method → the Scala named-arg call fails to compile. (By construction; the rename preserves the exactexecparameter list.)
Why the batch path survives: run_batch writes the per-request params inside the requestsFile JSON
object, and taint.sc parses that JSON itself (requestsJson, taint.sc:67). The extra fields ride as JSON
keys, never as exec arguments, so they are tolerated.
Blast radius
Broken (single-query taint):
taint._usable_flows(taint.py:124) →verdicts()(taint.py:131) →verdict()(taint.py:99).pipeline.scan_regiondispatches taint families totaint_verdict(pipeline.py:121) → so per-region single-query taint viascan_regionis dead whenever the disposable or session route is taken.
Unaffected:
- The push/scan batch path:
scan.pyusesrun_batch("taint", …)(scan.py:1259, 1264, 1387). This is why the engine-on pilot produced results. configanddominancesingle-queries (config_value.py:46,dominance.py:54) — their.scsignatures are presumed to match what their specs emit (confirm separately; not exercised here).
Open question to close: enumerate every production path that reaches scan_region/verdict() taint rather
than the batch route, to state whether any shipped push/replay flow silently loses taint flows today, or
whether the single-query path is only hit by pair measurements and ad-hoc harnesses.
Evidence
benchmarks/measurements/2026-10-12-banks-fixed-search-mvp/— the batch path works (11 flow rows,write_textsinks); the v1 single-query attempt returned 0/0 oncpg query taint failed.- Raw joern error captured by
banks_debug.py(scratchpad):Missing argument: --cpgFile; Unknown arguments: --fixedOrigin --guards --quotedSanitizers --originAnchors --fieldParameterSources.
Suggested fix (not yet applied)
Pick one, lowest-risk first:
- Make
backend.query()filter params to the script's declared signature before building--param/ named-arg lists (parse the@main def exec(arg names once, intersect). Fixes every current and future signature skew for all single-query scripts, symmetrically with how the batch path already tolerates extras. Preferred. - Add the five params to
taint.sc'sexecsignature as ignored passthroughs (mirrors batch semantics but duplicates the JSON parsing already in the script; narrower). - Route
_usable_flowsthroughrun_batchwith a one-element request (makes single-query taint share the tolerant path; changes the call shape upstream).
Add a regression test under the joern-gated suite: cpg.run("taint", request_params(path_spec, …)) must
return a parsed payload (not None) on a fixture CPG, asserting the instrument actually executed.