This file tracks all planned work for integrating published vulnerability-detection
research into the baco scanner. Each task is grep-able by its ID (e.g. P3.2)
and lists exact file paths, dependencies, and acceptance criteria.
Project rule reference: #6381 (tiered structure), #6452 (progress banner + [x]),
#6453 (exactly five Cross-Cutting tasks), #6422 (checkpoint transition tests
must be updated when adding phases), #5811 (coverage ≥ 80%), #5812 (zero
clippy warnings before merge).
| Tier | Status |
|---|---|
| T1 (T1.1-T1.4) | COMPLETE [x] |
| T2 (T2.1-T2.5) | COMPLETE [x] |
| T3 (T3.1-T3.3) | PARTIAL |
| X (X.1-X.5) | COMPLETE [x] |
| P1 (P1.1-P1.5) | COMPLETE [x] |
| P2 (P2.1-P2.5) | COMPLETE [x] |
| P3 (P3.1-P3.5) | COMPLETE [x] |
| P4 (P4.1-P4.5) | COMPLETE [x] |
| P5 (P5.1-P5.5) | COMPLETE [x] |
- Status: blocked
- Blocker: Joern binary not installed in environment (constraint
#6515). - Resume condition: Joern installed;
CpgSlicephase (src/cpg/slicer.rs) can produce real CPG output instead of falling back to tree-sitter-only slicing. - Tests: any code path depending on Joern must stay
#[ignore]until then (rule#6514).
- Status: blocked
- Blocker: TGI server not installed (constraint
#6516). - Resume condition: TGI endpoint available;
LlmConfiginsrc/config.rsextended withreasoning_endpointfield.
Five papers approved by the user on 2026-08-11. Each maps to one or more
existing scanner phases. All configs default to enabled = false so existing
behaviour is unchanged until the operator opts in.
References:
- P1: VulTriage — arXiv:2605.09461 — https://github.com/vinsontang1/VulTriage
- P2: VulnLLM-R — arXiv:2512.07533 — https://github.com/ucsb-mlsec/VulnLLM-R
- P3: MoCQ — arXiv:2504.16057 (Neuro-symbolic Static Analysis)
- P4: PacVD — arXiv:2504.16877 (Context-Enhanced Vuln Detection)
- P5: AgentFlow — arXiv:2604.20801
Paper claim. Augment the LLM input with three complementary context paths before the final judgement. SOTA on PrimeVul pair test set; generalises to Kotlin under low-resource and class-imbalanced settings.
Three paths (verbatim from paper).
- Control Path — extract and verbalise AST + CFG + DFG information to expose control and data dependencies for the target function.
- Knowledge Path — retrieve CWE-derived vulnerability patterns and examples via hybrid dense–sparse retrieval.
- Semantic Path — produce a functional-behaviour summary of the code before the final vulnerability judgement.
The three contexts are concatenated into one unified instruction passed to the LLM. Ablation study confirms each path contributes; removing any path degrades precision or recall.
Integration target. src/scanner/phases/llm_phases.rs::run_llm_static_analysis
(currently passes raw source + CWE prompt). The triple-path context must be
assembled before the LLM call and appended to the existing prompt.
Sub-tasks.
- Files to modify:
src/scanner/phases/llm_phases.rs— addbuild_control_path(fn_body, cpg)call site before LLM dispatchsrc/llm_analysis.rs(or newsrc/context/control_path.rs) — implement extractor
- Inputs. Function body source + existing CPG slice from
CpgSlicephase (when Joern available) or tree-sitter AST fallback. - Outputs. Verbalised string of the form:
Control dependencies: <list> Data dependencies: <list> AST summary: <compressed AST> - Reuse. Tree-sitter parsers already in repo; CPG slice from
src/cpg/slicer.rs. - Acceptance criteria. Given a sample function, returns a non-empty
structured string; unit test asserts CFG nodes appear for an
ifstatement. - Risk: low.
- Files to modify:
src/scanner/phases/llm_phases.rs— callretrieve_cwe_patterns(cwe_id, fn_signature)before LLM call- new
src/context/knowledge_path.rs— hybrid dense-sparse retriever src/cwe/(existing CWE routing module) — expose pattern corpus
- Inputs. CWE id (from
CweRoutingphase) + function signature. - Outputs. Top-k CWE-derived vulnerability patterns and examples.
- External dep. An embedding model. Reuse existing
LlmConfigembedding endpoint if present; otherwise add aKnowledgePathConfig { enabled, embedding_endpoint, top_k }. - Acceptance criteria. For CWE-78 (OS Command Injection), returns ≥ 1 example pattern; dense + sparse scores are combined.
- Risk: medium — needs embedding index; can be stubbed with sparse-only retrieval (BM25) as MVP.
- Files to modify:
src/scanner/phases/llm_phases.rs— callsummarise_function(fn_body)before final judgement- new
src/context/semantic_path.rs— LLM summariser
- Inputs. Function body.
- Outputs. 1-3 sentence functional summary.
- Reuse. Same
LlmClientused byLlmStaticAnalysis(smaller model OK). - Acceptance criteria. Output is ≤ 60 tokens; unit test with a sample function returns a non-empty English summary.
- Risk: low — extra LLM call per function; gate behind
enabledflag.
- Files to modify:
src/config.rs— addVultriageConfig { enabled: bool, control_path: bool, knowledge_path: bool, semantic_path: bool }afterValidateConfig. Default allfalse.config/*.tomlexample files — add[vultriage]sectiondocs/configuration.md— document the three flags
- Acceptance criteria.
cargo checkclean; existing tests unchanged (flag default off). - Risk: low.
- Files to modify:
src/scanner/phases/llm_phases.rs::run_llm_static_analysis— whenconfig.vultriage.enabled, assemble prompt as[Control Path]\n[Knowledge Path]\n[Semantic Path]\n[Original code + CWE prompt]
- Acceptance criteria. With
vultriage.enabled = true, the LLM prompt includes the three labelled sections; withfalse, prompt is unchanged. - Depends on: P1.1, P1.2, P1.3, P1.4.
Paper claim. First specialised reasoning LLM for vulnerability detection. 7B model distilled from DeepSeek-R1 + QwQ-32B outperforms commercial reasoning LLMs and CodeQL/AFL++. 15 zero-days in real projects.
Two reusable components.
A. Reasoning inference adapter. Even without their fine-tuned weights, the inference-time techniques are portable:
- Truncated generation — stop reasoning at a length cap, force final answer.
- Policy-based generation — query model 4× to get a CWE candidate set ("policy"), then re-query with the policy as additional context to pick one.
- Summary-based reasoning — query a summariser to compress the reasoning chain before the final answer.
B. Agent scaffold. For each target function:
- Extract all functions along three randomly sampled paths from project entry point to the target in the call graph.
- Provide these as initial context to the model.
- Equip the model with a tool that retrieves function implementations by name.
- Limit the number of interaction rounds to control inference cost.
Integration target.
- Reasoning adapter →
src/scanner/phases/llm_phases.rs::run_llm_static_analysisandrun_llm_verification(any LLM call site). - Agent scaffold → new option in
LlmStaticAnalysisconfig, or extendSecurityAgentVerificationphase insrc/scanner/phases/other_phases.rs.
Sub-tasks.
- Files to modify:
src/llm.rs— addmax_reasoning_tokens: Option<u32>toLlmConfigand to the request buildersrc/config.rs— expose field inLlmConfig
- Acceptance criteria. When set, the LLM request includes the cap; when
None, behaviour unchanged. - Risk: low.
- Files to modify:
src/scanner/phases/llm_phases.rs— whenconfig.policy_sampling.enabled, call the LLM 4 times, parse CWE candidates, then a 5th call with the policy in the promptsrc/config.rs— addPolicySamplingConfig { enabled: bool, samples: u8 }underLlmPhasesConfig
- Acceptance criteria. With flag on, produces a final CWE label from the policy set; with flag off, single call as today.
- Risk: medium — 5× LLM calls; must be opt-in.
- Files to modify:
- new
src/agent_scaffold/call_graph_paths.rs— given a target function, sample 3 random paths from project entry points to it - reuse call graph built by
Indexingphase (src/scanner/phases/other_phases.rs::run_indexing)
- new
- Inputs. Call graph + target function id.
- Outputs.
Vec<Vec<FunctionId>>of length 3. - Acceptance criteria. For a call graph with ≥ 3 paths, returns 3 distinct paths; for fewer, returns what is available without panicking.
- Risk: medium — depends on call graph quality from
Indexing.
- Files to modify:
- new
src/agent_scaffold/fn_lookup.rs— index all functions by name; exposelookup(name) -> Option<String>returning the function body src/llm.rs— add a tool-calling interface (or use the existing one if present)
- new
- Acceptance criteria. Agent can request a function by name and receive its
body; missing name returns
None. - Risk: low.
- Files to modify:
src/scanner/phases/other_phases.rs::run_security_agent_verification(line 555-592 range per outline)src/config.rs— addAgentScaffoldConfig { enabled, max_rounds: u8, paths_per_target: u8 }
- Acceptance criteria. With
agent_scaffold.enabled = true, the phase builds the 3-path context + lookup tool per target function; withfalse, existing behaviour unchanged. - Depends on: P2.3, P2.4.
- Risk: medium.
Paper claim. LLM generates vulnerability-detection patterns in a DSL; iterative refinement loop with trace-driven symbolic validation gives precise feedback. Comparable to expert patterns; 46 new patterns + 25 zero-days. Hours vs weeks of manual effort.
Core algorithm.
- Extract the DSL for expressing vulnerability patterns (paper: 12 vuln types across C/C++, Java, PHP, JS).
- LLM proposes a candidate pattern in the DSL given a CWE description.
- Symbolic validator runs the pattern against a trace corpus; produces a structured feedback signal (which traces matched, which missed).
- LLM rewrites the pattern using the feedback. Loop until validator accepts or budget exhausted.
- Accepted patterns are emitted as Semgrep rules (baco's existing rule format).
Integration target. src/scanner/phases/other_phases.rs::run_rule_synthesis
(currently a thin wrapper at lines 940-1031). The current RuleSynthesis phase
is the natural home — MoCQ is its upgrade.
Sub-tasks.
- Files to modify:
- new
src/rulesynth/dsl.rs— define the pattern DSL as Rust types (sink, source, sanitizer, path constraints, metavariables) - serialise to/from Semgrep YAML for emission
- new
- Acceptance criteria. A round-trip
pattern → semgrep_yaml → patternis lossless for the 12 supported vuln types. - Risk: medium.
- Files to modify:
- new
src/rulesynth/validator.rs— given a candidate pattern, run it against a labelled trace corpus and return{matched, missed, false_positives} - trace corpus: reuse
tests/fixtures/labelled samples
- new
- Acceptance criteria. For a known-correct pattern for CWE-78,
matched ≥ 1andfalse_positives == 0on the corpus. - Risk: medium — needs a labelled corpus; start small (CWE-78, CWE-89).
- Files to modify:
src/scanner/phases/other_phases.rs::run_rule_synthesis— replace the current body with the propose→validate→rewrite loop- reuse
LlmClientfromcreate_llm_client_with_metrics
- Acceptance criteria. Within a budget of N iterations, the loop either emits an accepted Semgrep rule or reports failure; never loops indefinitely.
- Depends on: P3.1, P3.2.
- Risk: medium.
- Files to modify:
src/rulesynth/emitter.rs— write accepted patterns tooutput/synthesised_rules/<cwe>_<timestamp>.ymlSemgrepphase (run_semgrep) — load synthesised rules alongside the bundled ones when present
- Acceptance criteria. A file is written on acceptance; the next
Semgreprun picks it up. - Risk: low.
- Files to modify:
src/config.rs— extendRulesynthConfigwithmocq_mode: bool,max_iterations: u8,corpus_path: PathBuftests/unit/rulesynth_tests.rs— add tests for the loop with a mock LLM returning a fixed candidate
- Acceptance criteria.
cargo testpasses; withmocq_mode = false, the oldRuleSynthesisbehaviour is preserved. - Risk: low.
Paper claim. Abstract callee functions via primitive APIs (malloc, free, open, close, …) at four granularity levels. Append abstraction to target function, feed to LLM. With CoT + DeepSeek-R1: +12.77% accuracy, +10.05% precision, +9.25% F1. Different models prefer different abstraction levels (GPT-4/DeepSeek = high-level; CodeLLaMA = detailed).
Core algorithm.
- Default analysis depth: 3 call layers (paper: 75% of inter-procedural vulns have call depth ≤ 3).
- Build CPGs of the target function and all callees within 3 layers.
- For each callee, extract four dimensions of primitive-API usage:
- Fuzzy Branches — API called in all / some / no branches.
- Concrete Branches — specific control conditions under which the API fires.
- Number of Calls — count per primitive API.
- Key Variables — identifiers operated on by the API.
- Four abstraction levels:
- Level 1: Fuzzy Branches only (highest abstraction)
- Level 2: Concrete Branches
- Level 3: Concrete Branches + Number of Calls
- Level 4: Concrete Branches + Key Variables
- Append the abstraction to the target function; feed to LLM.
Primitive API table (from paper).
| APIs | Targeted vuln type |
|---|---|
| open/socket/fopen/fdopen/opendir/close/fclose/closedir | Resource Leak |
| malloc/realloc/calloc/localtime | Null Pointer Dereference |
| malloc/free | Memory Leak, UAF, Double Free |
Integration target. src/scanner/phases/llm_phases.rs::run_llm_static_analysis.
The CPG slice from CpgSlice phase (when Joern available) or tree-sitter CFG
fallback provides the call graph. This is a strict superset of P1's Control Path
— P4 can be a more aggressive mode of the same prompt-augmentation hook.
Sub-tasks.
- Files to modify:
- new
src/context/primitive_api.rs— const table of primitive APIs grouped by targeted vuln type (from the paper table above) - extend to language-specific APIs (Python:
open,os.system; Java:FileInputStream, etc.)
- new
- Acceptance criteria.
lookup("free")returnsMemoryLeak | UAF | DoubleFree. - Risk: low.
- Files to modify:
- new
src/context/callees.rs— given a target function, return all callees within depth 3 via the call graph fromIndexing/CpgSlice
- new
- Acceptance criteria. For the CVE-2015-8962 example from the paper,
returns
blk_end_request_all,sg_finish_rem_req,blk_finish_request,blk_put_request,mempool_free,freewithin depth 3. - Risk: medium — depends on call-graph quality.
- Files to modify:
- new
src/context/api_abstraction.rs— for each callee, extract fuzzy branches, concrete branches, call counts, key variables - reuse CFG/DFG from tree-sitter or CPG
- new
- Acceptance criteria. On the paper's CVE-2015-8962 example, the fuzzy
branch abstraction matches the paper's stated output:
In blk_end_request_all: free called on all branches, malloc on no branch. - Risk: medium.
- Files to modify:
src/scanner/phases/llm_phases.rs::run_llm_static_analysis— whenconfig.pacvd.enabled, assemble the abstraction at the configured level and prepend to the LLM promptsrc/config.rs— addPacvdConfig { enabled: bool, level: u8 /* 1-4 */ }
- Acceptance criteria. With
level = 1, the prompt contains only fuzzy branch summaries; withlevel = 4, it contains concrete branches + key variables. Withenabled = false, prompt unchanged. - Depends on: P4.1, P4.2, P4.3.
- Risk: low.
- Files to modify:
src/config.rs— addauto_level: booltoPacvdConfigsrc/scanner/phases/llm_phases.rs— whenauto_level = true, pick level based onLlmConfig.model: large reasoning models (DeepSeek-R1, o3-class) → level 1-2; code-tuned small models (CodeLLaMA-class) → level 3-4
- Acceptance criteria. A known model string maps to the expected level.
- Risk: low.
Paper claim. Represent the multi-agent harness as a typed graph DSL. Search over all 5 dimensions (agent roles A, communication topology G, message schemas Σ, tool allocation Φ, coordination protocol Ψ) in one optimisation loop. Runtime feedback (coverage, sanitizer, traces) diagnoses which part of the harness failed. 84.3% on TerminalBench-2; 10 zero-days in Chrome including 2 critical sandbox escapes.
Five-component harness. H = (A, G, Σ, Φ, Ψ).
- A: agent set, each
(role, prompt, model, tools) - G ⊆ A × A: directed communication topology
- Σ: per-edge message schema (Jinja templates referencing upstream outputs + feedback channels)
- Φ: A → 2^Tools
- Ψ: coordination protocol (sequential, parallel, fan-out, retry-until-success)
DSL core (from paper).
- Node:
agent(role, prompt, model, tools)orfanout(node, k) - Edge:
n1 -> n2(data) orn1 ->_g n2(guarded, g ∈ {ok, fail}); surface syntaxn.on_fail >> m - Feedback channels:
cov(line coverage),branch,san(sanitizer),trace(agent),outcome(test) - Templates: Jinja-style
{{ analyst.out }},{{ cov }},{{ san }}
Well-formedness checks (type system).
- Every template variable resolves to an upstream output or feedback channel.
- Every edge feeds a downstream prompt that actually references the upstream output.
- The graph is connected (every node reachable from a source).
Iterative loop. propose → execute → observe → score → diagnose. The diagnoser reads runtime signals to localise which part of the harness failed (e.g. coverage shows the input never reached the vulnerable function; sanitizer distinguishes a benign crash from the target vuln).
Integration target. src/scanner/phases/other_phases.rs::run_security_agent_verification
(lines 555-592 per outline). Today this phase runs a fixed multi-verifier
pipeline; AgentFlow would make the harness itself searchable. This is the most
invasive integration — start with a static (non-search) harness encoded in the
DSL, then add the search loop as a follow-up.
Sub-tasks.
- Files to modify:
- new
src/agent_flow/dsl.rs—Harness,Node,Edge,FeedbackChannel,Agent { role, prompt, model, tools },Fanout(node, k) src/agent_flow/mod.rs— pub re-exports
- new
- Acceptance criteria. The example harness from Figure 3 of the paper
(analyst → fanout(explorer, 8) → validator, with
validator.on_fail >> analyst) can be constructed in Rust. - Risk: low.
- Files to modify:
src/agent_flow/typecheck.rs— implement rules T-Agent, T-Edge, T-Branch, T-Conn, T-Pipe from Figure 2 of the paper
- Acceptance criteria. Rejects a harness with an unresolved template variable; rejects a disconnected node; accepts the Figure 3 example.
- Risk: medium.
- Files to modify:
- new
src/agent_flow/runtime.rs— execute a well-formed harness: schedule agents per topology, bind template vars, dispatch tool calls, collect feedback channels - reuse
LlmClientfor agent dispatch
- new
- Inputs. Well-formed harness + target program (source + build with coverage/sanitizer instrumentation).
- Outputs. Per-agent traces + final verdict + collected feedback bundle.
- Acceptance criteria. On a single-agent harness, returns the agent's output; on the Figure 3 harness, runs analyst → 8 explorers → validator in order, with retry on validator failure.
- Risk: high — coverage/sanitizer feedback requires build instrumentation
that baco does not have today. Gate behind a
requires_instrumented_targetflag; fall back to stdout/stderr-only feedback when coverage is unavailable.
- Files to modify:
- new
src/agent_flow/diagnose.rs— given a failed run + feedback bundle, produce a structured diagnosis (e.g. "input never reached vulnerable function", "crash was benign", "harness repeats prior trial")
- new
- Acceptance criteria. On a run where coverage shows the vulnerable function was never executed, the diagnosis names that as the failure mode.
- Depends on: P5.3.
- Risk: medium.
- Files to modify:
- new
src/agent_flow/propose.rs— LLM-driven proposer that reads the diagnosis + archive of prior trials and emits a rewritten harness src/scanner/phases/other_phases.rs::run_security_agent_verification— whenconfig.agent_flow.enabled, run the propose→execute→diagnose loop for a budget of N iterationssrc/config.rs— addAgentFlowConfig { enabled, max_iterations: u8, requires_instrumented_target: bool }
- new
- Acceptance criteria. Within the budget, the loop either finds a crashing input or exhausts the budget; never runs forever.
- Depends on: P5.1, P5.2, P5.3, P5.4.
- Risk: high — full search loop is a research-grade system. Recommend shipping P5.1-P5.4 first (static harness execution), P5.5 last.
These are the previously-completed Cross-Cutting tasks, kept here for reference
per rule #6453 (exactly five Cross-Cutting tasks).
- X.1 — RationaleVerdict added to VulnerabilityFinding
- X.2 —
statement_rangefield on VulnerabilityFinding - X.3 — NormalizationConfig with ProjectBaseline
- X.4 — Dataset hygiene rules (tests/fixtures/README.md)
- X.5 — Fine-tuning guidelines (docs/fine-tuning-guidelines.md)
- Several papers (P2.4, P5.3) need the LLM to call tools (function lookup,
coverage query). Check whether
src/llm.rsalready supports tool calling; if not, add atools: Vec<ToolDef>field to the request builder. - Files to inspect:
src/llm.rs,src/llm_analysis.rs. - Blocker for: P2.4, P5.3.
- AgentFlow needs coverage maps and sanitizer reports. These are not produced
by any current phase. Add a
FeedbackCollectortrait with a no-op default and a real implementation gated behindrequires_instrumented_target. - Blocker for: P5.3, P5.4.
- P1.1 (Control Path), P2.3 (call-graph path sampler), P4.2 (callee walker) all
depend on a call graph. The
Indexingphase produces one; verify its language coverage and edge cases (indirect calls, dynamic dispatch). - Blocker for: P1.1, P2.3, P4.2.
- Per rule
#6422, if any paper integration adds a newScanPhase, all checkpoint transition tests intests/unit/checkpoint_resume_tests.rsandtests/unit/pipeline_ordering_tests.rsmust be updated to reflect the newresume_fromsequence before CI can pass. - Current phase count: 24. Any new phase requires renumbering and test updates.
- Per rules
#5773,#5812,#5815,#5846,#5968: every sub-task must end green oncargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test. No commit until the combined gate passes.
- P1.4, P2.1, P3.5 (config scaffolding), P4.1, P5.1 — can run in parallel; all are low-risk and unblock the rest of their tracks.
- P1.1, P1.3, P4.1-P4.3 — context extractors; depend only on existing tree-sitter/CPG infra.
- P3.1, P3.2 — pattern DSL + validator; unblock P3.3.
- P2.3, P2.4 — call-graph sampler + function lookup; unblock P2.5.
- P5.2 — well-formedness checker; unblocks P5.3.
- P1.2, P1.5 — knowledge-path RAG (needs embedding endpoint); wire triple-path.
- P2.2, P2.5 — policy sampling + agent scaffold wiring.
- P3.3, P3.4 — proposer loop + emitter.
- P4.4, P4.5 — prompt integration + auto-level.
- P5.3, P5.4, P5.5 — runtime, diagnoser, proposer (highest risk; last).
- Q1. Embedding endpoint for P1.2 (Knowledge Path RAG). Reuse the existing
LLM provider's embedding API, or add a dedicated
embedding_endpointfield? - Q2. P3 trace corpus. Use the existing
tests/fixtures/labelled samples as the initial corpus, or curate a separatecorpus/directory? - Q3. P5 build instrumentation. AgentFlow's full value needs coverage + sanitizer feedback, which requires building the target with instrumentation. Ship P5.1-P5.4 (static harness) first and defer P5.5 (search loop) until instrumentation is available, or invest in the instrumentation pipeline now?
- Q4. P2 model weights. VulnLLM-R's reasoning adapter techniques are portable, but the full value comes from their fine-tuned 7B model. Host the released weights locally, or apply only the inference-time techniques (truncated generation, policy sampling, summary-based reasoning) to the existing configured LLM?