
LLM de raisonnement spécialisé pour la détection de vulnérabilités dans le code source en C/C++ et Python, avec construction de jeux de données, entraînement SFT/DPO et scripts de reproduction des résultats.
git lfs install # one-time setup
git clone https://github.com/ucsb-mlsec/VulnLLM-R.git
cd VulnLLM-R
# If you cloned before installing Git LFS, run: git lfs pull
conda create -n vulnscan python=3.11
conda activate vulnscan
pip install -e . -e ./vulscan/train/LLaMA-Factory -e ./vulscan/model_zoo
# generate VulnLLM-R-7B's results
python -m vulscan.test.test --output_dir results/test_data --dataset_path ./datasets/test/function_level/ ./datasets/test/repo_level/ --language python c java --model UCSB-SURFI/VulnLLM-R-7B --requests_per_minute 1000 --save --use_cot --batch_size 4 --tp 2 --vllm --max_tokens 8192 --random_cwe
python -m vulscan.test.test_hf \
--output_dir results/test_hf \
--hf_dataset UCSB-SURFI/VulnLLM-R-Test-Data \
--hf_split repo_level function_level \
--language c python java \
--model UCSB-SURFI/VulnLLM-R-7B \
--save --use_cot --vllm --tp 2
# [optional] generate other models' results with our shell script
# remember to add your API keys to .env file if you want to run commercial models
# use ./run_test.sh -h for more options
./vulscan/test/run_test.sh -o results/test_data -t 2 # -o means output directory, -t means tensor parallelism
./vulscan/test/run_test.sh -o results/test_data -M o3-mini # -M means model name, which runs only one model.
./vulscan/test/run_test.sh -o results/test_data -M gpt-5.4 -e high # -e sets reasoning effort (e.g., none/low/medium/high/xhigh)
./vulscan/test/run_test.sh -o results/test_data -M claude-opus-4-6 -e high
# [optional] draw plot to compare with other models
python plots/plot_language_comparison_models.py --results-dir results/test_data
python plots/plot_model_size_scatter.py --results-dir results/test_data # Note: Labels may overlap with scatter points. Adjust text positions manually if needed.
Nous fournissons également la version à raisonnement réduit des jeux de données distillés :
Fusionnez les jeux de données existants de détection de vulnérabilités au niveau des fonctions : PrimeVul [1], SecCodePLT [2], Juliet [3], Sven [4], et Arvo [5]. Parmi ces jeux de données, PrimeVul contient les fonctions les plus complexes. Nous créons deux ensembles d'entraînement : clean (sans PrimeVul) et noisy (avec PrimeVul), afin de pouvoir nous entraîner sur des jeux de données relativement simples et tester sur le jeu de données complexe PrimeVul. Notez que le fait de nommer l'ensemble d'entraînement avec PrimeVul « noisy » ne signifie pas que le jeu de données est bruité. C'est un nom relativement arbitraire que nous avons utilisé au départ.
vulscan/data_process/data_utils contient un ensemble de scripts pour traiter et fusionner les
jeux de données.
raw_to_us.py : Fusionner les données brutes dans notre jeu de données et supprimer les données redondantescheck_cwe_correct.py : Calculer la précision pour chaque catégorie de CWEgenerate_arvo_raw_data.py : Générer des données brutes structurées à partir du jeu de données arvoarvo_to_us.py : Reformater les données brutes structurées d'arvo au format de notre jeu de donnéessplit_good_bad_for_juliet.py : Extraire les données du jeu de données brut Juliet 1.3 et les convertir au format requis,
ce qui constitue une partie de notre clean_dataset cadd_sven_to_clean_dataset.py : Extraire les données du jeu de données Sven, formant une partie de notre clean dataset Csync_large_small.py : Synchroniser les modifications de noisy_dataset/large_train/c vers
noisy_dataset/small_train/cremove_testing_from_training.py : Ajouter le tag human à chaque donnée, ce qui signifie que le point a été vérifié par un
humain et utilisé comme donnée de test
- : Ajouter le champ related_cwe au jeu de données.Après avoir construit les jeux de données, nous générons des données de raisonnement pour notre ensemble d'entraînement.
Nous interrogeons les modèles de raisonnement DeepSeek-r1 et QwQ pour générer les données de raisonnement et filtrons celles avec de très
longues chaînes de raisonnement.
Le code pour générer les données de raisonnement se trouve dans vulscan/data_process/generate_reasoning et les données de raisonnement seront
enregistrées
dans datasets/reasoning_data.
cd vulscan/data_process/generate_reasoning
generate_reasoning/generate.py est le script principal pour générer les données de raisonnement.
Pour chaque point de données, il génère n échantillons de données de raisonnement et sélectionne celui avec la bonne réponse et la longueur la plus
courte.
Des exemples d'exécution avec les modèles QwQ et DeepSeek-r1 (en utilisant l'API together.AI, plus lente mais plus stable
que l'API officielle) sont les suivants :
python generate.py \
--tp 2 \
--dataset_type clean_dataset \
--batch_size 200 \
--n 8 \
--training_set train \
--model_name Qwen/QwQ-32B
# or
python generate.py \
--dataset_type noisy_dataset \
--batch_size 200 \
--n 8 \
--training_set small_train \
--model_name together-deepseek-reasoner \
--together_deepseek
Après avoir généré les données de raisonnement brutes, nous pouvons utiliser un autre modèle pour les résumer et les raccourcir sans casser la structure
python extract_reasoning.py
Nous pouvons également filtrer les données de raisonnement en fonction d'une certaine longueur avec generate_reasoning/filter.py ; num_processes
varie selon votre nombre de cœurs CPU.
python filter.py \
--dataset_type noisy_dataset \
--training_set small_train \
--model_name Qwen/QwQ-32B \
--filter_input_length 16000 \
--filter_all_length 32000 \
--num_processes 16 \
--filter_correct_only # for filtering wrong predictions
# --model_name together-deepseek-reasoner \
Enfin, nous devons reformater les données de raisonnement générées pour le modèle cible que nous allons entraîner (Qwen-Instruct).
python reformat_ds.py \
--dataset_type noisy_dataset \
--training_set small_train \
--model_name Qwen/QwQ-32B \
--filter_input_length 16000 \
--filter_all_length 32000 \
--push_to_hub \
--push_to_hub_organization secmlr \
--filter_correct_only
Pour le jeu de données DPO :
python generate_dpo.py \
--tp 2 --dataset_type clean_dataset \
--batch_size 200 --n 8 --training_set train \
--model secmlr/VD-QWQ-Clean-8k_qwen2_7B_full_sft_1e-5
référez-vous à vulscan/train/README.md pour plus de détails
les résultats seront enregistrés dans le répertoire results/test_qwen/results.json.
Si vous souhaitez reproduire nos résultats, vous pouvez exécuter la commande suivante :
# open-source model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python --model Qwen/Qwen2.5-7B-Instruct \
--requests_per_minute 100 --save --use_cot \
--use_policy --batch_size 4 --tp 2 --vllm --max_tokens 16384 \
--random_cwe
# api model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python --model o3-mini-2025-01-14 \
--requests_per_minute 100 --save --use_cot \
--use_policy --batch_size 4 --max_tokens 16384 --random_cwe
# local saved model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python \
--model vulscan/train/result/VD-QWQ-Clean-16k/qwen2_7B_full_sft_1e-5 \
--requests_per_minute 100 --save --use_cot --use_policy \
--batch_size 4 --tp 2 --vllm --max_tokens 16384 --random_cwe
# our model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python \
--model secmlr/VD-QWQ-Noisy-Small-8k_qwen2_7B_full_sft_1e-5 --revision aa3235b \
--requests_per_minute 100 --save --use_cot --use_policy \
--batch_size 4 --tp 2 --vllm --max_tokens 16384 \
--random_cwe # whether to randomize the order of cwe and related cwes
Après le test, les réponses des modèles et les performances seront enregistrées dans le répertoire results/test_data.
Si vous souhaitez calculer les performances à partir des réponses du modèle, vous pouvez exécuter la commande suivante :
python -m vulscan.test.test_existing_json \
--json_file results/test_data/datasets_test_test_clean__cot_c_policy_QwQ-32B-Preview.json # the results file
python generate_constitution.py --model gpt-4o --input_dir results/train --output_dir results/train/constitution
@article{nie2025vulnllmrspecializedreasoningllm,
title={VulnLLM-R: Specialized Reasoning LLM with Agent Scaffold for Vulnerability Detection},
author={Yuzhou Nie and Hongwei Li and Chengquan Guo and Ruizhe Jiang and Zhun Wang and Bo Li and Dawn Song and Wenbo Guo},
year={2025},
journal={arXiv preprint arXiv:2512.07533},
url={https://arxiv.org/abs/2512.07533},
}
data_utils.pydatasets/clean_dataset : les données d'entraînement sans PrimeVul
datasets/clean_dataset/python contient les données de SVEN et SecCodePLTdatasets/clean_dataset/c contient les données de Juliet et SVENdatasets/noisy_dataset
datasets/noisy_dataset/small_train : Contient les données d'entraînement de PrimeVul et SVEN avec des CWE sélectionnés (
nous utilisons les données PrimeVul de ce jeu de données comme entraînement)datasets/noisy_dataset/large_train : Contient les données d'entraînement de PrimeVul, SVEN et SecCodePLT avec
plus de CWE (ce jeu de données peut ensuite être utilisé pour entraîner des modèles plus grands)datasets/noisy_dataset/test : Un petit ensemble de test de PrimeVul vérifié par un humaindatasets/test
datasets/test/test_clean : Les données de test de SVEN, SecCodePLT et Juliet ; avec des CWE OOD qui ne font pas
partie de l'ensemble d'entraînementdatasets/test/test_primevul_pair : Les données de test originales de PrimeVulvulscan/data_process/data_utils/get_cwe_stat.py pour obtenir l'histogramme du jeu de données| Dataset | Language | Train/test | CWE | # Benign | # Vuln. | average length |
|---|
| Clean (seccodeplt) | Python | Train | 20 | 1281 | 1281 | 741 |
| Clean (juliet) | C/C++ | Train | 22 | 1716 | 1653 | 3689 |
| Hard (primevul filtered) | C/C++ | Train | 26 | 2717 | 2952 | 4689 |
| Long Context (Oss-fuzz) | C/C++ | Train | 3 | 475 | 604 | 12761 |
| Simple (seccodeplt) | Python | Test | 24 (6 ood) | 74 | 74 | 814 |
| Simple (juliet) | C/C++ | Test | 38 (14 ood) | 358 | 376 | 2575 |
| Hard (PrimeVul, SecLLMHolmes) | C/C++ | Test | 13 (5 ood) | 145 | 152 | 4545 |
| Long Context (Oss-fuzz) | C/C++ | Test | 3 (0 ood) | 0 | 320 | 18929 |
| primevul test (noisy) | C/C++ | Test | 56 (34 ood) | 421 | 422 | 5341 |