Делаю диплом Тема: AI-линтер безопасности с автоп...
تم الإنشاء في: ١١ مارس ٢٠٢٦
تمت الإجابة باستخدام GPT-5.4 Pro بواسطة Chat01
تم الإنشاء في: ١١ مارس ٢٠٢٦
تمت الإجابة باستخدام GPT-5.4 Pro بواسطة Chat01
Делаю диплом
Тема: AI-линтер безопасности с автопочинкой
детекцию уязвимостей делать отдельной моделью по коду (AST/DFG/CPG + GNN/Transformer);
генерацию патчей делать через небольшую open-weights LLM для кода, дообученную LoRA на патчах (diff’ах). Подойдут, например, StarCoder2 (3B/7B/15B) или Code Llama — обе семейства открытые и хорошо держат кодовые задачи. (Hugging Face)
Данные - есть как синтетические, так и реальные уязвимости/патчи:
SARD/Juliet (NIST): сотни тысяч тесткейс-программ с размеченными CWE; Juliet C/C++ v1.3 покрывает ~118 CWEs — идеально для “белых” бенчмарков и регрессии. (NIST)
Devign: датасет и baseline-подход на графах (GNN) для детекции уязвимых функций в реальном C-коде. (NeurIPS Papers)
Big-Vul: C/C++ с привязкой к CVE и коммитам-фиксам — удобно для обучения автопочинки на before/after. (GitHub)
MegaVul: крупный набор из десятков тысяч уязвимых/неуязвимых функций и тысяч фиксов. (GitHub)
Комбинация Juliet→для точности и Big-Vul/MegaVul→для реализма и патчей даёт устойчивый пайплайн.
В каком формате и как это будет работать (базовый сценарий)
Артефакты продукта:
CLI + локальная библиотека; по желанию — VS Code-расширение и pre-commit hook.
Вывод в SARIF (индустриальный JSON-стандарт для статанализа), чтобы “из коробки” открываться в IDE/GitHub. (OASIS Open)
Пример UX:
bash
ailint scan ./repo --lang c --rules cwe_top25 --out out.sarif
ailint show out.sarif --only HIGH
ailint fix --file src/http.c --line 120 --cwe CWE-78 --apply
ailint verify --build "make test" --timeout 300
Под капотом (архитектура):
Парсер/графы кода → строим CPG/AST/DFG (можно через Joern/Fraunhofer CPG). (GitHub)
Детектор → модель (GNN/Transformer по графам/секвенсам) помечает кандидат-строки/функции (пер-CWE). Базовая идея как в Devign. (NeurIPS Papers)
Автофикс → маленькая LLM по диффам: prompt = контекст уязвимости + CWE-гайд + “до/после” примеры из Big-Vul/MegaVul; генерим patch (unified diff), прогоняем компиляцию/тесты. (GitHub)
Вывод → SARIF + “путь данных/управления” до sink/source, ссылки на CWE, confidence.
Дообучать или писать с нуля?
Детектор: уместно дообучить (или обучить с нуля небольшую) модель на Devign/Big-Vul + собственные хард-негативы.
LLM-фиксатор: только дообучение LoRA open-модели (StarCoder2 / Code Llama) на небольшом корпусе security-патчей — этого достаточно для осмысленных предложений, а вычислительно это на порядок дешевле, чем тренировать foundation-модель. (Hugging Face)
Сравним с популярными статанализаторами:
CodeQL — мощный, но требует писать правила на QL и не делает “генерацию фиксов” по умолчанию. (GitHub Docs)
Semgrep — быстрые паттерны, простые правила, снова без ML-детекции глубокой семантики и без генерации патчей. (GitHub)
Твоя дифференциация:
Гибрид SAST+ML: правила для очевидных CWE + ML/CPG для сложных потоков данных.
Автопочинка: предлагаешь конкретный diff + краткое объяснение “почему безопаснее” и ссылку на CWE.
Полный офлайн: приватность кода — selling point (актуально на фоне коммерциализации рынка SAST). (The Wall Street Journal)
Совместимость: SARIF-вывод → открывается в IDE/CI без интеграционных плясок. (OASIS Open)
Адаптация под проект: довчишь детектор на истории коммитов самого репозитория (индивидуальный стиль кода/патчей).
Язык/область и минимальный scope
Чтобы уложиться в сроки и показать сильный результат, предлагаю стартовый фокус:
C/C++ и топ-CWE для памяти (CWE-787/119/190) + одна инъекционная категория (CWE-78).
Причины: лучшие датасеты (Devign/Big-Vul/Juliet), наглядные краши/эксплойтабельность, измеримый эффект после фикса. (NeurIPS Papers)
Метрики и проверка качества
Детектор: Precision/Recall/F1 per-CWE на Devign/Juliet (holdout), AUROC. (NeurIPS Papers)
Автопочинка:
Patch-apply rate (патч применился, проект собрался),
Tests pass rate (до/после),
Security plausibility (экспертная проверка на подмножестве),
Time-to-fix и token-cost (для отчёта).
Отчётность: SARIF + линк на CWE и краткое объяснение (LLM-reasoning) — удобно для защиты проекта. (OASIS Open)
Быстрый план работ (8–10 недель)
Нед. 1–2: сбор датасетов (Juliet + Devign + Big-Vul), конвертация в единый формат (функция/метка/CWE/diff). (NIAID Data Ecosystem Discovery Portal)
Нед. 3: CPG-экстракция и baseline-детектор (логистическая регрессия/маленький Transformer по токенам) → sanity F1. (GitHub)
Нед. 4–5: GNN/Transformer-детектор по CPG/DFG + hard-negative mining.
Нед. 6: LoRA-довч LLM на патчах Big-Vul/MegaVul, формат вывода — unified diff. (GitHub)
Нед. 7: SARIF-экспорт + CLI/VSCode-плагин. (OASIS Open)
Нед. 8–9: оффлайн-бенчмарки на Juliet/репо-кейсы, абляции. (NIAID Data Ecosystem Discovery Portal)
Нед. 10: оформление диплома (методы, эксперименты, ограничения, этика).
Текущий прогресс:
!cp -r /kaggle/input/models/renesta/lintbs8/pytorch/default/1/ailint_diploma_starter /kaggle/working/ailint_diploma_starter
%cd /kaggle/working/ailint_diploma_starter/ailint_diploma_starter
!python prepare_unified.py --out_dir data/unified
--use_juliet --use_devign --use_bigvul --use_megavul
--max_megavul 200000
!cp -r /kaggle/input/models/renesta/lintbs8/pytorch/default/1/ailint_diploma_starter /kaggle/working/ailint_diploma_starter
%cd /kaggle/working/ailint_diploma_starter/ailint_diploma_starter
!python prepare_unified.py --out_dir data/unified
--use_juliet --use_devign --use_bigvul --use_megavul
--max_megavul 200000
!python train_detect.py
--data_dir data/unified/detect
--model_name microsoft/codebert-base
--output_dir runs/detect_codebert
--epochs 1
--train_bs 8 --eval_bs 8
--max_length 256
--eval_steps 0
--fp16
--max_train_samples 100000
--max_eval_samples 10000
13:22:41 | INFO | ailint.train_detect | [env] {"HF_DATASETS_CACHE": null, "HF_HOME": null, "HF_HUB_CACHE": null, "HF_TOKEN_set": false, "accelerate": "1.12.0", "bitsandbytes": null, "cuda_available": true, "cuda_version": "12.6", "cudnn_version": 91002, "datasets": "4.0.0", "gpu_count": 2, "gpu_name_0": "Tesla T4", "numpy": "2.0.2", "peft": "0.18.1", "platform": "Linux-6.6.113+-x86_64-with-glibc2.35", "python": "3.12.12 (main, Oct 10 2025, 08:52:57) [GCC 11.4.0]", "sklearn": "1.6.1", "torch": "2.9.0+cu126", "transformers": "5.2.0"}
13:22:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
13:22:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/config.json "HTTP/1.1 200 OK"
13:22:41 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/config.json "HTTP/1.1 200 OK"
config.json: 100%|██████████████████████████████| 498/498 [00:00<00:00, 237kB/s]
13:22:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/tokenizer_config.json "HTTP/1.1 307 Temporary Redirect"
13:22:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/tokenizer_config.json "HTTP/1.1 200 OK"
13:22:41 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/tokenizer_config.json "HTTP/1.1 200 OK"
tokenizer_config.json: 100%|██████████████████| 25.0/25.0 [00:00<00:00, 115kB/s]
13:22:41 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/tree/main/additional_chat_templates?recursive=false&expand=false "HTTP/1.1 404 Not Found"
13:22:41 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/tree/main?recursive=true&expand=false "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/vocab.json "HTTP/1.1 307 Temporary Redirect"
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
13:22:42 | WARNING | huggingface_hub.utils._http | Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/vocab.json "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/vocab.json "HTTP/1.1 200 OK"
vocab.json: 899kB [00:00, 25.9MB/s]
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/merges.txt "HTTP/1.1 307 Temporary Redirect"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/merges.txt "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/merges.txt "HTTP/1.1 200 OK"
merges.txt: 456kB [00:00, 95.9MB/s]
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/tokenizer.json "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/added_tokens.json "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/special_tokens_map.json "HTTP/1.1 307 Temporary Redirect"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/special_tokens_map.json "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/special_tokens_map.json "HTTP/1.1 200 OK"
special_tokens_map.json: 100%|█████████████████| 150/150 [00:00<00:00, 1.03MB/s]
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/chat_template.jinja "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/config.json "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/adapter_config.json "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/microsoft/codebert-base/3b0952feddeffad0063f274080e3c23d75e7eb39/config.json "HTTP/1.1 200 OK"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/model.safetensors "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/model.safetensors.index.json "HTTP/1.1 404 Not Found"
13:22:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/pytorch_model.bin "HTTP/1.1 302 Found"
13:22:42 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/xet-read-token/3b0952feddeffad0063f274080e3c23d75e7eb39 "HTTP/1.1 200 OK"
pytorch_model.bin: 100%|██████████████████████| 499M/499M [00:01<00:00, 291MB/s]
13:22:44 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/main/model.safetensors "HTTP/1.1 404 Not Found"
13:22:44 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base "HTTP/1.1 200 OK"
13:22:44 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/commits/main "HTTP/1.1 200 OK"
13:22:44 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/discussions?p=0 "HTTP/1.1 200 OK"
13:22:45 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/commits/refs%2Fpr%2F9 "HTTP/1.1 200 OK"
13:22:45 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/refs%2Fpr%2F9/model.safetensors.index.json "HTTP/1.1 404 Not Found"
13:22:45 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/microsoft/codebert-base/resolve/refs%2Fpr%2F9/model.safetensors "HTTP/1.1 302 Found"
Loading weights: 12%| | 24/197 [00:00<00:00, 3463.03it/s, Materializing param=r13:22:45 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/microsoft/codebert-base/xet-read-token/99d7ef814601faaf7bdc2f774ffa7dade4f4d828 "HTTP/1.1 200 OK"
Loading weights: 13%|▏| 26/197 [00:00<00:00, 2972.01it/s, Materializing param=r
Loading weights: 100%|█| 197/197 [00:00<00:00, 3767.54it/s, Materializing param=
RobertaForSequenceClassification LOAD REPORT from: microsoft/codebert-base
Key | Status |
---------------------------+------------+-
pooler.dense.bias | UNEXPECTED |
pooler.dense.weight | UNEXPECTED |
classifier.out_proj.weight | MISSING |
classifier.out_proj.bias | MISSING |
classifier.dense.weight | MISSING |
classifier.dense.bias | MISSING |
Notes:
model.safetensors: 6%|█▏ | 29.1M/499M [00:00<00:10, 42.7MB/s]
model.safetensors: 19%|████ | 96.1M/499M [00:01<00:03, 109MB/s]
model.safetensors: 46%|██████████▏ | 230M/499M [00:01<00:01, 258MB/s]
model.safetensors: 60%|█████████████ | 297M/499M [00:01<00:00, 290MB/s]
model.safetensors: 100%|██████████████████████| 499M/499M [00:01<00:00, 333MB/s]
13:22:47 | INFO | ailint.train_detect | [data:fallback] indexed=data/unified/detect/train.jsonl kept=100000 scanned=100000 (2.0s) only_lang=None max_samples=100000
13:22:47 | INFO | ailint.train_detect | [data:fallback] indexed=data/unified/detect/validation.jsonl kept=10000 scanned=10000 (0.1s) only_lang=None max_samples=10000
13:22:48 | INFO | ailint.train_detect | [data:fallback] indexed=data/unified/detect/test.jsonl kept=98264 scanned=98264 (1.2s) only_lang=None max_samples=None
13:22:48 | INFO | ailint.train_detect | [data] fallback ok. sizes: train=100000 val=10000 test=98264
13:22:49 | INFO | ailint.train_detect | [run] {"backend": "jsonl_fallback", "bf16": false, "data_dir": "data/unified/detect", "epochs": 1, "eval_bs": 8, "eval_steps": 0, "fp16": true, "grad_accum": 1, "logging_steps": 50, "lr": 2e-05, "max_length": 256, "model_name": "microsoft/codebert-base", "only_lang": null, "seed": 42, "train_bs": 8, "weight_decay": 0.01}
13:22:49 | INFO | ailint.train_detect | [train] start
0%| | 0/6250 [00:00<?, ?it/s]/usr/local/lib/python3.12/dist-packages/torch/nn/parallel/_functions.py:71: UserWarning: Was asked to gather along dimension 0, but all input tensors were scalars; will instead unsqueeze and return a vector.
warnings.warn(
{'loss': '1.391', 'grad_norm': '3.889', 'learning_rate': '1.984e-05', 'epoch': '0.008'}
{'loss': '1.294', 'grad_norm': '13.38', 'learning_rate': '1.968e-05', 'epoch': '0.016'}
{'loss': '0.9218', 'grad_norm': '9.888', 'learning_rate': '1.952e-05', 'epoch': '0.024'}
{'loss': '0.8774', 'grad_norm': '3.916', 'learning_rate': '1.936e-05', 'epoch': '0.032'}
{'loss': '0.7766', 'grad_norm': '4.447', 'learning_rate': '1.92e-05', 'epoch': '0.04'}
{'loss': '0.8193', 'grad_norm': '1.366', 'learning_rate': '1.904e-05', 'epoch': '0.048'}
{'loss': '0.7121', 'grad_norm': '3.079', 'learning_rate': '1.888e-05', 'epoch': '0.056'}
{'loss': '0.7027', 'grad_norm': '6.448', 'learning_rate': '1.872e-05', 'epoch': '0.064'}
{'loss': '0.7273', 'grad_norm': '7.024', 'learning_rate': '1.856e-05', 'epoch': '0.072'}
{'loss': '0.7262', 'grad_norm': '1.581', 'learning_rate': '1.84e-05', 'epoch': '0.08'}
{'loss': '0.721', 'grad_norm': '4.277', 'learning_rate': '1.824e-05', 'epoch': '0.088'}
{'loss': '0.6959', 'grad_norm': '6.259', 'learning_rate': '1.808e-05', 'epoch': '0.096'}
{'loss': '0.7449', 'grad_norm': '8.887', 'learning_rate': '1.792e-05', 'epoch': '0.104'}
{'loss': '0.6851', 'grad_norm': '5.285', 'learning_rate': '1.776e-05', 'epoch': '0.112'}
{'loss': '0.6527', 'grad_norm': '1.02', 'learning_rate': '1.76e-05', 'epoch': '0.12'}
................
{'loss': '0.6068', 'grad_norm': '1.483', 'learning_rate': '3.2e-09', 'epoch': '1'}
100%|███████████████████████████████████████| 6250/6250 [55:13<00:00, 1.88it/s]
0%| | 0/625 [00:00<?, ?it/s]
0%|▏ | 2/625 [00:00<00:58, 10.60it/s]
1%|▎ | 4/625 [00:00<01:33, 6.64it/s]
1%|▎ | 5/625 [00:00<01:40, 6.14it/s]
.....
100%|████████████████████████████████████████▊| 623/625 [01:57<00:00, 5.29it/s]
100%|████████████████████████████████████████▉| 624/625 [01:58<00:00, 5.32it/s]
{'eval_loss': '0.6341', 'eval_accuracy': '0.7752', 'eval_precision': '0.9045', 'eval_recall': '0.6154', 'eval_f1': '0.7324', 'eval_auroc': '0.8994', 'eval_runtime': '118.4', 'eval_samples_per_second': '84.42', 'eval_steps_per_second': '5.277', 'epoch': '1'}
100%|███████████████████████████████████████| 6250/6250 [57:12<00:00, 1.88it/s]
100%|█████████████████████████████████████████| 625/625 [01:58<00:00, 5.45it/s]
Writing model shards: 0%| | 0/1 [00:00<?, ?it/s]
Writing model shards: 100%|███████████████████████| 1/1 [00:00<00:00, 1.11it/s]
There were missing keys in the checkpoint model loaded: ['roberta.embeddings.LayerNorm.weight', 'roberta.embeddings.LayerNorm.bias', 'roberta.encoder.layer.0.attention.output.LayerNorm.weight', 'roberta.encoder.layer.0.attention.output.LayerNorm.bias', 'roberta.encoder.layer.0.output.LayerNorm.weight', 'roberta.encoder.layer.0.output.LayerNorm.bias', 'roberta.encoder.layer.1.attention.output.LayerNorm.weight', 'roberta.encoder.layer.1.attention.output.LayerNorm.bias', 'roberta.encoder.layer.1.output.LayerNorm.weight', 'roberta.encoder.layer.1.output.LayerNorm.bias', 'roberta.encoder.layer.2.attention.output.LayerNorm.weight', 'roberta.encoder.layer.2.attention.output.LayerNorm.bias', 'roberta.encoder.layer.2.output.LayerNorm.weight', 'roberta.encoder.layer.2.output.LayerNorm.bias', 'roberta.encoder.layer.3.attention.output.LayerNorm.weight', 'roberta.encoder.layer.3.attention.output.LayerNorm.bias', 'roberta.encoder.layer.3.output.LayerNorm.weight', 'roberta.encoder.layer.3.output.LayerNorm.bias', 'roberta.encoder.layer.4.attention.output.LayerNorm.we....
{'train_runtime': '3435', 'train_samples_per_second': '29.11', 'train_steps_per_second': '1.82', 'train_loss': '0.6609', 'epoch': '1'}
100%|███████████████████████████████████████| 6250/6250 [57:14<00:00, 1.82it/s]
Writing model shards: 100%|███████████████████████| 1/1 [00:00<00:00, 1.14it/s]
14:20:05 | INFO | ailint.train_detect | [save] best_model=runs/detect_codebert/best_model
/usr/local/lib/python3.12/dist-packages/torch/nn/parallel/_functions.py:71: UserWarning: Was asked to gather along dimension 0, but all input tensors were scalars; will instead unsqueeze and return a vector.
warnings.warn(
100%|███████████████████████████████████████| 6142/6142 [19:07<00:00, 5.35it/s]
14:39:12 | INFO | ailint.train_detect | [test] {"epoch": 1.0, "test_accuracy": 0.6231376699503378, "test_auroc": 0.7559748435556924, "test_f1": 0.4247724377893069, "test_loss": 1.1104133129119873, "test_precision": 0.33481071551006414, "test_recall": 0.5808411214953271, "test_runtime": 1147.4858, "test_samples_per_second": 85.634, "test_steps_per_second": 5.353}
14:39:12 | INFO | ailint.train_detect | DONE
!python train_repair_lora.py
--data_dir data/unified/repair
--base_model bigcode/starcoder2-3b
--output_dir runs/repair_starcoder2_3b_lora_quick
--quick_1h
08:36:18 | INFO | ailint.train_repair_lora | [quick_1h] enabled -> max_length=1024 grad_accum=4 max_steps=300 max_train_samples=10000 max_test_samples=500 device_map=single quant=8bit
08:36:18 | INFO | ailint.train_repair_lora | [env] {"CUDA_VISIBLE_DEVICES": "0", "DISABLE_ADDMM_CUDA_LT": "1", "HF_DATASETS_CACHE": null, "HF_HOME": null, "HF_HUB_CACHE": null, "HF_TOKEN_set": false, "accelerate": "1.12.0", "bitsandbytes": "0.49.2", "cuda_available": true, "cuda_version": "12.6", "cudnn_version": 91002, "gpu_count": 1, "gpu_name_0": "Tesla T4", "peft": "0.18.1", "platform": "Linux-6.6.113+-x86_64-with-glibc2.35", "python": "3.12.12 (main, Oct 10 2025, 08:52:57) [GCC 11.4.0]", "torch": "2.9.0+cu126", "transformers": "5.2.0"}
08:36:18 | INFO | ailint.train_repair_lora | [run] {"base_model": "bigcode/starcoder2-3b", "data_dir": "data/unified/repair", "dataloader_num_workers": 1, "dataloader_pin_memory": false, "device_map": "single", "disable_addmm_cuda_lt": true, "epochs": 1.0, "eval_bs": 1, "eval_steps": 200, "force_single_gpu": true, "grad_accum": 4, "logging_steps": 50, "lora_alpha": 32, "lora_dropout": 0.05, "lora_r": 16, "lr": 0.0002, "max_eval_samples": 1000, "max_length": 1024, "max_steps": 300, "max_test_samples": 500, "max_train_samples": 10000, "no_eval": true, "no_save": true, "only_lang": null, "output_dir": "runs/repair_starcoder2_3b_lora_quick", "quant": "8bit", "quick_1h": true, "save_steps": 200, "seed": 42, "train_bs": 1}
08:36:18 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
08:36:18 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
08:36:18 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/tokenizer_config.json "HTTP/1.1 307 Temporary Redirect"
08:36:18 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/tokenizer_config.json "HTTP/1.1 200 OK"
08:36:18 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/bigcode/starcoder2-3b/tree/main/additional_chat_templates?recursive=false&expand=false "HTTP/1.1 404 Not Found"
08:36:19 | INFO | httpx | HTTP Request: GET https://huggingface.co/api/models/bigcode/starcoder2-3b/tree/main?recursive=true&expand=false "HTTP/1.1 200 OK"
08:36:19 | INFO | ailint.train_repair_lora | [model] loading 8bit (bitsandbytes)
08:36:19 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
08:36:19 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
08:36:19 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
08:36:19 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
Loading weights: 100%|█| 483/483 [00:17<00:00, 27.60it/s, Materializing param=mo
08:36:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/generation_config.json "HTTP/1.1 404 Not Found"
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
08:36:41 | WARNING | huggingface_hub.utils._http | Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
08:36:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
08:36:41 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
08:36:42 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/custom_generate/generate.py "HTTP/1.1 404 Not Found"
/usr/local/lib/python3.12/dist-packages/bitsandbytes/autograd/_functions.py:123: UserWarning: MatMul8bitLt: inputs will be cast from torch.float32 to float16 during quantization
warnings.warn(f"MatMul8bitLt: inputs will be cast from {A.dtype} to float16 during quantization")
08:36:42 | INFO | ailint.train_repair_lora | [sanity] dry forward ok on device=cuda:0
08:36:42 | INFO | ailint.train_repair_lora | [model] loaded quant=8bit
08:36:42 | INFO | ailint.train_repair_lora | [ckpt] gradient_checkpointing_enable(use_reentrant=False)
08:36:42 | INFO | ailint.train_repair_lora | [lora] target_modules=['q_proj', 'k_proj', 'v_proj', 'o_proj']
trainable params: 9,093,120 || all params: 3,039,464,448 || trainable%: 0.2992
08:36:42 | INFO | ailint.train_repair_lora | [data] data/unified/repair/train.jsonl kept=10000 scanned=10001 only_lang=None max_samples=10000
08:36:43 | INFO | ailint.train_repair_lora | [data] data/unified/repair/validation.jsonl kept=1000 scanned=1001 only_lang=None max_samples=1000
08:36:43 | INFO | ailint.train_repair_lora | [data] data/unified/repair/test.jsonl kept=500 scanned=501 only_lang=None max_samples=500
08:36:43 | INFO | ailint.train_repair_lora | [train] start
The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'pad_token_id': 0}.
0%| | 0/300 [00:00<?, ?it/s]/usr/local/lib/python3.12/dist-packages/bitsandbytes/autograd/_functions.py:123: UserWarning: MatMul8bitLt: inputs will be cast from torch.float32 to float16 during quantization
warnings.warn(f"MatMul8bitLt: inputs will be cast from {A.dtype} to float16 during quantization")
{'loss': '0.8536', 'grad_norm': '0.2047', 'learning_rate': '0.000168', 'epoch': '0.02'}
{'loss': '0.2825', 'grad_norm': '0.2318', 'learning_rate': '0.0001347', 'epoch': '0.04'}
{'loss': '0.2129', 'grad_norm': '0.181', 'learning_rate': '0.0001013', 'epoch': '0.06'}
{'loss': '0.1588', 'grad_norm': '0.319', 'learning_rate': '6.8e-05', 'epoch': '0.08'}
{'loss': '0.1592', 'grad_norm': '0.202', 'learning_rate': '3.467e-05', 'epoch': '0.1'}
{'loss': '0.1449', 'grad_norm': '0.1346', 'learning_rate': '1.333e-06', 'epoch': '0.12'}
{'train_runtime': '3326', 'train_samples_per_second': '0.361', 'train_steps_per_second': '0.09', 'train_loss': '0.302', 'epoch': '0.12'}
100%|█████████████████████████████████████████| 300/300 [55:26<00:00, 11.09s/it]
09:32:09 | INFO | ailint.train_repair_lora | [train] done (3326.6s)
09:32:09 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
09:32:09 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
09:32:10 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/bigcode/starcoder2-3b/resolve/main/config.json "HTTP/1.1 307 Temporary Redirect"
09:32:10 | INFO | httpx | HTTP Request: HEAD https://huggingface.co/api/resolve-cache/models/bigcode/starcoder2-3b/733247c55e3f73af49ce8e9c7949bf14af205928/config.json "HTTP/1.1 200 OK"
09:32:10 | INFO | ailint.train_repair_lora | [save] adapter=runs/repair_starcoder2_3b_lora_quick/adapter tokenizer=runs/repair_starcoder2_3b_lora_quick/tokenizer
100%|█████████████████████████████████████████| 500/500 [05:26<00:00, 1.53it/s]
09:37:37 | INFO | ailint.train_repair_lora | [test] {"epoch": 0.12, "perplexity": NaN, "test_loss": NaN, "test_runtime": 326.7185, "test_samples_per_second": 1.53, "test_steps_per_second": 1.53}
Saved adapter to: runs/repair_starcoder2_3b_lora_quick/adapter
Test metrics: {'test_loss': nan, 'test_runtime': 326.7185, 'test_samples_per_second': 1.53, 'test_steps_per_second': 1.53, 'epoch': 0.12, 'perplexity': nan}
code = r'''#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Vulnerable: CWE-78 OS Command Injection */
int run_ls(const char *user_arg) {
char cmd[256];
snprintf(cmd, sizeof(cmd), "ls %s", user_arg);
return system(cmd);
}
int main(int argc, char **argv) {
if (argc < 2) return 1;
return run_ls(argv[1]);
}
'''
with open("/kaggle/working/ailint_diploma_starter/ailint_diploma_starter/sample.c", "w", encoding="utf-8") as f:
f.write(code)
print("wrote sample.c")
%cd /kaggle/working/ailint_diploma_starter/ailint_diploma_starter
!python infer_demo.py
--code_file sample.c
--cwe CWE-78
--lang c
--det_model_dir runs/detect_codebert/best_model
--patch_base_model bigcode/starcoder2-3b
--patch_adapter_dir runs/repair_starcoder2_3b_lora_quick/adapter
/kaggle/working/ailint_diploma_starter/ailint_diploma_starter
Loading weights: 100%|█| 201/201 [00:00<00:00, 1982.06it/s, Materializing param=
[detector] vuln_prob=0.5015
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
model.safetensors: 100%|████████████████████| 12.1G/12.1G [00:38<00:00, 319MB/s]
Loading weights: 100%|█| 483/483 [00:11<00:00, 41.50it/s, Materializing param=mo
[patcher] unified diff:
--- a/function.c
+++ b/function.c
@@ -17,12 +17,12 @@
/* Vulnerable: CWE-78 OS Command Injection */
int run_ls(const char *user_arg) {
char cmd[256];
snprintf(cmd, sizeof(cmd), "ls %s", user_arg);
char cmd[256] = "ls ";
strcat(cmd, user_arg);
return system(cmd);
}
-int main(int argc, char **argv) {
+int main(int argc, char **argv) {
if (argc < 2) return 1;
return run_ls(argv[1]);
}
хочу дообучить позднее(поставлю обучаться на день), сейчас нужно допилить прогу до полностью рабочего проекта.
Да. У тебя сейчас проблема не в том, что “модели ещё слабые”, а в том, что не замкнут продуктовый контур.
Сейчас у тебя уже есть база:
Но полностью рабочий проект появится не после ещё суток обучения, а после того, как ты закроешь 4 узких места: локализация, верификация, безопасный отбор патчей, CLI/SARIF.
У тебя:
Это не “всё плохо”. Это означает: ранжировать подозрительные функции модель уже умеет, но:
Для MVP этого достаточно.
test_loss = NaN, perplexity = NaN — почти наверняка у тебя в части eval/test примеров после токенизации получается, что все labels = -100, то есть целевой diff обрезан, и loss считается по пустому множеству.
То есть проблема, скорее всего, не в том, что LoRA “не учится”, а в том, что dataset encoding для SFT написан небезопасно.
На примере CWE-78 он выдал:
cchar cmd[256] = "ls "; strcat(cmd, user_arg); return system(cmd);
Это не фикс. Он убрал snprintf, но оставил:
Значит, сейчас у тебя нет security-gate, который запрещает принимать такие патчи.
Пока нет контура:
generate patch -> apply -> compile -> tests -> re-scan -> reject unsafe diff
инструмент нельзя считать “рабочим”.
Я бы шёл в таком порядке.
Не пытайся сейчас добить всё сразу.
Для рабочего дипломного MVP хватит:
VS Code extension, pre-commit, Joern/CPG/GNN — это потом или в “future work”.
Правильная архитектура для защиты сейчас такая:
system, popenstrcpy, strcat, sprintf, getsmalloc(n*m) / индексные операции и т.п.git apply --checkgit applygcc -fsyntax-only или project buildmake test / пользовательская командаЭто уже выглядит как полноценный офлайн security tool, даже если модель ещё не идеальна.
train_repair_lora.pyСейчас тебе надо гарантировать: в каждом sample есть хотя бы несколько target tokens, по которым считается loss.
Схема должна быть такой:
pythondef encode_sft_sample(tokenizer, prompt, target, max_length=1024, min_target_tokens=8): target_ids = tokenizer( target + (tokenizer.eos_token or ""), add_special_tokens=False ).input_ids if len(target_ids) < min_target_tokens: return None # Всегда резервируем место под target max_prompt_len = max_length - len(target_ids) if max_prompt_len <= 0: # если target слишком длинный, режем target, но не убиваем его полностью target_ids = target_ids[: max_length // 2] max_prompt_len = max_length - len(target_ids) prompt_ids = tokenizer( prompt, add_special_tokens=False, truncation=True, max_length=max_prompt_len ).input_ids input_ids = prompt_ids + target_ids labels = [-100] * len(prompt_ids) + target_ids attention_mask = [1] * len(input_ids) if not any(x != -100 for x in labels): return None return { "input_ids": input_ids, "attention_mask": attention_mask, "labels": labels, }
И обязательно:
Ещё важно: для repair сейчас perplexity почти бесполезна. Тебе нужны другие метрики:
infer_demo.py не принимай первый попавшийся diffНужен режим “generate candidates + verify”.
Логика:
pythoncandidates = generate_n_diffs(..., n=4) for diff in candidates: ok, reason = security_validate(diff, cwe) if not ok: continue if not git_apply_check(diff): continue if not build_ok(diff): continue if not detector_score_decreased(diff): continue return diff return rule_based_fallback_or_manual_review
Для патчей лучше не делать сильно случайную генерацию. Нормально:
temperature=0.1 или вообще do_sample=Falsenum_beams=4num_return_sequences=4max_new_tokens=256..384Это must-have.
Примерно так:
pythonBANNED_BY_CWE = { "CWE-78": [ r"\bsystem\s*\(", r"\bpopen\s*\(", r"/bin/sh", r"sh\s+-c", ], "CWE-120": [ r"\bstrcpy\s*\(", r"\bstrcat\s*\(", r"\bsprintf\s*\(", r"\bgets\s*\(", ], } def security_validate(patched_code: str, cwe: str): for pat in BANNED_BY_CWE.get(cwe, []): if re.search(pat, patched_code): return False, f"banned sink remains: {pat}" return True, "ok"
То есть твой текущий патч для CWE-78 verifier должен сразу отбрасывать.
Для диплома это не минус, а плюс. Ты как раз показываешь гибридный SAST+ML.
На твоём примере безопасная правка должна убирать shell вообще.
Пример класса патча:
c#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int run_ls(const char *user_arg) { pid_t pid = fork(); if (pid < 0) return -1; if (pid == 0) { execlp("ls", "ls", "--", user_arg, (char *)NULL); _exit(127); } int status = 0; if (waitpid(pid, &status, 0) < 0) return -1; return status; }
Почему это безопаснее:
user_arg идёт отдельным аргументом,-- не даёт трактовать пользовательскую строку как опцию ls.Именно такой патч verifier должен принимать, а system(cmd) — отвергать.
Сейчас 0.5015 как trigger — это слишком сыро.
Сделай после тренировки отдельный sweep по validation:
pythondef best_threshold(y_true, y_prob): best_t, best_f1 = 0.5, -1 for t in np.linspace(0.05, 0.95, 37): pred = (y_prob >= t).astype(int) f1 = f1_score(y_true, pred) if f1 > best_f1: best_t, best_f1 = t, f1 return float(best_t), float(best_f1)
Сохраняй threshold.json рядом с моделью и используй его в scan.
Это быстро поднимет практическую полезность без нового обучения.
У тебя detector пока фактически function-level. Чтобы проект стал рабочим, не нужно срочно обучать line-level model.
Сделай так:
Для MVP этого более чем достаточно.
textailint/ cli.py scan.py detect.py localize.py patch.py patch_rules.py verify.py sarif.py utils.py tests/ test_scan_cwe78.py test_fix_rejects_bad_patch.py test_end2end_cwe78.py pyproject.toml
pyproject.tomlСделай консольный entrypoint:
toml[project.scripts] ailint = "ailint.cli:app"
CLI удобнее всего на typer или обычном argparse.
Минимум вот эти:
bashailint scan ./repo --lang c --out out.sarif ailint show out.sarif --only HIGH ailint fix --from-sarif out.sarif --id 0 --apply ailint verify --build "make test" --timeout 300
Если fix пока сложен по --id, можно временно оставить:
bashailint fix --file sample.c --line 8 --cwe CWE-78 --apply
Но внутри он всё равно должен:
verify.pysecurity_validate()git apply --check + compile checkscan -> SARIFshowfix --applylabels == -100Сейчас не трать время на:
Для диплома сильнее выглядит:
Очень сильная формулировка:
Система использует гибридный подход: ML-модель ранжирует уязвимые функции, rule/AST-локализатор находит точную проблемную строку, а модуль автопочинки генерирует несколько патчей и пропускает их через верификатор (apply/build/test/re-scan/security gates). Это делает автопочинку безопаснее, чем прямое принятие ответа LLM.
Это зрелая инженерная архитектура, а не “попросили модель написать diff”.
Когда продуктовый контур будет готов, тогда уже запускай долгий train:
Сейчас не “докручивай модель”, а собери рабочий security pipeline вокруг уже имеющихся моделей: detector как ranker, rules для локализации, LLM как candidate generator, verifier как главный судья.
Пришли infer_demo.py, train_repair_lora.py и cli.py — разложу это уже в точные diff’ы по файлам.
то есть ты мне сейчас предлагаешь все же добавить модули для улучшения точности? А не лучше просто реально нормально обучить модель, а остальное - cli интеграция?
Нет — я не предлагаю сейчас тащить тяжёлые модули ради “ещё чуть-чуть точности”. Я предлагаю добавить минимальные модули, без которых проект не выглядит рабочим продуктом.
То есть разделение такое:
scanfixverifySARIFПочему я на этом настаиваю: у тебя уже есть доказательство, что CLI-обвязка вокруг одной модели не спасает. Твой текущий fix на CWE-78 выдал патч, который всё ещё уязвим. Если ты просто “нормально дообучишь” модель и завернёшь это в CLI, на защите тебе могут показать ровно такой кейс — и получится, что инструмент автоматически предлагает плохой фикс. Это гораздо опаснее, чем средний F1 у детектора.
Иначе говоря:
Для диплома второе важнее.
Я бы делал так:
довести продуктовый контур до состояния:
scan -> alert -> fix candidate -> apply/build/test/re-scan -> accept/reject
Это не “улучшение точности”. Это делает систему пригодной к использованию.
поставить на ночь/на день:
Такой порядок безопаснее.
Мой выбор был бы такой:
лучше рабочий end-to-end pipeline с текущими моделями, чем хорошо обученная модель без верификации.
Потому что:
Не “или-или”, а вот так:
scan с выводом в SARIFfix с генерацией diffverify:
git apply --checkЭто идеальная стратегия именно для тебя: архитектура уже будет готова, и новые веса просто улучшат качество.
Сейчас тебе не нужно выбирать между:
Тебе нужно выбрать между:
Я за второе.
Очень коротко:
Если хочешь, я могу прямо следующим сообщением расписать тебе минимальный MVP на 3–4 файла, который реально успеешь добить: что именно должно быть в scan.py, fix.py, verify.py, и что можно вообще не трогать до обучения.
хорошо, давай пойдем по твоему варианту. Прикладываю существующий код, будь осторожен с возможными правками, я как-то тебя предупреждал в другой переписке с проблемами версионности HF и прочих модулей, но ты все равно что-то поправил и пришлось искать другие классы и функции с нужными переменными и их количеством, так что будь аккуратнее. Остальной код допиши и структурируй сам
Готово. Собрал чистую версию проекта с рабочим MVP вокруг твоих текущих моделей и скриптов: ailint_diploma_starter_completed.zip
Что сделал:
train_detect.py, train_repair_lora.py, prepare_unified.py, infer_demo.py, чтобы не ломать совместимость по transformers/peft/HF;ailint/ с продуктовым контуром:
scanshowfixverifyailint_cli.py и pyproject.toml, чтобы можно было запускать либо python ailint_cli.py ..., либо после pip install -e . командой ailint ...;README.md под новый workflow.Что внутри нового MVP:
ailint/cli.py — CLIailint/engine.py — orchestrationailint/detect.py — обёртка над detector modelailint/patcher.py — LoRA patch generation wrapperailint/rules.py — правила, локализация, безопасные rule-based фиксыailint/verify.py — security/syntax/build verificationailint/sarif.py — SARIF экспорт/импортailint/c_lang.py — лёгкое выделение функций C/C++Что уже проверил руками:
scan -> show -> fix -> verify на кейсе CWE-78 проходит end-to-end;scan -> show -> fix -> verify на кейсе с strcpy тоже проходит;--file/--line, и через --from-sarif --id.Как запускать:
bashpip install -r requirements.txt python ailint_cli.py scan ./repo --lang c --out out.sarif --det_model_dir runs/detect_codebert/best_model python ailint_cli.py show out.sarif --only HIGH python ailint_cli.py fix --from-sarif out.sarif --id 0 --apply python ailint_cli.py verify --build "make test" --timeout 300
Важный момент: я специально сделал архитектуру так, чтобы потом ты просто подменил веса в --det_model_dir и --patch_adapter_dir, не переписывая сам продуктовый пайплайн.
Следующим шагом логично будет уже очень аккуратно править train_repair_lora.py под длительный retrain и отдельно закрыть историю с NaN в eval.