Commit Graph
11 Commits
Author SHA1 Message Date
Marius CiepluchandClaude Opus 4.8 90e0098a94 spec(g2): root-cause engine 0/5 (library params not sources) + the '#2 guided by #1' workstream
Verified root cause: the pilot repos are libraries whose untrusted input is public-API
parameters, not HTTP requests. Python taint sources (ruleset/semantic/python.toml) are web/CLI
only; the parameter-as-source mode (_parameters_are_input) fires only for recognised entry
points, and the pilot vuln regions are entry:false -- so their params are deliberately not
sources. Widening that statically would reintroduce the WordPress/PMPro false-SQL-injection
flood the docstring documents. So the static engine cannot safely source library APIs on its
own -- which is exactly the seam for the maintainer's '2 guided by 1' directive: the LLM
adjudicates which library parameter is a trust boundary, the taint engine traces that
source->sink as proof (not LLM say-so), and repeated source shapes graduate to guarded static
facts. First increment (pending budget): LLM-guided-source mode on the 5 pilot regions,
measuring source naming, traced finding, catch lift vs regex 1/5, and false-alarm on ordinary
changes; staged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 09:24:06 +02:00
Marius CiepluchandClaude Opus 4.8 ae22136c08 measure(g2): CORRECT pilot diagnosis -- engine produced zero vuln-family findings; anchoring is a red herring
Inspecting head.findings (not ranker evidence) overturns the previous commit's read:
the dataflow/taint tier produced ZERO findings in the vuln families (path/injection/deser)
on all 5 repos -- only config_secrets (banks 1, socketio 2; launch-editor/graphiti/bedrock 0).
The tier1/score2.0 'sink' I cited was the RANKER's static candidate, not a confirmed taint
finding. taint_evidence_not_applicable = no source->sink flow was traced (banks: sink found,
no source). So ambiguous_line_correspondence is downstream and moot -- there are no
vuln-family findings to anchor, and the region-level witness fix (Option A) is refuted before
implementation. The LLM adjudication tier was disabled (model_calls=0).

Reprioritised: #1 LLM-tier pilot L1/L2 (now primary, staged, costs money), #2 taint
source/sink coverage + region ranking, #3 change-anchoring (only after tiers yield findings).
Instrument rule: caught the ranker-vs-finding misread before building the wrong fix.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 09:19:04 +02:00
Marius CiepluchandClaude Opus 4.8 a5a37374a7 measure(g2): engine-on pilot -- regex 1/5, engine 0/5 on clean host; budget hypothesis refuted
Five public pool repos, vuln change each, clean host, 300s. The shipped engine added NO
catches over the regex floor. 3/5 repos finished their region queue (deferred=0) and still
emitted no finding, so change-scoping is not the path to catch. The engine ranked the vuln
region in all 5 and found the right sink on 2 (score 2.0), but withholds any finding it
cannot anchor 1:1 to changed lines -- all 5 hit ambiguous_line_correspondence ->
witness_unresolved -> coverage incomplete, suppressing even the scored sinks. Confirmed not a
shallow-clone artifact (correspondence is computed from both blobs).

Reprioritised engine levers: #1 change-anchoring robustness (region-level witness fallback),
#2 family ranking/taint coverage, #3 the LLM adjudication tier (the pilot is the case for it).
Release read: the pre-push net is NOT releasable on detection yet.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 08:53:38 +02:00
Marius CiepluchandClaude Opus 4.8 4c4b7d375a measure(g2): E1/E2 clean-host results -- the engine 'query=0 wall' was contention, not intrinsic
On a clean host the shipped engine runs dataflow (query 0->35s, selected 0->356 regions).
The evidence/taint batch (~225s) is invariant to warm session and graph preload; importCpg
is cheap (~35s). The real barrier is taint.sc's ~160s per-batch flow work, so build+evidence
~255s of a 300s budget defers 1009/1365 regions. Lever = change-scope the family/file set the
taint batch walks, not session/preload. Corrects the prior ~200s graph-load wall diagnosis.

Next decisive experiment: engine-on unseen pilot vs record-07 regex floor (catch 3/92).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 08:20:16 +02:00
Marius CiepluchandClaude Opus 4.8 e716e3beab docs(g2): record-07 baseline is regex-only; prioritize semantic detection + barrier analysis
Verified on the records: the push replay arms hardcode model="disabled",
max_model_calls=0, cost_usd=0 (NO LLM) and engine_covered=0 (engine never ran,
~200s wall). So the measured catch~2.5% is the quick pattern-rule tier ALONE --
grep-with-structure. Pattern rules alone will never be enough.

Reprioritize G2 after record 07 to an efficiency barrier analysis over the two
tiers that carry detection and both sat out:
- engine (Joern dataflow): E1 split the ~150s evidence-fixed (JVM/graph-load/
  census); E2 does a warm session + graph preload at build kill it (decisive);
  E3 analysis per-pair; E4 build vs repo size.
- LLM adjudication: L1 model latency + calls per push; L2 recall lift over
  regex+dataflow on the pool.
Deliverable: a barrier table (budget x tier combo -> fits? measured recall/FP)
for a realistic pre-push window (<=120s target, 300s hard) -> minimal releasable
config. Change-scoping branch is a late add-on (trims only the per-pair tail),
not the lever. Also fixed a stray </content> tag in the brief.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 15:05:51 +02:00
Marius CiepluchandClaude Opus 4.8 df09fe4030 docs(g2): engine-arm wall diagnosed -- ~200s fixed overhead, change-scoping insufficient
Records the full G2 engine-detection finding on main (code attempt parked on
branch engine-change-scoped-evidence-pass, unmerged). The engine arm has ~200s
intrinsic fixed overhead -- ~48s CPG build + ~150s first-query graph-load+census
(measured uncontended: build 48s, evidence 243.79s over 1015 pairs, query 0) --
so a 300s budget cannot reach the flow queries. Pair-count change-scoping trims
only the ~86s per-pair tail; the warm-session env times out at startup contended
and spares only subsequent batches. Real fix is clean-host engine work (reliable
session startup + graph preload, or census-cost reduction). Parked to finish G0.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 13:31:55 +02:00
Marius CiepluchandClaude Opus 4.8 15992d3090 docs(g2): increment 2 measured -- the 236s is 99.9% the evidence-tier pass
Saved-artifact run: total=291s build=52s query=0s arbitrate=238.75s, and
query_seconds_by_kind[evidence]=238.54s. The evidence-tier pass is the whole
middle; affected_context + _scope_work are 0.21s. It tiered 1015 pairs at
~235ms/pair (contended) and exhausted the budget -> selected=0, deferred=1365
all deadline_exhausted. Secondary anomaly: ~50-100x the design's assumed
2.5ms/pair -- the evidence query is pathologically slow on a cold large CPG.

Increment 2's single target is the evidence pass (scan.py:338-359). Fork
recorded: (i) scope+bound the pass change-first, (ii) two-phase neighbourhood
pass, (iii) per-pair speedup. Must preserve reach-into-unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 12:11:57 +02:00
Marius CiepluchandClaude Opus 4.8 a0637f2f9d docs(g2): correct increment 2 -- preserve reach-into-unchanged, measure the 236s split first
A first scoping attempt (restrict analyzed regions to changed files) was reverted:
it breaks a tested design invariant. The engine deliberately selects an unchanged
region's question when a changed region reaches it
(test_changed_dependency_reaches_unchanged_ranked_question;
test_change_does_not_replace_ranker_selection). _changed_files feeds _evidence_pass
only as contextFilter/contextPaths (itemise vs summarise), not to shrink the tier
population. Any scope must keep changed regions PLUS the regions they reach.

Also: increment 1 over-claimed. Increment 0 measured build=67s/query=0s but NOT
the split of the ~236s middle (evidence-tier pass vs affected_context/context
resolution vs _scope_work enumeration). Increment 2 must capture per_kind /
other_seconds / affected_context timing and cut the dominant phase while
preserving reach-into-unchanged -- instrument before fixing.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 11:50:41 +02:00
Marius CiepluchandClaude Opus 4.8 9580ad1e30 docs(g2): increment 1 located -- regions are discovered whole-repo, never change-scoped
The 4095 candidates are not an in-scan artifact: push/runner.py:345 discovers
head_regions across the whole head tree, then runner.py:397 passes them wholesale
to scan_repository with change_context threaded in but unused for scoping. The
scan enumerates all regions x families (scan.py:167/364) and, with
order_by_evidence=True, evidence-tiers every pair before analysis -> the ~236s
from increment 0 -> deadline at query_seconds=0.

Fix locus (increment 2): scope the analyzed region set to context.changed_paths +
dataflow neighbourhood (regions.py:279 primitive exists; compare_targeted_base is
precedent). Keep the CPG whole-repo so taint can trace into the change from
unchanged code; only the question set is change-scoped.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 11:24:43 +02:00
Marius CiepluchandClaude Opus 4.8 7a1e47e0cc docs(g2): increment 0 confirmed -- CPG build cheap, candidate enumeration is the overspend
Timed live bedrock replay: build_seconds=66.69, query_seconds=0.0, total 303s,
4095 candidate questions all analysis_not_started=deadline_exhausted, selected=[],
regions_scanned=0. ~67s builds the CPG; ~236s enumerates/ranks/prepares 4095
repo-wide candidates for a 3-file change, then the deadline hits before any query
runs. The CPG build is not the bottleneck; the over-broad candidate set is ->
change-scoping is the confirmed lever (and compounds with the correspondence fix,
since per-candidate prep includes correspondence work).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 11:21:33 +02:00
Marius CiepluchandClaude Opus 4.8 9f312177ce docs(g2): open the change-scoped engine-analysis workstream (brief + roadmap)
Track the engine-detection lever found while driving G0. Live diagnosis on the
frozen pool: the engine's "no finding" on unseen changes is usually
analysis-never-runs -- it ranks repo-wide candidates and defers them all
(deadline_exhausted) before analyzing the pushed change (bedrock injection,
303s, selected=[], ranked sites unrelated to the change). Lever: seed analysis
from the change's declarations + dataflow neighbourhood (rank.py/mapping.py) so
it runs within budget.

- .kiro/specs/engine-change-scoped-analysis/brief.md: problem (measured),
  hypothesis, anchors, and checkable increments (0 confirm build-vs-analysis
  cost split -> 1 locate selection/budget -> 2 seed from change -> 3 soundness
  + tests -> 4 live re-test -> 5 measure delta on the engine arm).
- roadmap.md G2: active workstreams (change-scoped analysis; the done-but-
  necessary-not-sufficient line-correspondence fix), for progress tracking.

Honest scope: G2, gated on the pool metric; not part of the G0 quick-rule
record-07. Coverage also needs the capability registry (Task 8.3); arm-scale
measurement needs the kube lane.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-11 11:10:26 +02:00