Commit Graph
8 Commits
Author SHA1 Message Date
Marius CiepluchandClaude Fable 5.1 bad8c7ada4 measure(independent): known-answer check of the verifier -- 11/15 sites, unstable verdicts, escalation costs more
Declared vulnerable sites: 11 of the 15 that were candidates are confirmed (8 at 3
steps, 3 more at 6). Missed: winml (a CORS config; the reachability question does
not fit), FUXA, Budibase, Froxlor.

Restart-based escalation is strictly worse than a straight 6-step budget: 88% of
pgAdmin candidates used every step, so they paid 3 + 6 ($0.0201 per candidate vs
$0.0139 baseline). Defaults are now 6 steps, no escalation. The cache order change
did not lower the per-call cost ($0.002): the conversation prefix was already
cached; 56% of prompt tokens are served from cache.

The larger finding: on 180 pgAdmin candidates verified twice, 15 and 13 were
flagged with only 2 in common. Verdicts on borderline candidates flip run to run,
while the declared sites were found consistently. Precision needs agreement
across runs or a second judgement, not a single pass.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 10:10:33 +02:00
Marius CiepluchandClaude Fable 5.1 6df74cc3ae fix(independent): verifier checkouts live under the run's own results directory
Two runs exporting the same pin into one path deleted each other's tree.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 09:56:46 +02:00
Marius CiepluchandClaude Fable 5.1 7be1f18778 chore(independent): verifier flags for a known-answer check into a separate results directory
--declared-sites-only, --candidates-from <jsonl> and --results-suffix, so the
efficiency change can be validated on the 180 already-verified pgAdmin candidates
and the 15 declared sites without touching the main results.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 09:52:50 +02:00
Marius CiepluchandClaude Fable 5.1 041d87199a perf(hunter): cacheable message order; adaptive step budget in the sink verifier
Token efficiency, measured on 180 verified pgAdmin candidates: stage 2 costs
$0.0139 per candidate, 90% of the spend, because the conversation is re-sent on
every turn and grows with each tool result.

- Context files now precede the question in the hunter's first message. DeepSeek
  bills a cached prefix at 1/30 of a fresh one, by prefix; with the question first,
  every candidate asked about the same file (about 5 per file) broke the prefix on
  its first line and paid for the file again. A test pins the order.
- The verifier runs each candidate at 3 steps and repeats it once at 6 only when
  the hunt spent every step on tool calls without a verdict. On the seven declared
  v2 sites, 3 steps found 5 and 8 steps found 6; most candidates conclude in 3.
- Per-candidate token usage (prompt, cached, completion, calls) and escalation
  are recorded, and each case reports its cache share, so the next efficiency
  decision is made on numbers.

Validation on the 180 pgAdmin candidates plus the 22 known sites (about $1)
waits for account credit: DeepSeek returns 402 at the moment.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 09:52:23 +02:00
Marius CiepluchandClaude Opus 5.5 66fa08cdd0 fix(independent): the sink verifier stops on an account error and retries unverified candidates
The rerun hit DeepSeek HTTP 402 ("Insufficient Balance") after 180 pgAdmin
candidates. Every later call failed at $0, and the runner wrote sixteen cases as
complete with nothing asked, because an unverified candidate counted toward
completion. That is a clean-looking zero from an instrument that could not call
the model.

Now:
- 401/402 or "Insufficient Balance" stops the run at once, exit 2, nothing
  recorded;
- only verified candidates count as done, so unverified ones are asked again on
  resume;
- a case is complete only when every candidate was verified.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 07:00:59 +02:00
Marius CiepluchandClaude Opus 5.5 8794884a64 fix(hunter): bound every tool result; show the provider's reason for an HTTP error
FUXA failed every hunt with HTTP 400, and neither run could say why. The client
discarded the response body. With the body shown, the reason was "This model's
maximum context length...": FUXA ships minified bundles whose single lines run to
megabytes, and grep_repo returned a matched line whole.

- grep_repo and find_refs quote a matched line (240 characters), never the whole
  line.
- Every tool result is capped at 16 x max_chars, with a truncation marker.
- OpenRouterChatClient errors carry the provider's response body (first 500
  characters). That would also have exposed the rejected model id in one call
  instead of eighteen days.

After the fix, FUXA candidates verify normally at $0.004-0.013 each. A test covers
the megabyte-line case.

verify_sinks.py: skip futures cancelled at the spend ceiling instead of reading
them (the run crashed with CancelledError).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-28 20:15:14 +02:00
Marius CiepluchandClaude Opus 5.5 267bca5232 perf(hunter): read a repository's source once per state, not once per tool call
Each grep_repo and find_refs call re-enumerated the tree (walk, ignore rules,
vendored checks) and re-read every file. The v2 sink verifier spent almost three
hours on its first case (pgAdmin, about 1,800 files) at 108% CPU with one open
connection. It was CPU-bound in these tools, not waiting on the model.

Source lines are now memoised per root, keyed by path and modification time. A hit
is re-validated file by file, because an edit inside a subdirectory does not touch
the root's mtime; a test covers one enumeration and a detected edit.

verify_sinks.py now runs candidates in worker processes, not threads, and appends
each candidate's result to a per-case log, so a stopped run resumes where it left
off. GoPay: 12 candidates in 14 s, the real site found.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-28 18:30:09 +02:00
Marius CiepluchandClaude Opus 5.5 023c24535f measure(independent): stage-2 verifier for model-classified sink candidates
Per candidate function, one bounded tool-hunter pass (custom prompt, the file as
context, 3 steps) asks whether attacker-controlled data reaches the operation
without a sufficient guard. It reports only with a stated source-to-sink path.
Hard spend ceiling, parallel workers; one run per pin; the fixed pin is rechecked
only for detected sites. Exploratory on v2.

Pilot: on GoPay, the one real site was reported in 3 of 4 runs and nothing else
out of 12 candidates.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-28 13:24:22 +02:00