CVE → CWE Classifier (Qwen3-32B)

A QLoRA fine-tune of Qwen3-32B that maps a free-text CVE description to the CWE weakness ID(s) it corresponds to. The LoRA adapter is merged into the base and released in 16-bit, so it loads directly with transformers. A smaller/faster variant is available at exploitintel/cve-cwe-qwen3-8b.

Trained only on labels where NVD and the CNA agree after roll-up to CWE View-1003 — see the cve-cwe-consensus dataset.

Results (held-out test split, 6,802 rows)

Metric This model (32B) 8B variant
Exact-match 0.707 0.676
Micro-F1 0.729 0.702
Macro-F1 0.595 0.511

By difficulty (does the description name the weakness, or must it be inferred?):

Stratum n Exact-match Micro-F1
Easy (weakness named) 2,046 0.871 0.893
Hard (must infer) 4,756 0.636 0.657

Both models are scored identically; the 32B's gains are largest on macro-F1 (rare/long-tail CWEs) and the hard inference split.

Reading the numbers:

  • Macro-F1 is over the union of gold and predicted labels (118 = 117 gold + ~1 the model predicted outside the gold set), so 0.595 is a conservative figure. The low out-of-label count also means the model rarely hallucinates non-existent CWEs.
  • Exact-match has an inherent ceiling of ~98.3%: ~1.74% of the test set (273 groups / 1,205 rows) are identical descriptions mapped to different CWEs (e.g. a bare "Windows Kernel Elevation of Privilege Vulnerability"), which a description-only model cannot disambiguate.
  • Scores are on the capped/balanced test split (~30% "easy" rows), so they are not directly comparable to metrics measured on a different (e.g. natural-distribution) split.

Usage

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

mid = "exploitintel/cve-cwe-qwen3-32b"
tok = AutoTokenizer.from_pretrained(mid)
model = AutoModelForCausalLM.from_pretrained(mid, torch_dtype="auto", device_map="auto")

messages = [
    {"role": "system", "content": "You are a vulnerability analyst. Given a CVE description, "
                                   "reply with only the CWE ID(s) it maps to, comma-separated."},
    {"role": "user", "content": "A SQL injection vulnerability in the login endpoint allows an "
                                "unauthenticated attacker to execute arbitrary SQL via the username parameter."},
]
inputs = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
out = model.generate(inputs, max_new_tokens=32, do_sample=False)
print(tok.decode(out[0][inputs.shape[-1]:], skip_special_tokens=True))
# -> CWE-89

GGUF / Ollama

A Q4_K_M GGUF (~20 GB) is included in this repo for local runners — needs ~24 GB VRAM:

ollama run hf.co/exploitintel/cve-cwe-qwen3-32b:Q4_K_M

Set the same system prompt (/set system You are a vulnerability analyst...) so it returns bare CWE IDs.

Note: This Ollama command has not been verified end-to-end. This is a standard qwen3 model so the embedded template should apply normally — but if ollama run ignores the system prompt and produces rambling text instead of a bare CWE ID, supply an explicit ChatML Modelfile TEMPLATE as shown in the Qwen3.5-4B card.

Training

  • Base: Qwen/Qwen3-32B (trained 4-bit via unsloth/Qwen3-32B-unsloth-bnb-4bit)
  • Method: QLoRA (4-bit) with Unsloth, merged to 16-bit · released checkpoint: checkpoint-960 (final; eval loss declined monotonically through training)
  • Dataset: exploitintel/cve-cwe-consensus — 69,386 rows (55,810 / 6,774 / 6,802), majority CWEs capped at 2,500
  • Settings: 2 epochs · context 512 · LR 2e-4 · AdamW 8-bit · linear schedule · packing on · train-on-completions-only off · seed 3407
  • LoRA fine-tune, rank 16 (confirmed); adapter merged into the base. Exact LoRA alpha, batch size, and weight decay were not logged to the repo.

Prompt format

ChatML (Qwen3 standard). Fixed system prompt; the description is the only user input.

  • system: You are a vulnerability analyst. Given a CVE description, reply with only the CWE ID(s) it maps to, comma-separated.
  • user: the CVE description
  • assistant: CWE-79, CWE-80

Limitations

  • CWEs below the dataset's 50-example floor are not in the label space and won't be predicted.
  • Outputs CWE IDs as text; validate against the official CWE list.
  • English-only; descriptions only (no code, CVSS, or references).
  • A triage/assist aid, not an authoritative CWE assignment — human-review before acting.

License

Apache-2.0 (inherited from Qwen3-32B). Dataset derives from public upstreams (NVD, MITRE CVE/CWE).

Downloads last month
96
Safetensors
Model size
33B params
Tensor type
BF16
·
Inference Providers NEW
Input a message to start chatting with exploitintel/cve-cwe-qwen3-32b.

Model tree for exploitintel/cve-cwe-qwen3-32b

Base model

Qwen/Qwen3-32B
Quantized
(157)
this model

Dataset used to train exploitintel/cve-cwe-qwen3-32b