mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
measure(g2): banks fixed-search MVP -- "no wrong targets, only wrong searching" demonstrated
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]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7ab5c2ab2d
commit
8f7fc4ae83
@@ -462,3 +462,30 @@ suspicion. So:
|
||||
- **Bet #3 (the proof arm) is the ASYNC BLOCK lane**: run `search/verify` on the plane/CI for the suspicions worth
|
||||
proving, with authored (eventually DSPy-authored) demos. Worth an empirical run on one tractable target
|
||||
(mcp-remote) to confirm the arm demonstrates on a real unseen vuln — a bounded, separate effort.
|
||||
|
||||
## BANKS FIXED-SEARCH MVP (2026-10-12) — "no wrong targets, only wrong searching" demonstrated
|
||||
|
||||
Record: `benchmarks/measurements/2026-10-12-banks-fixed-search-mvp/`. Target: banks (the "library" that gave
|
||||
the engine ZERO path findings in the pilot). Same target, same CPG — only the search changes.
|
||||
|
||||
- Injected the two annotations a per-target LLM steer would supply: **parameter-as-source**
|
||||
(`parameter_sources=True`, so `prompt.name` counts as untrusted) + the **missing file-write sink**
|
||||
(`write_text`/`read_text` added to path sinks).
|
||||
- **Baseline (shipped search): 0 resolved `prompt → write_text` flows** — its 5 flow rows are all census/
|
||||
evidence (`sink=None`), matching the pilot's zero path findings.
|
||||
- **Fixed search: the engine resolves `prompt → path-join → prompt_file.write_text(prompt.raw)` in
|
||||
`from_prompt_path`** (`source=prompt`), plus a second `write_text` sink in `_scan`. These appear ONLY with
|
||||
the annotations.
|
||||
- **Thesis demonstrated on a concrete case:** banks was not a wrong target; the search was unannotated. The
|
||||
fix needed exactly **two** inputs — the param-source and the file-write sink — which are precisely what the
|
||||
per-target LLM steer (DSPy-optimised, framework-seeded) has to learn to supply. This is the first measured
|
||||
instance of the "#2 guided by #1" loop closing on an unseen target.
|
||||
- Caveats: (1) one row shows `source=path` not `prompt` — the param-source fires broadly, a precision knob for
|
||||
the steering loop; (2) this is a TRACE, not the runtime differential proof (proof stays the async BLOCK lane).
|
||||
|
||||
**Instrument note / filed bug:** the first MVP used the single-query `cpg.run("taint", …)` path and returned
|
||||
0/0 — an INSTRUMENT FAILURE, not a result (`cpg query taint failed` on every query). Root cause filed in
|
||||
`bugs/taint-sc-single-query-signature-skew.md`: `request_params()` emits 5 keys (`guards`, `fixedOrigin`,
|
||||
`originAnchors`, `quotedSanitizers`, `fieldParameterSources`) that `taint.sc`'s `exec` signature does not
|
||||
declare, so both single-query sub-routes (CLI `--param` and session named-arg) reject them. The batch path
|
||||
(`run_batch` → `requestsFile` JSON) tolerates them, which is why the pilot scans ran and this MVP used it.
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
# 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 errors
|
||||
`Unknown arguments: --fixedOrigin --guards --quotedSanitizers --originAnchors --fieldParameterSources`
|
||||
(and, as a knock-on, `Missing argument: --cpgFile`). Observed verbatim via `banks_debug.py` on 2026-10-12.
|
||||
- **Session route** (lines 1321–1326): defines `def ousast_taint(...)` by renaming `@main def exec(` and then
|
||||
calls `ousast_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 exact
|
||||
`exec` parameter 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_region` dispatches taint families to `taint_verdict` (`pipeline.py:121`) → so **per-region
|
||||
single-query taint via `scan_region` is dead** whenever the disposable or session route is taken.
|
||||
|
||||
Unaffected:
|
||||
|
||||
- The push/scan **batch** path: `scan.py` uses `run_batch("taint", …)` (scan.py:1259, 1264, 1387). This is
|
||||
why the engine-on pilot produced results.
|
||||
- `config` and `dominance` single-queries (`config_value.py:46`, `dominance.py:54`) — their `.sc` signatures
|
||||
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_text` sinks); the v1 single-query attempt returned 0/0 on `cpg 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:
|
||||
|
||||
1. **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.
|
||||
2. Add the five params to `taint.sc`'s `exec` signature as ignored passthroughs (mirrors batch semantics but
|
||||
duplicates the JSON parsing already in the script; narrower).
|
||||
3. Route `_usable_flows` through `run_batch` with 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.
|
||||
@@ -132,6 +132,14 @@ against a realistic pre-push window (≤120 s target, 300 s hard):
|
||||
5 pilot regions in LLM-guided-source mode; measure source naming, traced finding, catch lift vs regex 1/5, and
|
||||
false-alarm on ordinary changes; stage 5–10 → ~10–20 → full on lift. #3 change-anchoring deprioritised until
|
||||
tiers yield findings.
|
||||
- **BANKS FIXED-SEARCH MVP DONE (2026-10-12) — "#2 guided by #1" closes on an unseen target.** See brief
|
||||
"BANKS FIXED-SEARCH MVP" + `benchmarks/measurements/2026-10-12-banks-fixed-search-mvp/`. On banks (the
|
||||
library that gave the engine zero), injecting the two annotations an LLM steer would supply (param-as-source
|
||||
+ `write_text`/`read_text` sink) makes the engine resolve `prompt → path-join → write_text` where the
|
||||
shipped search resolved nothing. Proves the governing principle "no wrong targets, only wrong searching" and
|
||||
names the exact inputs the DSPy-optimised steer must learn. Filed instrument bug:
|
||||
`.kiro/specs/engine-change-scoped-analysis/bugs/taint-sc-single-query-signature-skew.md` (single-query taint
|
||||
path dead on an exec-signature skew; batch path unaffected).
|
||||
- LLM: L1 model latency and calls per push; L2 the recall LIFT of the LLM tier over regex+dataflow on the pool.
|
||||
- Deliverable: a barrier table (budget × tier combo → fits? measured recall/FP) → the minimal releasable config.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user