RFC 0006: cve_patches¶
Status: implemented (experimental) Author: @adithya-s-k Created: 2026-05-15 (retrospective — pipeline shipped in v0.7.0; hardened + PoC agent added in v0.8.5; RFC written 2026-07-15 as archival record)
Summary¶
OSV-driven security-task pipeline: for a target repo, look up its CVE fixes in the OSV database, map each vulnerability to the fixing commit, and emit a Harbor task whose oracle is the upstream security patch. Reuses pr_runtime's F2P/P2P verifier. When the CVE ships no regression test (common), an agentic PoC-test synthesis step (v0.8.5) lets an LLM with shell access inside the vulnerable sandbox write a regression test that catches the vulnerability.
Motivation¶
Two existing corpora — PatchSeeker (5K CVEs), PATCHEVAL (1K) — are datasets of CVE-fix pairs. What didn't exist was a reusable pipeline that takes any repo + OSV and turns the pair into Repo2RLEnv-shaped tasks: paper-only artifacts, no library. cve_patches closes that gap.
Beyond the practical "pipeline over dataset" reason, CVEs are qualitatively different from ordinary bugs — they're security-relevant, human-triaged, and have documented reproduction paths. An RL agent that can fix a CVE (not just any test failure) is a more compelling story than one that fixes an off-by-one.
The counter-argument at v0.7 time was scope: "we don't need a CVE-specific pipeline; pr_runtime would emit these too if the fix PR was in the mined set." Our answer: OSV gives us an authoritative source of which commits are the security fixes, and the CVE→fix mapping is data that pr_runtime doesn't have. Plus the follow-up shape (LLM-synthesized PoC tests when the fix ships no test) is CVE-specific and doesn't belong in pr_runtime.
Design¶
Input¶
- Source — GitHub (needs the GitHub commit API + OSV; local + GitLab don't apply).
- Trigger —
repo2rlenv generate --pipeline cve_patches --repo <owner>/<name> --pipeline-opt limit=20 --pipeline-opt osv_ecosystem=PyPI --llm anthropic/claude-sonnet-4-6 ... - Options model —
CvePatchesOptions:limit,since,min_severity,osv_ecosystem(auto-guessed per owner for known ecosystems),require_fail_to_pass,synthesize_poc_test(default on in v0.8.5),poc_agent_max_iterations, LLM knobs.
Algorithm¶
- Query OSV for vulnerabilities affecting the repo's package.
- For each vulnerability: resolve the fixing commit URL (OSV's
references[].url). git show <fix_commit>→ split into(source_patch, test_patch).- Branch A — CVE ships a test: proceed like
pr_runtime, validate F2P/P2P in the sandbox. - Branch B — CVE has no test (majority case, ~60–70% of OSV entries): run the PoC agent (
_poc_agent.py, v0.8.5). An LLM with shell access inside the pre-fix vulnerable container writes a regression test that: (a) fails on the pre-fix state, (b) passes on the post-fix state, © doesn't reference the fix's SHA or CVE ID. - Validate the resulting
(patch, test_patch)tuple in the sandbox. - Emit Harbor task with the shared shape.
Output¶
Same shape as pr_runtime. [metadata.repo2env] adds a cve_patches subtable: cve_id, ghsa_id, severity, fix_commit, osv_ecosystem, poc_agent_used (bool), poc_agent_iterations (if used).
Verification¶
- Reward kinds —
test_execution+diff_similarity. Uses_pr_runtime_verifier.pyverbatim (upgraded to graded F2P/P2P in v0.8.5; was binary whole-suite before that pass, which spuriously scored the gold patch 0.0 on unrelated suite failures). - Oracle invariant — gold security patch scores
reward=1.0, resolved=Trueon every emitted task. - Non-tamper — same as
pr_runtime.
Anti-contamination¶
CVE tasks have the highest contamination risk in the set. The fix has a CVE ID, a GHSA ID, a public advisory, a PyPI release note, a patched wheel published to PyPI, and often a PoC in the advisory. Multiple leak paths.
- Git-history scrub + egress guard — mandatory. PyPI is blackholed so
pip download <pkg>==<fixed_version>fails (the original observed leak that drove_env_guard.py's creation). - Leak-stripped instruction — v0.8.5 strips CVE / GHSA IDs, PR/commit URLs,
"fixed in vX.Y"phrases,Closes #Ntrailers. The prompt only carries the symptom. - PoC agent's
no_fix_referenceguard — the agent that synthesizes a regression test is prompted + regex-checked to not include the fix commit SHA or CVE ID in the emitted test.
The principle: the environment enforces, the prompt never asks. For CVE tasks, prompt-only defenses are insufficient — the network isolation is doing the real work.
LLM use¶
at bootstrap(cached) — one call chain per repo, standard.at synthesis(branch B, per CVE without a shipped test) — the PoC agent iterates in a shell inside the vulnerable sandbox. Multi-turn, budget-bounded (poc_agent_max_iterations, default 8). Cost: ~$0.10–0.50 per PoC synthesis. Skipped for branch A CVEs.
Yield & repo suitability¶
- 5–25% yield — the lowest in the set. Dominant factors:
- Does the CVE fix actually have a verifiable test (shipped or synthesizable)?
- Does the repo's suite collect in a slim container?
- Real numbers from repo scouting (
plans/cve_repo_scout.py):urllib3→ 0/16 CVEs (tests need network, don't collect in slim);sqlparse→ 2/4 (suite runs clean);werkzeug,requests,httpx,flask→ mid-yield. - What works: library-shaped Python repos with pytest-clean suites and CVE-rich histories.
- Budget many repos: at ~15% yield, 100 CVE tasks ≈ 15–20 CVE-rich, test-clean repos.
Dependencies¶
pr_runtime— full validation harness + verifier + Docker file emitter.bootstrap/— cache._env_guard.py— critical here.- OSV API — public, no auth needed.
- GitHub commit API — to resolve fixing commits.
_poc_agent.py— new in v0.8.5, wraps LiteLLM + a bash-tool loop inside aDockerSandbox.
Alternatives considered¶
- Static PoC extraction from CVE advisories — rejected. Advisory PoCs are inconsistent in format and often not runnable (curl-y HTTP dumps that don't map to a test).
- Third-party CVE-fix datasets (PatchSeeker, PATCHEVAL) as input — the pipeline can consume them (via a hand-curated URL list, once
pr_to_envlands as RFC 0007) but the direct-mine path is OSV. - Skip CVEs without shipped tests entirely — considered pre-v0.8.5. Would have cut yield to ~5%. PoC-agent synthesis expanded the addressable set 3–4×.
Rollout plan¶
Historic. v0.7.0 shipped experimental (binary whole-suite reward, no PoC synthesis). v0.8.5 hardening pass in PR #69 added: - Graded F2P/P2P verifier (was binary; fixed the "gold patch scores 0.0 on unrelated suite failures" bug) - Leak-stripped instruction - Anti-contamination bake (git-history scrub + egress guard applied via emitter) - Agentic PoC-test synthesis - 19-env reference dataset published
Stays experimental until a graded-reward audit shows the reward is meaningful across a broader CVE pool.
Open questions¶
- NVD-direct fallback for CVEs OSV hasn't curated. Rely on OSV's pre-resolved fix URLs today; a PatchSeeker-style LLM+embedding fallback that resolves NVD CVEs to fix commits is on the roadmap.
- PoC synthesis distribution shape — should we distribute PoC tests as part of the published dataset, or does that carry a "we're publishing exploit code" concern? Currently included. Worth revisiting with the community.
References¶
- OSV: osv.dev.
- PatchSeeker: hungkien05/PatchSeeker.
- CVE-Bench (NAACL '25): uiuc-kang-lab/cve-bench.
- Anti-contamination writeup:
plans/reward_hacking_writeups.md.
Implementation¶
| Initial PR | #10 — v0.7: equivalence_tests + cve_patches |
| Shipping release | v0.7.0 (experimental); hardened in v0.8.5 |
| Source file | src/repo2rlenv/pipelines/cve_patches.py |
| PoC agent | src/repo2rlenv/pipelines/_poc_agent.py (added in #69) |
| Options model | src/repo2rlenv/spec/options.py — CvePatchesOptions |
| Doc page | docs/pipelines/cve_patches.md |
| Findings / release notes | (covered in v0.8.5 release notes; standalone findings-cve_patches.md still TBD) |
| Reference dataset | AdithyaSK/repo2rlenv-cve-patches (19 oracle-verified envs) |
| Follow-up PRs | #63 input-source abstraction · #69 anti-contamination defenses + graded verifier + PoC agent + 19-env dataset · #70 reference the dataset in docs · #75 Harbor spec sidecar |