Skip to content

Commit b597597

Browse files
docs: silent-fix detection — research background + current algorithm
docs/silent_fix_detection.md: the research landscape (learned code-change classifiers — Sabetta&Bezzi/VulFixMiner/GraphSPD; patch backlinking — VCMatch/PatchScout/OSV; training-free LLM — LLM4VFD/agents/VulReaD/Vul-RAG) and exactly what this repo runs: deterministic gate + tiering, method B (OSV backlink/fix_commit), method A (training-free gemma4:31b LLM4VFD-style classifier over local_diffs), the rejected approaches (regex, TF-IDF, multi-agent consensus) with why, and the pipeline diagram. Cross-links model_evaluation.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent abdb81f commit b597597

1 file changed

Lines changed: 167 additions & 0 deletions

File tree

docs/silent_fix_detection.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Silent-fix detection — research background & the algorithm we use
2+
3+
**Silent (a.k.a. stealth) security fixes** are patches that repair an exploitable
4+
or availability-affecting defect but ship *without* an advisory (no CVE/GHSA),
5+
often with a deliberately uninformative commit message, so users upgrade before
6+
attackers notice. Ethereum clients do this heavily — issue #2 in this project
7+
found **~98–100% of client security fixes are silent**. Detecting them is the
8+
core problem this dataset addresses.
9+
10+
This document covers (1) the research landscape and (2) exactly what algorithm
11+
this repo runs, including what we tried and rejected.
12+
13+
---
14+
15+
## 1. Research landscape
16+
17+
The literature splits along two axes: **what evidence** is used (commit message
18+
vs. code change vs. external advisory) and **how** (hand-rules vs. trained model
19+
vs. prompted LLM). Two families matter for silent fixes.
20+
21+
### A. Learned code-change classifiers — *detect unknown* silent fixes
22+
The message is useless for a silent fix by construction, so these learn from the
23+
**code change** itself.
24+
25+
| work | idea | representation |
26+
|---|---|---|
27+
| Sabetta & Bezzi, ESEM 2018 | security-relevant commit classification | patch **as a document** (bag-of-words) + classifier |
28+
| **VulFixMiner**, Zhou et al. ASE 2021 | "needle in a haystack" — mine *silent* vuln fixes | **CodeBERT** on the code change |
29+
| **GraphSPD**, Wang et al. S&P 2023 | merge pre/post-patch **code property graphs** (MCPG) | multi-attributed graph convolution |
30+
| SPI / E-SPI, CoLeFunDa | contrastive learning, function-change augmentation | PLM embeddings |
31+
| VulCurator | multi-modal (message + code + issue) | ensemble |
32+
| XGV-BERT, CSGVD | **CodeBERT + GNN** fusion | hybrid |
33+
34+
Recurring finding: **surface features don't separate security fixes from
35+
ordinary code**; you need semantic code embeddings or graph structure. Class
36+
imbalance is brutal — real fixes are <1% of commits, so precision collapses
37+
without a pre-filter (VulFixMiner's title says it: *finding a needle in a
38+
haystack*).
39+
40+
### B. Patch backlinking — *recover known* silent fixes from advisories
41+
Start from a confirmed advisory and trace to the fixing commit. High precision,
42+
but only covers vulns that eventually got an advisory.
43+
44+
- **VCMatch**, **PatchScout**, **Midas** — rank commits for a given CVE by
45+
feature similarity (code, message, CVE text).
46+
- **OSV** structured data — `affected[].ranges[].events[].fixed` gives the exact
47+
fixing commit/version; `references[type=FIX]` links the patch.
48+
49+
### C. LLM-based (recent, 2024–2026) — training-free
50+
The axis this project ultimately uses.
51+
52+
| work | approach | trained? |
53+
|---|---|---|
54+
| **LLM4VFD**, arXiv 2501.14983 (2025) | CoT over **diff + dev-artifacts (issue/PR) + history-RAG**; +68–145% F1 over PLM baselines | no — prompting only |
55+
| **From LLMs to Agents**, arXiv 2511.08060 (2025) | zero-shot LLM / ReAct agent for security-patch detection; agent reaches graph-level precision (**86%**); notes **LLM×graph is unexplored** | no |
56+
| **LLMDA** (Just-in-Time Detection of Silent Security Patches), arXiv 2312.01241 | LLM-generated patch explanations + code-text alignment; beats GraphSPD +20% F1 | yes |
57+
| **VulReaD**, arXiv 2602.10787 (2026) | **knowledge-graph-guided** LLM reasoning (anchors CWE semantics, cuts hallucination) | yes (LoRA/ORPO) |
58+
| **Vul-RAG** (2024) | knowledge-level RAG + LLM | mostly no |
59+
60+
Key takeaways for a *training-free* setting: (a) LLM4VFD-style prompting works
61+
without fine-tuning; (b) diff + artifacts are the signal; (c) light structural
62+
("graph-lite") context is a cheap, largely-unexplored add-on; (d) LLMs still
63+
need a pre-filter because of the base-rate problem; (e) commercial models beat
64+
open ones, but strong open code-models are close.
65+
66+
---
67+
68+
## 2. The algorithm in this repo
69+
70+
We implement **both** working families and reject what doesn't survive
71+
validation. The guiding discipline: **a signal ships only if it survives an
72+
*applied-ranking* spot-check**, not just an aggregate metric (a TF-IDF model
73+
with CV-AUC 0.97 was killed because it ranked features above real fixes).
74+
75+
### Pipeline
76+
77+
```
78+
raw crawl (11 clients) [collection/]
79+
│ advisories · stealth PRs · commits · releases · CVE/OSV/RustSec
80+
81+
build_derived → merge → cross_reference (dedup)
82+
83+
data/raw/train.classified.parquet (~18.5k rows)
84+
85+
▼ pipeline/build_security_dataset.py (deterministic gate + tiering)
86+
87+
├─ T1 drop release-note boilerplate
88+
├─ T2 drop CI/docs/dep-bump meta-work (title-anchored; advisory-id/
89+
│ strong-kw/severity protected)
90+
├─ T2b drop NVD substring-match false positives (glibc/X.Org/… mis-hits)
91+
├─ GATE keep a row on ANY independent signal
92+
├─ authority_tier A_authoritative · B_corroborated · C_candidate
93+
├─ n_signals count of independent signals
94+
└─ fix_commit from /commit/ URLs ← method B (backlink)
95+
96+
data/ethereum_vulns.parquet (essential slice = tier A∪B)
97+
```
98+
99+
### Signals feeding the gate / tiering (`count_signals`)
100+
Independent, order-free; ≥2 stacking promotes C→B:
101+
1. advisory **id** (CVE/GHSA/RustSec) · 2. rated **severity**
102+
3. strong security keyword · 4. moderate bug-class keyword
103+
5. **sensitive subsystem** touched (A2 "graph-lite": fork-choice, evm, p2p, kzg…)
104+
6. **fix-verb × crash-class impact** co-occurrence ("fix panic on …")
105+
7. LLM STRIDE / CWE (if a classification pass was run)
106+
8. **learned silent-fix classifier**`silent_fix_prob ≥ 0.70` ← method A
107+
108+
### Method B — patch backlinking (`collection/crawl_osv.py`, `fix_commit`)
109+
OSV's structured `introduced`/`fixed` events are extracted into
110+
`introduced_in_commit` + a fix backlink; `fix_commit` is pulled from `/commit/`
111+
source URLs. Deterministic, high precision, bounded to advisory-covered vulns.
112+
113+
### Method A — training-free LLM classifier (`collection/llm_classify_fixes.py`)
114+
LLM4VFD-style, **no training, no torch**:
115+
116+
```
117+
row → diff (via local_diffs, rate-limit-free) ─┐
118+
title + description (dev artifacts) ──────┼─► CoT prompt ─► LLM ─► JSON
119+
sensitive subsystem (graph-lite) ─────────┘ {is_security_fix,
120+
confidence, vuln_class}
121+
→ silent_fix_prob = confidence if fix else 1-confidence
122+
→ ≥0.70 counts as an independent signal (promotes C→B)
123+
```
124+
125+
- **Diffs:** `collection/local_diffs.py` serves them from a bare **blobless git
126+
clone** (geth = 21 MB / 1.7 s) with a persistent cache + delta `git fetch`
127+
no GitHub REST rate limit, and re-runs only fetch new rows. Falls back to `gh`
128+
for the rare divergent-fork PR.
129+
- **Model:** `gemma4:31b` (chosen by an 80-item eval sweep — F1 0.872, precision
130+
0.895, recall 0.85; see [`model_evaluation.md`](./model_evaluation.md)). Runs
131+
off-Claude via the env's Ollama-Cloud route (`--engine openai`); Claude and
132+
local Ollama are also selectable.
133+
- **Graph-lite, not full graph:** we feed the touched security-sensitive
134+
subsystem as context — the cheap LLM×graph combination the 2025 survey calls
135+
unexplored. Full CPG/GNN would need training and a code checkout.
136+
137+
### What we rejected (validated negatives)
138+
- **Regex diff classifier** (`detect_silent_fixes.py`) — surface guard/impact
139+
regexes don't discriminate (ranking inverted, then collapsed to ≈chance).
140+
This reproduces the literature's reason for using learned embeddings.
141+
- **TF-IDF patch-as-document classifier** (`train_silent_fix_classifier.py`) —
142+
CV-AUC 0.97 but *misleading*: dep-bump/manifest confound, then topic-vocabulary
143+
overfitting; in deployment it ranked features above real fixes. Not shipped.
144+
- **Multi-agent consensus** (devstral + qwen, majority vote) — no gain over the
145+
best single model; the models' errors are correlated (devstral+qwen are
146+
nested). Only `AND(gemma,qwen)` is useful, as a 0.93-precision sub-tier.
147+
148+
### Result
149+
The learned silent-fix pass classified 339 `C_candidate` diffs and promoted
150+
**166** real silent fixes (133 DoS / 16 consensus / 11 validation …) into the
151+
corroborated tier. Essential slice (A∪B): **173 → 1,535** across the project.
152+
153+
---
154+
155+
## References
156+
- Sabetta & Bezzi, *A practical approach to the automatic classification of security-relevant commits*, ESEM 2018.
157+
- Zhou et al., *VulFixMiner: Finding a Needle in a Haystack — Automated Mining of Silent Vulnerability Fixes*, ASE 2021.
158+
- Wang et al., *GraphSPD: Graph-Based Security Patch Detection with Enriched Code Semantics*, IEEE S&P 2023 — https://csis.gmu.edu/ksun/publications/SP23_GraphSPD.pdf
159+
- *Just-in-Time Detection of Silent Security Patches* (LLMDA), arXiv 2312.01241.
160+
- *Code Change Intention, Development Artifact and History Vulnerability … by LLM* (LLM4VFD), arXiv 2501.14983.
161+
- *From LLMs to Agents: … LLMs and LLM-based Agents in Security Patch Detection*, arXiv 2511.08060.
162+
- *VulReaD: Knowledge-Graph-guided Software Vulnerability Reasoning and Detection*, arXiv 2602.10787.
163+
- *Vul-RAG: Enhancing LLM-based Vulnerability Detection via Knowledge-level RAG*, arXiv 2406.11147.
164+
- OSV schema — https://ossf.github.io/osv-schema/ ; VCMatch / PatchScout (CVE→commit ranking).
165+
166+
_See also [`model_evaluation.md`](./model_evaluation.md) for model benchmarks and
167+
`collection/IMPROVEMENT_LOG.md` for the full iteration ledger._

0 commit comments

Comments
 (0)