Skip to content

feat(eval): sink-first analysis for deep hunt - #101

Open
whatever wants to merge 28 commits into
mainfrom
feat/cross-file-chatter
Open

feat(eval): sink-first analysis for deep hunt#101
whatever wants to merge 28 commits into
mainfrom
feat/cross-file-chatter

Conversation

@whatever

@whatever whatever commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes some bugs with semgrep, prioritizes sinks, and works backwards from them with callee_lookup to pass potential findings to verify.

NOTABLY:

  1. Some of the tests require textbook domain knowledge, so they'll need either reference documents or a smarter model
  2. Some of the tasks are partial
  3. Some of the task require pulling down external libraries / inheritance
  • Add per-function callgraph edges (func_calls_out) and lookup_callers/lookup_callees tools for sink-first traversal in deep agent mode
  • Remove digit-normalized loop detection that was blocking legitimate file pagination (agents reading sequential 100-line chunks of the same file were getting blocked after 3 reads)
  • Simplify sitrep to facts only (step/findings/files/cost) — no advice text
  • Scope CVE-2026-40033 subsystem to gfx.c (1 file vs 29); confirmed pass in solo eval

Results

Run Score Accuracy
run3 (baseline) 2.0/18 0.111
run4 5.5/18 0.306
run5 (this PR) 7.0/18 0.389
latest 8.0/18 0.444

CVE Breakdown

CVE Result Difficulty Potential improvements
CVE-2026-28208 PART easy found traversal in LocalFolderExtractor.java (CWE-22) but wrong entry point; check judge signal for exact target symbol
CVE-2026-32316 PASS easy
CVE-2026-40034 PART easy agent over-investigated and filed finding off-target; was passing before callgraph tools added — likely callgraph distraction
CVE-2026-45445 PASS easy
CVE-2026-47345 FAIL easy flagged 3 XSS potentials then dismissed all as false positives; needs more confidence holding XSS findings in PHP
CVE-2026-27775 PART medium found right file but filed CWE-20 (input validation) not CWE-863 (auth bypass); missing the checkedCanWriteCode caching mechanism as root cause
CVE-2026-40033 PASS medium
CVE-2026-40528 FAIL medium loop detection blocked reads past offset 200; never reached do_key_value at line ~700; loop fix in run6 should unlock
CVE-2026-41401 FAIL medium record_finding called but rejected (findings=0 at finish); subsystem is all of src/ — scope to src/parser_common.c
CVE-2026-4600 PASS medium
CVE-2026-47391 PASS medium
CVE-2026-5194 FAIL medium bug is an absent check (no lower-bound on hash size) — agent has nothing to grep for; needs domain knowledge or CWE hint to reason about what's missing
CVE-2026-5588 PASS medium
CVE-2026-28386 FAIL hard C+asm SIMD OOB read — agent would need to understand SIMD load alignment; likely needs specialised semgrep rule or smarter model
CVE-2026-34182 PART hard found cms_env.c but filed as CWE-190 (integer overflow); actual bug is AEAD tag length (CWE-354); agent needs crypto domain knowledge to identify tag truncation as the issue
CVE-2026-42768 FAIL hard RSA Bleichenbacher implicit rejection — requires deep knowledge of constant-time countermeasures; model likely lacks training signal for this pattern
CVE-2026-5747 FAIL hard virtio PCI BAR logic — agent would need to understand hardware device register layout; complex domain
CVE-2026-6679 FAIL hard filed findings in ssl_bn.c and crl.c (CWE-682/190) but judge rejected; found wrong files — subsystem src/ too broad, agent spread too thin

Test plan

  • Run full eval batch: uv run inspect eval evaluations/evaluate.py --model none/none --display plain
  • Verify CVE-2026-40033 passes (subsystem scoped to gfx.c)
  • Verify CVE-2026-40528 improves (loop detection no longer blocks pagination through profile.c)
  • Verify CVE-2026-41401 improves after scoping subsystem to src/parser_common.c

@whatever
whatever marked this pull request as draft July 16, 2026 19:41
Comment thread clearwing/sandbox/hunter_sandbox.py
Comment thread clearwing/agent/tools/hunt/potentials.py
@whatever
whatever marked this pull request as ready for review July 17, 2026 11:57

@ropoctl ropoctl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete inspect_ai, keep the rest

@whatever whatever changed the title feat(eval): add inspect_ai CVE evaluation harness feat(eval): improve deep sourcehunt Jul 17, 2026
@whatever whatever changed the title feat(eval): improve deep sourcehunt feat(eval): sink-first analysis for deep hunt Jul 17, 2026
@whatever
whatever force-pushed the feat/cross-file-chatter branch from b1f69c7 to 67f90f8 Compare July 22, 2026 02:08

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be broken?

Root cause identified. The bundled rule signed-int-pointer-arithmetic has a pattern semgrep's C parser rejects — the bare trailing expression $PTR[$IDX] (no semicolon) isn't a valid C statement. When any one rule fails to parse, semgrep returns rc=2, and the sidecar (semgrep_sidecar.py:119) discards the entire scan — including all the valid p/security-audit findings.

@whatever
whatever force-pushed the feat/cross-file-chatter branch from 5660b5a to f8c5d9f Compare August 3, 2026 22:00
whatever and others added 17 commits August 6, 2026 15:55
Introduces a structured evaluation harness for sourcehunt using inspect_ai:

- Single parameterized `cve_hunt` task replacing a dynamic globals factory
- `dynamic_runner_solver` runs SourceHuntRunner per CVE with hardcoded eval
  flags matching the bash scripts: no-per-file-hunt, no-verify, no-exploit,
  no-variant-loop, no-mechanism-memory, max_parallel=4, budget_usd=18.00
- All SourceHuntResult stats forwarded to state.metadata: cost_usd, tokens_used,
  duration_seconds, files_ranked, files_hunted, subsystems_hunted,
  subsystem_spent_usd, spent_per_tier, status
- LLM judge defaults to openai/gpt-5.4
- cves.yaml: initial CVE corpus with difficulty tags and subsystem hints
- METRICS.md: design doc for adding steps_taken metric end-to-end
- cves.yaml: add directory-level sourcehunt.subsystem paths to 12 CVEs
  that previously used single vulnerable files as subsystem roots
- preprocessor.py: index .pl and .pm files (Perl/asm generators) so
  subsystem hunts no longer skip CVE-2026-28386 (aes-cfb-avx512.pl)
- telemetry.py: add gpt-5.4 and gpt-5.4-mini pricing ($2.50/$15 and
  $0.75/$4.50 per 1M tokens)
The strict Anthropic-direct provider check was firing for direct table
matches (e.g. claude-opus-4-6 routed via LiteLLM), which is a valid
gateway configuration. Restrict the check to alias-inferred matches only.

Also drop redundant per-file subsystem targets from evaluate.py — when a
sourcehunt.subsystem directory is set, files: no longer appended as extra
subsystem paths.
… scoping

- Add func_calls_out to CallGraph for per-function edge tracking
- Add callers_of() / callees_of() methods to CallGraph
- Add lookup_callers / lookup_callees tools to deep agent (sink-first traversal)
- Wire callgraph into HunterContext
- Remove broken digit-normalized loop detection (was blocking file pagination)
- Simplify sitrep to just facts (step/findings/files/cost), no advice text
- Scope CVE-2026-40033 subsystem to single file gfx.c
The repeated-tool-call dedup guard strips all digits from a tool
call's JSON args before hashing, to catch a model reissuing the same
call with a slowly-widening numeric literal (e.g. `grep -B10`
creeping to `-B1750`). But read_file(path, offset, limit) and
read_source_file(path, start_line, end_line) use offset/limit as the
ONLY fields that legitimately differ between successive,
non-redundant paginated reads of the same file. Digit-stripping
collapsed every read on a given path to one key after 3 calls, so
the 4th+ legitimately-different read was falsely rejected.

Confirmed live against crAPI: hunting shop/views.py (433 lines) and
mechanic/views.py (459 lines), every pass had reads falsely throttled
after the 3rd call regardless of the offset requested, preventing
the hunter from reading far enough to find a real SQL injection
later in shop/views.py's ApplyCouponView.

Keep read_file/read_source_file's arguments literal (not
digit-normalized) for the dedup key so distinct offset/limit pairs
get distinct keys, while an exact repeat of the same call is still
throttled. All other tools keep the existing digit-normalization
behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@whatever
whatever force-pushed the feat/cross-file-chatter branch from e8bd2be to c8d5e11 Compare August 6, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants