OWASP Security Checker
Статический анализатор кода на уязвимости OWASP Top 10:2025 — A04 (Cryptographic Failures) и A07 (Identification and Authentication Failures). Работает как расширение VS Code и как CLI для CI/CD.
Поддерживаемые языки (9): JavaScript, TypeScript (+ JSX/TSX), Python, Java, Kotlin, Go, PHP, C, C++. Для JS/TS используется семантический AST-анализ (TypeScript Compiler API), для остальных языков — regex-движок с маскированием комментариев; поверх находок всех языков работает ML-классификатор ложных срабатываний.
В версии 0.2.0 добавлены три ключевых улучшения:
| Возможность |
Что даёт |
| AST-анализ (TypeScript Compiler API) |
Точное обнаружение для JS/TS без ложных срабатываний на комментариях и строках |
| ML-классификация (логистическая регрессия) |
Контекстное подавление ложных срабатываний (плейсхолдеры, namespace-URL, тестовые файлы) |
| GitHub Action + CLI (SARIF) |
Автоматическая проверка при каждом коммите/PR |
Архитектура
src/
├── core/ ← ядро анализа (НЕ зависит от vscode → работает и в CLI)
│ ├── engine.ts ← оркестратор: выбор анализатора + ML
│ ├── astAnalyzer.ts ← AST-детекторы для JS/TS (ts.createSourceFile)
│ ├── regexAnalyzer.ts ← regex-движок для Python/Java/Kotlin/Go/PHP/C/C++
│ ├── text.ts ← позиции, комментарии (лексер)
│ └── ml/
│ ├── features.ts ← извлечение контекстных признаков
│ ├── classifier.ts ← инференс (sigmoid(w·x))
│ ├── dataset.ts ← размеченный обучающий набор
│ ├── train.ts ← обучение (градиентный спуск + L2)
│ └── model.ts ← обученные веса (генерируется)
├── rules.ts ← база из 70+ правил (CWE/OWASP), 9 языков
├── diagnosticProvider.ts ← адаптер ядра → vscode.Diagnostic
├── hoverProvider.ts · codeActionProvider.ts · reportPanel.ts
├── extension.ts ← точка входа расширения
└── cli.ts ← headless-сканер (SARIF) для CI
Расширение и CLI используют одно и то же ядро, поэтому результаты в редакторе и в CI совпадают.
Принцип работы (полный конвейер анализа)
Анализ одного документа проходит через единый конвейер engine.analyze() — он одинаков и в редакторе, и в CLI. Разберём его по шагам.
Шаг 0. Вход
На вход подаётся объект { code, languageId, filePath }:
code — текст файла;
languageId — идентификатор языка в стиле VS Code (javascript, kotlin, python, …). В редакторе его сообщает VS Code, в CLI он выводится из расширения файла (.kt/.kts → kotlin);
filePath — путь, используется как контекстный признак для ML (находки в тестовых каталогах понижаются).
Сначала computeLineStarts() строит таблицу смещений начала строк — это позволяет за O(log n) переводить любое смещение в координаты «строка:столбец», независимые от API редактора.
Шаг 1. Выбор анализатора по языку
┌──────────────────────────────────────────────┐
JS / TS / JSX/TSX │ AST-детекторы (TypeScript Compiler API) │
─────────────────► │ + regex-fallback для правил без AST-детектора │
│ (матчи внутри комментариев отбрасываются) │
└──────────────────────────────────────────────┘
Kotlin, Python, ┌──────────────────────────────────────────────┐
Java, Go, PHP, │ regex-движок + лексер комментариев (text.ts) │
C, C++ ───► │ (матчи внутри // … и /* … */ отбрасываются) │
└──────────────────────────────────────────────┘
- JS/TS: код разбирается в AST (
ts.createSourceFile). Семантические детекторы (см. ниже) ищут формы узлов, а не текст. Правила, для которых нет AST-детектора (AES-ECB, DES, RC4 и др.), выполняет regex-движок, но матчи внутри диапазонов комментариев, полученных из ts.createScanner, отбрасываются.
- Прочие языки (включая Kotlin): работает regex-движок
runRegexRules(). Перед этим лёгкий лексер findCommentRanges() находит диапазоны комментариев (//, /* */, для Python/PHP — #), отслеживая строковые литералы, чтобы // внутри строки не принять за комментарий. Любой матч, начинающийся внутри комментария, игнорируется.
На этом шаге каждое сработавшее правило порождает «сырую» находку Finding с координатами, текстом совпадения и пометкой движка (ast | regex).
Шаг 2. Фильтрация по правилам и серьёзности
Ещё до запуска движков применяется ruleAllowed(): учитываются disabledRules, enabledRules и minSeverity. Это позволяет на уровне настроек включать/выключать отдельные правила и поднимать порог серьёзности.
Шаг 3. ML-скоринг каждой находки
Для каждой сырой находки извлекается вектор из 12 контекстных признаков (features.ts) и логистическая регрессия выдаёт P(уязвимость):
признаки(находка, строка, путь) → P = σ(w · x) → сравнение с порогом (0.5)
Признаки разведены по типу правила: значение-несущие признаки (энтропия, длина, плейсхолдер, namespace-URL) вычисляются только для правил-секретов (*-hardcoded-secret) и http-правил (*-http-url). Поэтому, например, kotlin-hardcoded-secret и kotlin-http-url автоматически получают «осмысленные» признаки — именно благодаря единому соглашению об именах правил поддержка нового языка не требует менять ML-код.
Если P < порога, находка помечается suppressed и по умолчанию скрывается (в CLI её можно показать флагом --include-suppressed, в редакторе — настройкой showSuppressedAsHints).
Шаг 4. Результат
Оставшиеся находки сортируются по позиции и возвращаются. Дальше:
- в редакторе
diagnosticProvider.ts превращает их в vscode.Diagnostic (с пометкой движка и % уверенности);
- в CLI они печатаются и/или экспортируются в SARIF 2.1.0 для GitHub Code Scanning.
Добавление поддержки нового языка (на примере Kotlin)
- Описать правила с
languages: ['kotlin'] в rules.ts. Для правил-секретов/URL придерживаться соглашения об именах (kotlin-hardcoded-secret, kotlin-http-url), чтобы ML-признаки активировались автоматически.
- Зарегистрировать
kotlin в SUPPORTED_LANGUAGES (extension.ts) и сопоставить расширения .kt/.kts в cli.ts.
- (Опционально) добавить язык в общие правила (например,
jwt-alg-none).
- Пополнить обучающий набор
dataset.ts примерами на новом языке и выполнить npm run train.
AST-анализатор писать не требуется: Kotlin, как и Java/Go/PHP, обслуживается regex-движком с маскированием комментариев.
Как работает AST-анализ
Для JavaScript/TypeScript код разбирается парсером TypeScript в дерево (AST). Детекторы сопоставляют семантические формы, а не текст:
crypto.createHash('md5') распознаётся только как реальный вызов — упоминание в // комментарии или внутри строки "...createHash('md5')..." игнорируется;
res.cookie(..., { httpOnly: true, secure: true }) не подсвечивается, потому что флаги фактически присутствуют в объекте;
{ alg: 'none' }, origin: '*', rejectUnauthorized: false находятся как узлы дерева.
Остальные языки используют regex-движок с маскированием комментариев (лексер в text.ts).
Как работает ML-классификатор
Каждая находка превращается в вектор контекстных признаков (features.ts): тип правила, серьёзность, энтропия и длина значения, признаки плейсхолдера, чтение из окружения, тестовый путь, namespace-URL и др. Логистическая регрессия выдаёт вероятность того, что находка — настоящая уязвимость. Находки ниже порога (mlThreshold, по умолчанию 0.5) скрываются.
Переобучение модели:
npm run train # компилирует проект и пересчитывает src/core/ml/model.ts
Как увидеть работу ML
ML по умолчанию скрывает ложные срабатывания, поэтому его эффект незаметен. Чтобы его увидеть:
- В редакторе — включите
owaspChecker.showSuppressedAsHints = true: подавлённые находки появятся серым с пометкой «🤖 Подавлено ML (уверенность N%)».
- В консоли — запустите CLI с
--include-suppressed (видны проценты уверенности):
node ./out/cli.js demo/ml-secrets.js --include-suppressed
Демо-файлы, где реальное и ложное выглядят для regex одинаково, а ML их разделяет:
| Файл |
Что показывает |
Результат |
demo/ml-secrets.js |
реальные секреты vs плейсхолдеры/низкая энтропия |
4 оставлено (91–94%), 7 подавлено (3–7%) |
demo/ml-urls.ts |
реальный http-трафик vs XML-namespace |
3 оставлено (91%), 6 подавлено (12%) |
demo/ml-context.py |
секреты в коде vs плейсхолдеры |
3 оставлено (90–93%), 5 подавлено (3–7%) |
CLI и GitHub Action
npm run compile
node ./out/cli.js <пути...> [опции]
--sarif <file> SARIF 2.1.0 отчёт (для GitHub Code Scanning)
--min-severity <sev> critical|high|medium|low (по умолчанию low)
--fail-on <sev> выход с кодом 1 при находке ≥ уровня (по умолчанию high)
--no-ml отключить ML-классификатор
--include-suppressed показать и подавленные находки
--json | --quiet
GitHub Action (.github/workflows/security-scan.yml в корне репозитория) запускается на каждый push/PR, формирует SARIF и загружает его в Security → Code scanning, падает при CRITICAL/HIGH.
Pre-commit hook (.githooks/pre-commit):
git config core.hooksPath .githooks
Тестирование
npm test # 24 теста (node:test): AST, ML, движок, метрики, Kotlin
Сравнение точности на наборе ложных срабатываний (demo/false-positives.js):
| Метод |
Находок |
Эталон |
Precision |
| Regex (наивный) |
11 |
2 |
18% |
| AST |
7 |
2 |
29% |
| AST + ML |
2 |
2 |
100% |
Сборка и установка
npm install
npm run compile
npm run package # → owasp-security-checker-0.2.0.vsix
code --install-extension owasp-security-checker-0.2.0.vsix
Настройки расширения
| Параметр |
По умолчанию |
Описание |
owaspChecker.enabled |
true |
Авто-анализ при открытии/сохранении |
owaspChecker.analyzeOnType |
true |
Живой анализ при вводе, без сохранения |
owaspChecker.analyzeOnTypeDelay |
400 |
Задержка (мс) перед повторным анализом |
owaspChecker.minSeverity |
low |
Минимальный уровень |
owaspChecker.useMlClassifier |
true |
ML-подавление ложных срабатываний |
owaspChecker.mlThreshold |
0.5 |
Порог уверенности ML |
owaspChecker.showSuppressedAsHints |
false |
Показывать подавлённые ML находки как подсказки |
owaspChecker.disabledRules / enabledRules |
[] |
Управление правилами |