Skip to content

Latest commit

ย 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

title CVE Triage Env
emoji ๐Ÿ”
colorFrom blue
colorTo green
sdk docker
pinned false
tags
openenv
security
reinforcement-learning
vulnerability-triage

๐Ÿ” CVE Triage Environment

An OpenEnv-compliant RL benchmark for training and evaluating AI agents on autonomous security vulnerability triage โ€” the real task of an on-call security engineer.

OpenEnv Compliant HF Space Docker


๐ŸŒ Motivation

Every week, security teams receive dozens of new CVEs. A typical company runs 100+ dependencies. The hard problem is not finding vulnerabilities โ€” tools like Dependabot do that. The hard problem is knowing which ones to patch first, given CVSS scores, exploitability, attack vectors, and the company's actual business exposure.

This environment trains and evaluates AI agents on exactly that skill. It simulates the full triage workflow: detection โ†’ prioritization โ†’ remediation โ€” and scores each phase with a mathematically rigorous reward function that provides partial credit at every step.


๐Ÿ† Benchmark Results

Agent Task 1 โ€” Detection Task 2 โ€” Prioritization Task 3 โ€” Remediation Overall
Naive LLM Baseline (Llama 3.3 70B, zero-shot) 0.4783 0.4494 0.4641 0.4639
Hardened Agentic Pipeline (Deterministic + Reflection) 1.0000 0.9638 0.9013 0.9550

Why the naive LLM scores ~0.47: Pure LLMs hallucinate semantic version ranges. They guess that requests==2.28.0 is not vulnerable to a CVE affecting requests < 2.32.0 โ€” getting the math wrong consistently. This is a known failure mode, not a prompt engineering problem.

Why Task 3 scores 0.9013, not 1.00: The pipeline shows highly realistic variance on complex dependency graphs. While it correctly identifies the critical fixes, minor variations in the exact wording and specificity of the 3rd-slot fix suggestion correctly trigger partial-credit penalties. This is intentional benchmark design โ€” the environment tests genuine reasoning and exactness, avoiding the "overfitted 1.00" trap.

Reflection loop effectiveness: On 5 of 8 stacks, the agent's initial ranking was suboptimal. After receiving Kendall-Tau feedback from the OpenEnv grader, the agent utilized its reflection loop to self-correct. The largest single improvement was on Stack 7 (Healthcare SaaS): 0.77 โ†’ 0.91 (+0.14).


๐ŸŽฏ Tasks

Task 1 โ€” CVE Detection (Easy)

Given a company dependency stack (package โ†’ installed version) and a feed of 12 CVEs with affected version ranges, identify which CVEs affect this specific stack.

  • Input: dependencies dict + full CVE feed
  • Output: affected_cves: list[str]
  • Reward: F1 score (precision ร— recall) with an additional โˆ’0.10 penalty per missed critical-severity CVE

Task 2 โ€” Priority Ranking (Medium)

Given the CVEs that affect the stack (pre-filtered), rank them from highest to lowest patch priority. The agent must weigh CVSS score, exploitability rating, attack vector (network vs. local), and the company's business profile.

  • Input: pre-filtered CVE list + company profile
  • Output: priority_ranking: list[str] (ordered highest โ†’ lowest)
  • Reward: Normalized Kendall-Tau distance + 0.10 bonus for correct #1, + 0.05 bonus for correct #2

Task 3 โ€” Full Triage Report (Hard)

End-to-end triage with no scaffolding. The agent must: (1) detect affected CVEs, (2) rank them, and (3) suggest a specific fix action for the top 3 (e.g. "Upgrade flask to 2.3.0 or higher").

  • Input: raw dependency stack + full CVE feed + company profile
  • Output: affected_cves + priority_ranking + fix_suggestions: dict[str, str]
  • Reward: Weighted composite โ€” 40% detection F1 + 30% ranking Kendall-Tau + 30% fix quality. Fix suggestions are penalized โˆ’0.10 per hallucinated CVE ID.

๐Ÿ“ Reward Function โ€” Full Specification

Task 1 Reward

precision = TP / (TP + FP)
recall    = TP / (TP + FN)
f1        = 2 * precision * recall / (precision + recall)
penalty   = 0.10 * count(missed critical CVEs)
reward    = max(0.0, f1 - penalty)

Task 2 Reward

kendall_tau = concordant_pairs / (concordant_pairs + discordant_pairs)
top1_bonus  = 0.10  if predicted[0] == ground_truth[0]
top2_bonus  = 0.05  if predicted[1] == ground_truth[1]
reward      = min(1.0, kendall_tau + top1_bonus + top2_bonus)

Task 3 Reward

fix_score = sum(0.33 for each top-3 CVE where correct package is mentioned)
            - 0.10 * count(hallucinated CVE IDs in fix_suggestions)
fix_score = clamp(fix_score, 0.0, 1.0)

reward = 0.40 * task1_score + 0.30 * task2_score + 0.30 * fix_score

Empty action penalty: Any step with no fields populated receives reward = โˆ’0.30.


๐Ÿ“ฆ Observation Space

{
  "task_id":          1,
  "stack_id":         "stack_001",
  "company_profile":  "HIPAA-compliant Healthcare SaaS. Patient data processed in-region. External API exposure on Flask endpoints.",
  "dependencies": {
    "flask":        "2.0.1",
    "sqlalchemy":   "1.4.23",
    "cryptography": "3.4.7",
    "requests":     "2.25.1"
  },
  "cve_feed": [
    {
      "cve_id":                  "CVE-2024-0001",
      "affected_package":        "flask",
      "affected_versions_below": "2.3.0",
      "cvss_score":              8.1,
      "exploitability":          "high",
      "attack_vector":           "network",
      "description":             "Remote code execution via crafted request headers.",
      "fix":                     "Upgrade flask to 2.3.0 or higher."
    }
  ],
  "task_description": "TASK 1 โ€” Identify which CVEs affect this dependency stack.",
  "step_number":      0,
  "done":             false,
  "reward":           0.0
}

๐ŸŽฎ Action Space

{
  "task_id": 1,
  "affected_cves":    ["CVE-2024-0001", "CVE-2024-0005"],
  "priority_ranking": ["CVE-2024-0001", "CVE-2024-0005"],
  "fix_suggestions": {
    "CVE-2024-0001": "Upgrade flask to 2.3.0 or higher.",
    "CVE-2024-0005": "Upgrade cryptography to 41.0.0 or higher."
  }
}

Fields required per task:

  • Task 1: task_id + affected_cves
  • Task 2: task_id + affected_cves + priority_ranking
  • Task 3: all four fields

๐Ÿ—๏ธ Environment Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    FastAPI Server (port 7860)                โ”‚
โ”‚                  Thread-safe (threading.Lock)                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  POST /resetโ”‚       POST /step           โ”‚    GET /state      โ”‚
โ”‚  task_id   โ”‚  action: CveTriageAction   โ”‚  episode_id        โ”‚
โ”‚  stack_idx โ”‚  โ†’ CveTriageObservation    โ”‚  step_count        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ–ผ                โ–ผ                โ–ผ
    grade_task1()   grade_task2()   grade_task3()
    F1 + penalty    Kendall-Tau     Weighted composite
                    + top-2 bonus   + hallucination penalty

State management: The environment is fully stateless between episodes. reset() generates a new episode_id (UUID4) and clears all episode state. The threading lock ensures concurrent evaluation requests from automated graders cannot corrupt state mid-episode.


๐Ÿค– Inference Pipeline Architecture

Our pipeline treats CVE triage as a multi-stage agentic workflow, not a single LLM call:

[Tech Stack] + [CVE Feed]
       โ”‚
       โ–ผ
โ‘  Deterministic Version Matcher
  packaging.version (PEP 440) โ€” mathematically exact,
  zero hallucination. Achieves perfect 1.00 on Task 1.
       โ”‚
       โ–ผ
โ‘ก LLM Priority Ranker  โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  Llama 3.3 70B via Groq.                          โ”‚
  Ranks by CVSS + exploitability + company profile. โ”‚
       โ”‚                                            โ”‚
       โ–ผ                                            โ”‚
  OpenEnv grader returns partial reward             โ”‚
  + Kendall-Tau feedback                            โ”‚
       โ”‚                                            โ”‚
       โ–ผ                                            โ”‚
  score < 1.0? โ”€โ”€โ”€โ”€ Reflection Loop (max 3x) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  score = 1.0? โ”€โ”€โ”€โ”€ proceed
       โ”‚
       โ–ผ
โ‘ข LLM Fix Suggester
  Context-aware mitigation for top 3 CVEs.
  Penalized for hallucinated CVE IDs.

Key insight: Offloading version-range math to a deterministic engine and using the LLM strictly for reasoning tasks eliminates the primary failure mode of naive LLM baselines.


๐Ÿ“Š Sample Reflection Log

The reflection loop in action โ€” Stack 7 (Healthcare SaaS, HIPAA compliant), self-correcting after receiving directional feedback (+0.14 improvement):

