Model Card — CEVuD Vulnerability Classifier
HuggingFace-ready model card. This documents the custom Stage-2 classifier trained by
src/training/and is the artifact to publish athuggingface.co/Denash/codebert-vuln-classifier.
Model Details
Model Description
- Model ID:
Denash/codebert-vuln-classifier - Model type: Fine-tuned transformer for binary sequence classification
- Base model:
microsoft/codebert-base(RoBERTa-based, ~125 M parameters) - Language: Python (code)
- License: MIT (inherits from CEVuD; CodeBERT itself is MIT)
- Task: Binary classification — vulnerable vs. safe Python function chunks
This model is the Stage-2 local classifier ("small model") in the CEVuD
pipeline. It scores uniform code windows (chunks) of Python functions and
outputs a probability P(vulnerable) ∈ [0, 1]. The Stage-2 gate combines this
neural probability with Semgrep's static severity via a linear risk equation
R = W₁·S_sev + W₂·P_slm to decide whether to escalate a finding to the
Stage-3 LLM.
The model is trained on the CEVuD Training Dataset (CVEfixes-based) and is designed to be a component of a gated pipeline, not a standalone vulnerability oracle. Its primary role is to suppress trivially-safe code so that the expensive LLM is only called when truly needed.
Model Architecture
The model uses the standard HuggingFace RobertaForSequenceClassification
head on top of the CodeBERT encoder:
Input: Python code chunk (≤ 512 tokens)
↓
CodeBERT Encoder (12 layers, 768 hidden dim, 12 attention heads)
↓
[CLS] token hidden state (768-dim)
↓
Pooler: dense(768 → 768) + tanh
↓
Classifier: dense(768 → 768, tanh) + dropout → out_proj(768 → 2)
↓
Softmax → P(vulnerable), P(safe)
Key components:
- Encoder:
microsoft/codebert-base— a RoBERTa-based transformer pre-trained on natural language and programming language pairs. Frozen by default; can be unfrozen for fine-tuning. - Pooler: Maps the
[CLS]token to a 768-dim representation via a dense layer + tanh activation. - Classifier head: Two-layer MLP (768 → 768 → 2) with dropout and tanh activation. Outputs logits for the two classes.
- Output: Softmax probabilities.
P(vulnerable) = softmax(logits)[:, 1].
When freeze_backbone=True is used, only the classifier.* submodule is
trained; the encoder and pooler stay frozen. This is the recommended setting for
small datasets (~1.4k samples) because it is more sample-efficient and stable.
Intended Use
Primary Intended Use
The model is designed to be the Stage-2 local edge classifier in the CEVuD pipeline. Its intended use case is:
- CI/CD integration: Scan code changes in pull requests or pushes.
- Local gating: Score each Semgrep finding locally (zero marginal cost).
- Escalation decision: Combine the neural score with static severity to decide whether to escalate to the Stage-3 LLM.
How to Use
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
model_id = "Denash/codebert-vuln-classifier"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
model.eval()
def score_chunk(code_chunk: str) -> float:
"""Return P(vulnerable) for a single code chunk (≤ 512 tokens)."""
inputs = tokenizer(
code_chunk,
truncation=True,
max_length=512,
padding="max_length",
return_tensors="pt",
)
with torch.no_grad():
logits = model(**inputs).logits
probs = torch.softmax(logits, dim=-1)
return float(probs[0, 1]) # P(vulnerable)
# Example: score a Python function chunk
code = """
def get_user(user_id):
query = "SELECT * FROM users WHERE id = " + str(user_id)
return db.execute(query)
"""
p_vuln = score_chunk(code)
print(f"P(vulnerable) = {p_vuln:.3f}")
Important: This model scores chunks (uniform code windows), not whole
functions. For a complete function, chunk it into 64-line windows with 8-line
overlap, score each chunk, and aggregate using max (default) or mean.
Out-of-Scope Uses
- Standalone vulnerability oracle: The model is not designed to be used alone. Its standalone recall is 28.9% (on CVEfixes test) and 70.5% (on VUDENC). It is meant to be part of a gated pipeline.
- Other languages: The model is trained on Python only. Performance on other languages is unverified.
- Adversarial settings: The model has not been evaluated against adversarially crafted code.
- Definitive security verdict: The model's output is one input to a composite gate. It should not be used as the sole determinant of whether code is vulnerable.
Training Data
Dataset Overview
The model is fine-tuned on the CEVuD Training Dataset (CVEfixes-based), a curated corpus of 2,181 Python function chunks derived from real-world vulnerability fixes.
| Property | Value |
|---|---|
| Source | hitoshura25/cvefixes (HuggingFace) |
| Total samples | 2,181 |
| Projects (repos) | 554 |
| Vulnerable | 474 (21.7%) |
| Safe | 1,707 (78.3%) — 1,643 benign_sibling + 64 benign_control |
| Unique CWEs | 93 |
| Unique CVEs | 470 |
| Chunk size | 64 lines with 8-line overlap |
| Hunk-centering | Enabled (vulnerable chunks contain the sink) |
| Near-duplicate threshold | 0.75 token-similarity |
Data Creation
The training data is created through a multi-stage pipeline:
CVEfixes conversion:
src/scripts/convert_cvefixes.pystreams the CVEfixes dataset, filters to Python, applies noise and trivial-change filters, and emits only vulnerable samples (label=1). The post-fix function is retained infixed_codebut not emitted aslabel=0.Benign control mining:
src/scripts/mine_benign_functions.pyextracts safe functions from files the fix commit did not touch. These are taggedsample_subtype="benign_control"and serve as the genuine safe class.Enrichment:
src/training/dataset_builder.pyenriches each sample with the full enclosing function (AST-expanded) and module-level imports.Chunking: Functions are cut into 64-line windows with 8-line overlap. For vulnerable samples, only chunks overlapping the diff hunk are kept (hunk-centering).
Quality filters: Hard contradictions and near-duplicate safe chunks (>0.75 token-similar to vulnerable chunks) are removed.
Splitting: Project-level 60/20/20 split with
seed=42. No project appears in more than one split.
Safe Class Construction
The safe class is constructed from two sources:
- Benign siblings (1,643 samples): Functions from the same file as the vulnerable function, but in commits the fix did not touch.
- Benign controls (64 samples): Functions from files the fix commit never touched, mined from verified-benign repositories.
Both sources are passed through a token-similarity guard (>0.75 to any vulnerable function ⇒ dropped) to prevent near-duplicates from entering the safe class.
The post-fix function is explicitly not used as label=0 because it is a
near-duplicate of its vulnerable twin (median token-similarity ≈ 0.94). Using
it would create contradictory pairs and collapse training to P = 0.5.
Data Splits
| Split | Samples | Vulnerable | Safe | Projects |
|---|---|---|---|---|
| Train | 1,464 | 316 | 1,148 | 330 |
| Validation | 358 | 76 | 282 | — |
| Test | 359 | 82 | 277 | — |
Preprocessing
- Tokenizer:
AutoTokenizerfrommicrosoft/codebert-basewithmax_length=512,padding="max_length",truncation=True. - Chunking: Uniform 64-line windows with 8-line overlap. Matches inference format.
- Labels:
0= safe,1= vulnerable. Mapped toid2label = {0: "safe", 1: "vulnerable"}andlabel2id = {"safe": 0, "vulnerable": 1}. - Problem type:
single_label_classification(softmax).
Evaluation Data
Datasets Used
The model is evaluated on two datasets:
- CVEfixes test split (same corpus as training): 359 samples, project-level split. This measures the model's standalone performance on held-out projects.
- VUDENC test split (held-out corpus): 821 samples, project-level split. This measures the model's performance on a completely different dataset.
Metrics
| Metric | CVEfixes Test | VUDENC Test |
|---|---|---|
| Accuracy | 81.9% | — |
| Precision | 100.0% | — |
| Recall | 20.7% | 70.5% |
| F1 | 34.3% | — |
| ROC-AUC | 0.0* | — |
| PR-AUC | 0.496 | — |
* The standalone evaluator initially reported ROC-AUC=0.0 due to loading the wrong checkpoint. This was fixed; the correct ROC-AUC on the CVEfixes validation split is 74.9%.
The gate study (full CEVuD pipeline) is evaluated on VUDENC using F2 (beta=2.0) as the primary metric, with Token Reduction Rate (TRR) and Cost Reduction as efficiency metrics.
Quantitative Analysis
Training Dynamics
| Epoch | Train Loss | Val Loss | Val Accuracy | Val Precision | Val Recall | Val F1 | Val ROC-AUC |
|---|---|---|---|---|---|---|---|
| 1 | — | 0.419 | 84.9% | 100.0% | 28.9% | 44.9% | 74.9% |
| 2 | — | 0.419 | 84.9% | 100.0% | 28.9% | 44.9% | 74.9% |
| 3 | — | 0.419 | 84.9% | 100.0% | 28.9% | 44.9% | 74.9% |
| 4 | 0.710 | 0.419 | 84.9% | 100.0% | 28.9% | 44.9% | 74.9% |
Training early-stopped at epoch 4 (patience=3 on validation loss). The best checkpoint is from epoch 1 (step 366), which has the same validation metrics as epoch 4.
Confusion Matrix (Validation)
| Predicted Safe | Predicted Vulnerable | |
|---|---|---|
| Actually Safe | 282 (TN) | 0 (FP) |
| Actually Vulnerable | 54 (FN) | 22 (TP) |
Confusion Matrix (Test)
| Predicted Safe | Predicted Vulnerable | |
|---|---|---|
| Actually Safe | 277 (TN) | 0 (FP) |
| Actually Vulnerable | 65 (FN) | 17 (TP) |
Key Observations
- Precision = 100%: The model never produces a false positive. When it predicts "vulnerable", it is always correct.
- Recall = 28.9% (val) / 20.7% (test): The model misses most vulnerabilities. This is expected for a small model trained on a difficult, imbalanced corpus.
- ROC-AUC = 74.9%: The model learns strong discriminative ranking. The low recall reflects the classification threshold (0.5), not poor ranking ability.
- Class imbalance effect: The ~1:3.6 vulnerable/safe split causes the model to be conservative. Class-weighted cross-entropy (weights ≈ [0.64, 2.30]) gives the vulnerable class a ~3.6× higher per-sample gradient signal, but the small dataset size limits how much the model can learn.
Performance in the Gated Pipeline
When embedded in the CEVuD pipeline with the tuned linear gate ($W_1=0.15, W_2=0.85, T=0.2$):
| Metric | Value |
|---|---|
| Recall | 95.2% |
| Precision | 12.8% |
| F2 | 0.417 |
| Escalation Rate | 94.9% |
| TRR | 5.1% |
| Cost Reduction | 5.0% |
The linear gate improves recall from 70.5% (small model standalone) to 95.2% by combining the neural signal with Semgrep's static signal. The trade-off is lower precision (12.8%) and high escalation rate (94.9%), which is acceptable because the escalated snippets are reviewed by a more capable LLM.
Environmental Impact
- Hardware: CPU-only training (no GPU required).
- Training time: ~2.4 hours on a 4-core CPU (8,759 seconds).
- Estimated CO2 emissions: Using the ML CO2 Impact calculator, CPU training for ~2.4 hours on an Intel i7-9700K emits approximately 0.2-0.4 kg CO2 (depending on electricity grid carbon intensity).
- Inference cost: The model runs locally on CPU/edge hardware. A single inference on a 512-token chunk takes ~50-100ms on a modern CPU, with near-zero marginal cost compared to cloud LLM APIs.
Ethical Considerations
Intended Users
CEVuD is designed for software developers, security engineers, and organizations that want to shift-left security scanning in their CI/CD pipelines. The model augments human experts by filtering safe code, not replacing them.
Potential Misuse
- False sense of security: The model's 100% precision might lead users to believe it never misses vulnerabilities. In reality, its standalone recall is only 28.9%, and even in the gated pipeline, 4.8% of vulnerabilities slip through (FN=5 out of 105 on VUDENC test). Users must understand that CEVuD is a filter, not a definitive scanner.
- Over-reliance on automation: The low precision (12.8%) means many benign snippets are escalated. If users skip reviewing escalated snippets, they waste LLM resources without gaining security.
- Bias in training data: CVEfixes is biased toward well-known, high-profile projects. Vulnerabilities in niche or internal codebases may not be represented. The model may perform worse on code that differs stylistically from the CVEfixes corpus.
Fairness and Transparency
- The model's decisions are interpretable: the linear gate formula
R = W₁·S_sev + W₂·P_slmis transparent, and the weights are selected by exhaustive grid search. - The training data is publicly available, and the full training pipeline is open-source.
- The model does not process personal data. Code snippets are the only input.
Limitations and Recommendations
| Limitation | Recommendation |
|---|---|
| Low standalone recall (28.9%) | Always use as part of the gated pipeline, not standalone. |
| Python-only | Do not apply to other languages without retraining. |
| Chunk-level granularity | Score whole functions by chunking and aggregating. |
| CWE imbalance | Consider augmenting rare CWE types if your use case targets specific vulnerabilities. |
| No adversarial evaluation | Do not deploy in adversarial settings without additional testing. |
Training Procedure
Implementation
Training is implemented in src/training/trainer.py using the HuggingFace
Trainer API with a custom WeightedTrainer subclass.
Loss Function
Class-weighted cross-entropy: The ~1:3.6 vulnerable/safe imbalance is countered by inverse-frequency class weights:
weight(class) = total_samples / (num_classes × count(class))
For the current split, this yields approximately [0.64, 2.30] for
[safe, vulnerable], meaning each vulnerable sample contributes ~3.6× the
gradient signal of a safe sample.
Why not focal loss? Focal loss was evaluated but removed in favor of class weights. Class-weighted cross-entropy is simpler, more interpretable, and equally effective for this dataset size. The weights are computed automatically from the training distribution.
Optimization
- Optimizer: AdamW
- Learning rate: 2e-5
- Weight decay: 0.01
- Batch size: 8
- Warmup: Linear warmup for 10% of total steps
- Scheduler: Linear decay after warmup
Regularization
- Early stopping: Patience=3 epochs on validation loss. Best checkpoint restored.
- Dropout: 0.1 in the classifier head (default for RobertaClassificationHead)
- Frozen backbone (optional): When
freeze_backbone=True, only the classifier head is trained. Recommended for small datasets.
Reproducibility
All randomness is controlled with seed=42:
- Dataset split:
seed=42 - Sample capping:
seed=42 - Model initialization:
seed=42 - Training shuffle:
seed=42
Training Command
python -m src.training.cli run-all \
--manifest benchmark_manifest_cvefixes.json \
--benign-manifest benign_controls_manifest.json \
--epochs 30 \
--batch-size 8
How to Get Started with the Model
Installation
pip install transformers torch
Loading the Model
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
model_id = "Denash/codebert-vuln-classifier"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
model.eval()
Scoring a Function
def score_function(function_code: str, chunk_max_lines: int = 64,
chunk_overlap: int = 8) -> float:
"""Score a Python function for vulnerability probability."""
lines = function_code.splitlines()
chunks = []
for i in range(0, max(len(lines) - chunk_overlap, 1), chunk_max_lines - chunk_overlap):
chunk = "\n".join(lines[i:i + chunk_max_lines])
if chunk.strip():
chunks.append(chunk)
scores = []
for chunk in chunks:
inputs = tokenizer(chunk, truncation=True, max_length=512,
padding="max_length", return_tensors="pt")
with torch.no_grad():
logits = model(**inputs).logits
probs = torch.softmax(logits, dim=-1)
scores.append(float(probs[0, 1]))
return max(scores) if scores else 0.0
# Example usage
vuln_code = """
def get_user(user_id):
query = "SELECT * FROM users WHERE id = " + str(user_id)
return db.execute(query)
"""
print(f"P(vulnerable) = {score_function(vuln_code):.3f}")
Using in the CEVuD Pipeline
from triage_orchestrator import TriageOrchestrator
orchestrator = TriageOrchestrator(
config_path="config.json",
workspace_path="."
)
orchestrator.process_pipeline()
Model Card Authors
CEVuD Authors
Citation
@misc{cevud2026,
title={CEVuD: Cost-Effective Vulnerability Detection via Gated Static-Neural Reasoning},
author={CEVuD Authors},
year={2026},
note={Model: Denash/codebert-vuln-classifier; Training Dataset: Denash/cevud-training-dataset; Pipeline Dataset: Denash/cevud-pipeline-dataset}
}
Model Card Contact
Open an issue on the CEVuD GitHub repository.
Related Resources
| Resource | Link |
|---|---|
| Training Dataset (CVEfixes) | Denash/cevud-training-dataset |
| Pipeline Dataset (VUDENC) | Denash/cevud-pipeline-dataset |
| Source Dataset (CVEfixes) | hitoshura25/cvefixes |
| Source Dataset (VUDENC) | DetectVul/Vudenc |
| CEVuD GitHub | https://github.com/Denash/CEVuD |
- Downloads last month
- -