|
| 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