[START] task=task_2 env=cve_triage_env model=llama-3.3-70b-versatile
[STEP] step=1 action=rank_cves reward=0.77 done=false error=null
[STEP] step=2 action=rank_cves reward=0.91 done=false error=null
[END] success=true steps=2 score=0.91 rewards=0.77,0.91

Full run logs for all 8 stacks available in evaluation_logs.txt. Full per-stack breakdown available in reports.txt.


๐Ÿ“‚ Dataset

The environment ships with 8 company stacks across distinct real-world risk profiles:

Stack Profile Key Packages Task 3 Score
stack_001 B2B SaaS Startup โ€” public REST API, no WAF, 10k users Flask, SQLAlchemy, Redis, Celery 1.00
stack_002 Internal Analytics Tool โ€” no public exposure, 50 users Django, Pillow, PyYAML, Pandas 0.88
stack_003 Fintech Platform โ€” WAF enabled, handles payments, 500k users Flask, Paramiko, PyJWT, aiohttp 0.88
stack_004 DevOps Tooling โ€” SSH automation, internal only Paramiko, cryptography, PyYAML 1.00
stack_005 E-commerce Platform โ€” image processing, 1M users Django, Pillow, Celery, Gunicorn 0.87
stack_006 ML Platform โ€” internal Jupyter notebooks, S3 access Pandas, Jinja2, boto3, cryptography 0.87
stack_007 Healthcare SaaS โ€” HIPAA compliant, patient portal FastAPI, PyJWT, cryptography, httpx 0.84
stack_008 Startup API Gateway โ€” microservices, no auth layer FastAPI, aiohttp, PyJWT, psycopg2 0.87

20 CVEs in the feed spanning CVSS 4.2โ€“9.8, covering network/local/adjacent attack vectors and high/medium/low exploitability ratings. Ground truth rankings are calculated per-stack based on CVSS score, exploitability weight, and attack vector multiplier.

Extending the dataset: New stacks can be added by appending entries to data/stacks.json and data/ground_truth.json โ€” no code changes required. The environment loads all stacks dynamically at startup. CVEs can be extended in data/cves.json; the graders are fully data-driven so rankings automatically reflect any new entries. This makes the benchmark straightforward to scale to larger corpora or domain-specific CVE feeds (e.g., Node.js, Java, Go ecosystems), and enables curriculum learning by ordering stacks from simple (few dependencies, low CVSS variance) to complex (many packages, near-equal CVSS scores).


๐Ÿš€ Setup & Usage

Environment Variables (Required)

export API_BASE_URL="[https://api.groq.com/openai/v1](https://api.groq.com/openai/v1)"   # LLM API endpoint
export MODEL_NAME="llama-3.3-70b-versatile"             # Model identifier
export HF_TOKEN="your_api_key_here"                     # API key (Groq or OpenAI)
export ENV_BASE_URL="http://localhost:8000"              # Environment server URL

Local Quickstart

# 1. Install dependencies
pip install -e .

# 2. Start the environment server
uvicorn server.app:app --host 0.0.0.0 --port 8000

# 3. In a second terminal, run evaluation
python inference.py

Docker

# Build and run
docker build -t cve-triage-env .
docker run -p 7860:7860 \
  -e HF_TOKEN=your_key \
  -e API_BASE_URL=[https://api.groq.com/openai/v1](https://api.groq.com/openai/v1) \
  -e MODEL_NAME=llama-3.3-70b-versatile \
  cve-triage-env

API Endpoints

Method Endpoint Description
POST /reset Start new episode. Body: {"task_id": 1, "stack_index": 0}
POST /step Submit action. Body: {"action": {...}}
GET /state Current episode state
GET /health Health check
GET /schema Action + observation JSON schemas
GET /metadata Environment metadata

๐Ÿ”ฌ Research Extensions (PyTorch & GNNs)

The structured observation space is explicitly designed to support future PyTorch research:

Graph Neural Networks (PyG): The dependencies dict naturally forms a directed dependency graph. Researchers can use PyTorch Geometric to model how vulnerability risk propagates through transitive dependencies โ€” a library like cryptography vulnerably affects every framework that depends on it.

PPO Policy Training: The F1 and Kendall-Tau reward functions are differentiable proxies suitable for training lightweight RL policies (e.g., fine-tuned Llama-3-8B with PPO) to replace the deterministic + LLM pipeline entirely.

Curriculum Learning: The easy โ†’ medium โ†’ hard task progression is designed for curriculum learning โ€” train on Task 1 until convergence, then transfer to Task 2 using the learned detection representations.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages