
Поиск уязвимостей с помощью брутфорса

Вдохновленный докладом Nicholas Carlini и Ralph loop, Nelson — это инструмент для перебора всех файлов в проекте, побуждающий агента искать уязвимости. У него есть режим сканирования, похожий на bash-цикл Carlini, где модель просят найти любую уязвимость в файле или каталоге файлов; режим проверки, где (обычно более умная) модель заново проверяет каждую сообщенную уязвимость и решает, стоит ли передавать ее человеку-рецензенту; и этап дедупликации между ними, чтобы одна и та же ошибка, найденная много раз, оценивалась только один раз.
Главный урок, извлеченный из обширного бенчмаркинга, заключается в том, что повторение выявляет ошибки. В более ранних версиях был «режим фокусировки», который просил модель охотиться за одним конкретным классом CWE за раз, и казалось, что это помогает — но это была иллюзия: расширение по CWE просто заставляло модель просматривать каждый файл много раз, и именно повторение, а не нацеливание на CWE, выполняло работу. Называние класса ошибок, чеклисты и другие способы формирования промпта не давали реального прироста в контролируемых A/B-тестах. Поэтому режим фокусировки удален. Вместо этого --repeat N запускает всю матрицу файл × модель N раз (по умолчанию 3), что является гораздо лучшим использованием тех же токенов. Обнаружение действительно ненадежно — находимая ошибка часто появляется только в одном из трех проходов — поэтому повторение, даже с той же моделью, теперь является стандартной практикой.
Большее количество сообщенных проблем не обязательно является хорошим, если при этом растет количество ложных срабатываний (а они растут, особенно у маленьких моделей). Повторение само по себе усугубляет это — одна и та же ошибка появляется в каждом проходе — поэтому Nelson дедуплицирует находки в кластеры (один и тот же файл/CWE в нескольких строках) до проверки: каждая уникальная ошибка оценивается один раз, и вердикт применяется ко всем копиям. Это не дает (часто дорогой) модели-рецензенту платить за повторное подтверждение одной и той же находки. Если ошибка реальна один раз, она реальна и во второй. Использовать более умную модель для проверки — хорошая идея, но даже глупая модель может заметить собственные ошибки при проверке.
Nelson работает с различными моделями через Claude Code, Gemini CLI и API, совместимые с OpenAI. В рамках одной модели задачи выполняются по одной — подписные планы имеют скользящие лимиты токенов, а локальные модели работают на относительно скромном оборудовании, поэтому дополнительная конкурентность на одном провайдере не дает выигрыша. Однако для разных моделей ограничения скорости независимы, поэтому, когда вы передаете несколько спецификаций -m, Nelson по умолчанию запускает по одному рабочему на каждую модель параллельно (например, Claude, Gemini и локальный Qwen через LM Studio — все одновременно обрабатывают очередь). Передайте --no-parallel, чтобы вернуться к последовательному выполнению одной модели за раз.
Если вы не торопитесь получить наилучшие результаты и у вас не безлимитный бюджет токенов, я считаю, что разумное использование токенов — это запустить отчет с дешевой, но проверенной эффективной моделью, например Gemma 4 31B или DeepSeek V4 Pro, повторив несколько раз, затем проверить отчет с помощью более дорогой модели и, наконец, провести более тщательную интерактивную сессию с вашей любимой frontier-моделью для исправления проблемы или просто открыть редактор и исправить ошибку самостоятельно. Все, что достаточно просто для автоматического исправления моделью без дополнительного контроля, вероятно, обнаруживается с помощью инструментов статического анализа (например, ruff для Python с включенными правилами S или semgrep и т.д.), и вам следует запускать такие инструменты и исправлять все найденные проблемы перед тем, как передавать кодовую базу nelson.
Nelson в настоящее время не пытается исправлять ошибки безопасности. Это исключительно инструмент отчетности, хотя модели часто предлагают советы по исправлению без дополнительных запросов.
Я провел много тестирования и бенчмаркинга различных моделей, чтобы выяснить наиболее эффективное использование времени и токенов, так как мне нужно просмотреть сотни тысяч строк кода в десятках репозиториев. Основные выводы: повторение превосходит формирование промпта, дешевые модели, повторенные несколько раз, часто обеспечивают лучшую ценность, и одна сильная модель, используемая в качестве рецензента, стоит больше, чем изощренные трюки со сканированием. Возможно, все еще окажется, что, как и в случае с программированием, лучше просто использовать самую умную модель, к которой у вас есть доступ, потому что глупые модели тратят гораздо больше человеческого времени, чем экономят на стоимости использования, — но относительно глупая модель, запущенная несколько раз, а затем отсортированная умным рецензентом, может сделать удивительно много.
Возможно, этот проект излишне усложнен для вашего случая использования. Может быть, скрипт вроде того, о котором говорил Carlini, подходит вам, что-то вроде:```
find . -type f -name *.py -print0 | while IFS= read -r -d '' file; do
claude
--verbose
--dangerously-skip-permissions
--print "You are playing in a CTF.
Find a vulnerability.
hint: look at $file
Write the most serious
one to /out/report.txt."
done
## Установка
Требуется Python 3.12+.```bash
git clone https://github.com/swelljoe/nelson.git
cd nelson
python -m venv .venv
source .venv/bin/activate
pip install -e .
Виртуальное окружение изолирует зависимости Nelson от системного Python. Вам нужно активировать его (source .venv/bin/activate) каждый раз, когда вы открываете новую оболочку, или просто запускать Nelson напрямую:```bash
/path/to/nelson/.venv/bin/nelson --help
Или запустить без установки:```bash
python -m venv .venv
source .venv/bin/activate
pip install click httpx
python -m nelson --help
Типичный рабочий процесс: сканирование, анализ, отчет.```bash
nelson scan -m claude:haiku /path/to/project
nelson review -m claude:sonnet
nelson report --verdict confirmed
Или запустите полный конвейер одной командой:```bash
nelson haha --scan-model claude:haiku --scan-model claude:sonnet \
--review-model claude:opus /path/to/project
haha применяет несколько моделей сканирования к коду (каждая повторяется --repeat раз), удаляет дубликаты и оценивает каждый уникальный результат с помощью одной мощной модели рецензирования. Для этого требуется как минимум две модели сканирования и одна модель рецензирования — проще всего указать их в файле конфигурации, чтобы потом можно было просто ввести nelson haha /path/to/project. Подробнее см. в разделе режим haha.
nelson scan отправляет каждый файл каждой модели с широким запросом "найти любую уязвимость", аналогично подходу Карлини — одно задание на пару (файл, модель). Ключевой параметр — --repeat: он запускает всю матрицу N раз (по умолчанию 3). Именно повторение, а не нацеливание на конкретные CWE, позволяет выявить ошибки, и обнаружение настолько ненадёжно, что реальная ошибка часто появляется только в одном из трёх проходов, поэтому повторение оправдано даже с одной моделью. Дублирующиеся результаты по проходам (и по моделям) объединяются на этапе рецензирования.```bash
nelson scan /path/to/project
nelson scan --repeat 1 /path/to/project
nelson scan -m claude:sonnet /path/to/project
nelson scan -m claude:haiku -m "lmstudio:google/gemma-4-31b" --repeat 5 /path/to/project
**Инструменты для моделей, совместимых с OpenAI.** Claude Code и Gemini CLI уже являются
агентами — они сами читают все необходимые файлы. Простой конечный API, совместимый с OpenAI
(`openai:`, `lmstudio:`, `ollama:`), таким не является: по умолчанию он видит только один файл,
вставленный в приглашение. Передайте `--tools`, чтобы предоставить этим моделям
цикл инструментов только для чтения `read_file` / `grep` / `list_dir`, привязанный к сканированному дереву,
чтобы они могли проследить импорты, вызывающие функции и вспомогательные компоненты в другие файлы,
прежде чем решить, является ли уязвимость реальной и достижимой. (Установите [ripgrep](https://github.com/BurntSushi/ripgrep)
для инструмента `grep`.) Это использует больше токенов на файл. Для спецификаций `claude:` /
`gemini:` это не имеет эффекта.```bash
# Let a local Qwen poke around the project, not just the one file
nelson scan --tools -m "lmstudio:Qwen/Qwen3-27B" /path/to/project
Вы также можете направить nelson scan на один или несколько отдельных файлов вместо целой директории. Это полезно для выборочной проверки одного файла или для сканирования того, что разворачивается из shell glob. Когда вы явно указываете файлы, обычные фильтры на основе путей (шаблоны test/doc, обнаружение сгенерированных файлов) пропускаются — Nelson доверяет вам знать, что вам нужно. То же самое относится к nelson inventory и nelson haha.```bash
nelson scan path/to/suspicious.py
nelson scan src/api/*.py
nelson scan src/auth.py src/db.py src/handlers/*.go
nelson inventory src/api/*.py nelson haha src/auth.py src/db.py
Сканирование можно возобновить. Если прервано, просто возобновите по ID сканирования:```bash
nelson scan --resume 3
Этап проверки сначала дедуплицирует результаты сканирования в кластеры (один и тот же файл и CWE, номера строк в пределах --line-tolerance, по умолчанию 2), затем отправляет одного представителя от каждого кластера модели (желательно более умной) вместе с полным исходным файлом, прося её проследить поток выполнения и оценить, является ли уязвимость достижимой и реалистичной. Полученный вердикт применяется к каждому результату в кластере, поэтому ошибка, которую --repeat и несколько моделей обнаружили множество раз, оценивается один раз — проверяющему не платят снова и снова за одну и ту же находку. Все дублирующиеся строки сохраняются (с указанием того, какая модель/этап их нашла), чтобы представление сравнения по-прежнему работало.```bash
nelson review
nelson review 3
nelson review -m claude:opus
nelson review --line-tolerance 5
nelson review -m "lmstudio:Qwen/Qwen3-27B" --tools
Каждое обнаружение получает вердикт: `confirmed`, `false_positive`, `needs_review` или `resolved` (если файл был удалён после сканирования). Флаг `--tools` работает так же, как и для `nelson scan`: он предоставляет совместимой с OpenAI модели (`openai:`/`lmstudio:`/`ollama:`) цикл только для чтения `read_file`/`grep`/`list_dir` по просканированному дереву, чтобы она могла проследить обнаружение до файлов, к которым оно обращается, прежде чем вынести вердикт о достижимости. Это пустая операция для `claude:`/`gemini:`, которые уже сами читают файлы. Проверка идемпотентна — повторный запуск обрабатывает только непроверенные обнаружения, так что вы можете проверить одной моделью, а затем запустить второй проход другой.
### Отчётность```bash
# Show all findings from the latest scan
nelson report
# Show findings from a specific scan
nelson report 3
# Filter by review verdict
nelson report --verdict confirmed
nelson report --verdict false_positive
nelson report --verdict needs_review
# Filter by confidence or CWE
nelson report --confidence high
nelson report --cwe CWE-89
# JSON output for scripting
nelson report --json-output
nelson report --verdict confirmed --json-output
Когда вы сканируете с несколькими моделями (параллельно или иным образом), nelson compare группирует результаты в группы «одинаковая проблема», чтобы вы могли видеть, где модели согласились:```bash
nelson compare nelson compare 5
nelson compare --scans 3,5,7
nelson compare --line-tolerance 0 # exact line match only nelson compare --line-tolerance 5 # more forgiving
nelson compare --min-agreement 2 # only show clusters >= 2 models flagged nelson compare --cwe CWE-89 nelson compare --confidence high
nelson compare --json-output
nelson html-compare nelson html-compare --scans 3,5,7 -o my-comparison.html
"Кластер" — это один выявленный дефект: один файл, один CWE, номера строк в пределах заданного окна допуска. Для каждого кластера в отчете указывается, какие модели его обнаружили, а какие имели возможность обнаружить, но не сделали этого (набор возможных голосующих — это все модели, завершившие открытое сканирование этого файла). Кластеры с высокой согласованностью (например, 3/3) являются сильным сигналом; кластеры с единственной моделью обычно являются ложными срабатываниями. Это полезно как для фильтрации шума, так и для оценки производительности небольшой локальной модели по сравнению с передовой.
### HTML-отчеты

Nelson может создавать автономные статические HTML-отчеты:```bash
# Detailed report for a single scan (default: latest)
nelson html-report
nelson html-report 3
nelson html-report -o my-report.html
# Executive summary across all scans
nelson html-summary
nelson html-summary -o summary.html
Подробный отчет показывает каждое обнаружение, сгруппированное по файлам, с значками уверенности, вердиктами проверки, фрагментами кода и использованием токенов. Сводка для руководства представляет собой одностраничный документ, показывающий все сканирования с количеством подтвержденных/ложно-положительных/требующих проверки, а также разбивку подтвержденных обнаружений по каждому сканированию.
nelson inventory /path/to/project
nelson scan)nelson list
nelson status nelson status 3
### Режим Haha
Команда `haha` (фирменная фраза Нельсона) применяет к коду сразу всё в одном заходе:
1. **Scan** — каждая модель сканирования проверяет каждый файл, `--repeat` раз каждая (по умолчанию 3)
2. **Dedup** — объединённые находки группируются в уникальные ошибки
3. **Review** — одна сильная модель ревью оценивает каждую уникальную ошибку один раз
4. **Summary** — выводит количество подтверждённых, ложных срабатываний и требующих проверки
Для работы требуется **как минимум две модели сканирования и одна модель ревью**. Укажите их в командной строке или — что удобнее — в [конфигурационном файле](#configuration); `haha` завершится с ошибкой, если не сможет их найти.```bash
# Models from ./nelson.yaml or ~/.nelson.yaml
nelson haha /path/to/project
# Or specify on the command line (--scan-model is repeatable)
nelson haha /path/to/project \
--scan-model "openai:deepseek-v4-flash@https://api.deepseek.com/v1" \
--scan-model "lmstudio:google/gemma-4-26b-a4b" \
--review-model claude:opus \
--repeat 3
Всё попадает в один скан, который вы можете проверить позже с помощью nelson report <scan_id>, nelson html-report <scan_id> или nelson compare <scan_id>.
Предупреждение об использовании токенов: В большом проекте haha потребляет много токенов и занимает много времени — он запускает files × scan_models × repeat заданий сканирования плюс задание проверки для каждой уникальной ошибки. Рассмотрите возможность запуска отдельных команд nelson scan и nelson review, если вы хотите больше контроля над темпом и стоимостью.
Nelson читает опциональный YAML-конфиг, чтобы вам не приходилось заново вводить любимые модели для каждого этапа. Он ищет ./nelson.yaml (локальный для проекта), затем ~/.nelson.yaml (домашний); файл проекта имеет приоритет по каждому ключу, а явные флаги командной строки переопределяют оба. Все ключи опциональны:```yaml
scan_models: # used by haha (needs >= 2) and as the default for scan
haha (required) and as the default for review
repeat: 3 # default number of passes
db: nelson.db # default database path
delay: 2.0 # default per-job pacing (seconds)Теперь `nelson haha /path/to/project` просто работает, а `nelson scan` / `nelson review` используют те же настройки по умолчанию, если вы их не переопределяете.
## Конфигурация моделей
Модели указываются с помощью синтаксиса `тип:модель`:
| Спецификация | Описание |
|------|-------------|
| `claude:haiku` | Claude Haiku через CLI |
| `claude:sonnet` | Claude Sonnet через CLI |
| `claude:opus` | Claude Opus через CLI |
| `gemini:gemini-2.5-flash` | Gemini CLI с конкретной моделью |
| `gemini:` | Gemini CLI с моделью по умолчанию |
| `lmstudio:google/gemma-4-26b-a4b` | LM Studio на localhost:1234 |
| `ollama:llama3` | Ollama на localhost:11434 |
| `openai:model@http://host:port/v1` | Любой API-совместимый с OpenAI эндпоинт (локальный или облачный) |
| `openai:deepseek-v4-pro@https://api.deepseek.com/v1` | DeepSeek (облачный) |
| `openai:nvidia/nemotron-3-super-120b-a12b@https://openrouter.ai/api/v1` | OpenRouter (облачный) |
Тип `openai:` работает с любым сервисом, поддерживающим API чат-завершений OpenAI — будь то локальный сервер или облачный провайдер. Для локальных серверов (`lmstudio:`, `ollama:` или специфика `openai:...@http://localhost...`) ключ не требуется. Для облачных провайдеров смотрите раздел [Модели облачных API](#hosted-api-models-deepseek-mimo-openrouter) ниже.
Для сравнения эффективности можно использовать несколько моделей в одном сканировании. По умолчанию они работают параллельно — один рабочий процесс на модель, поскольку ограничения скорости действуют для каждого провайдера отдельно:```bash
# Claude Haiku and a local Qwen model both work the queue at once
nelson scan /path/to/project \
-m claude:haiku \
-m "lmstudio:Qwen/Qwen3-27B"
Используйте --no-parallel, если предпочитаете выполнять каждую модель последовательно (например, чтобы снизить конкуренцию за CPU/GPU между двумя локальными моделями на одном компьютере).
Агенты на основе CLI (Claude Code, Gemini CLI) работают с настраиваемой задержкой между заданиями, чтобы избежать превышения лимитов подписок. Модели на основе API (LM Studio, Ollama, пользовательские конечные точки) выполняются без задержки. По умолчанию задержка составляет 2 секунды; настройте с помощью --delay. Темп регулируется для каждого рабочего процесса, поэтому каждая модель независимо выдерживает свою задержку между своими заданиями:```bash
nelson scan /path/to/project -m claude:haiku --delay 5
### Хостируемые API-модели (DeepSeek, MiMo, OpenRouter)
Вам не нужен локальный GPU для запуска дешевой модели. Любой хостируемый провайдер с
совместимой с OpenAI конечной точкой работает через спецификацию `openai:`, в виде
`openai:MODEL@BASE_URL`, где `BASE_URL` заканчивается на `/v1`. По моим тестам эти
хостируемые «дешевые» модели — особенно DeepSeek и Xiaomi MiMo — были лидерами по
соотношению цена/производительность: они находят большую часть того, что находят передовые модели, за
малую долю стоимости, что делает их хорошим выбором для подхода Нельсона «грубая сила,
каждый файл».
**Аутентификация.** Нельсон считывает ключ из переменной окружения `OPENAI_API_KEY`
(универсальное соглашение для OpenAI-совместимых). Экспортируйте ключ вашего провайдера
под этим именем перед сканированием — независимо от того, на какого провайдера указывает `@BASE_URL`:```bash
export OPENAI_API_KEY="sk-your-provider-key"
Хранение ключа в окружении (или в неотслеживаемом .env, который вы source'ите) удерживает его
вне вашей истории командной оболочки и вне любых файлов, которые пишет Nelson. Отсутствующий или отклонённый
ключ проявляется как ошибка аутентификации, а не как молчаливое "просканировано и ничего не найдено."
DeepSeek — deepseek-v4-pro — более мощная/дорогая модель, deepseek-v4-flash
— более дешёвая:```bash
export OPENAI_API_KEY="sk-..." # your DeepSeek key
nelson scan /path/to/project -m "openai:deepseek-v4-pro@https://api.deepseek.com/v1"
nelson scan /path/to/project -m "openai:deepseek-v4-flash@https://api.deepseek.com/v1"
**MiMo (Xiaomi)** — укажите на OpenAI-совместимый endpoint MiMo:```bash
export OPENAI_API_KEY="..." # your MiMo key
nelson scan /path/to/project \
-m "openai:mimo-v2.5-pro@https://token-plan-sgp.xiaomimimo.com/v1"
OpenRouter — один ключ и один базовый URL открывают доступ к большинству основных моделей через одну учётную запись; идентификатор модели — это slug с префиксом провайдера из каталога OpenRouter (например, nvidia/nemotron-3-super-120b-a12b, добавьте :free для бесплатного маршрута). Это удобный способ попробовать множество моделей, не регистрируясь у каждого провайдера:```bash
export OPENAI_API_KEY="sk-or-..." # your OpenRouter key
nelson scan /path/to/project
-m "openai:nvidia/nemotron-3-super-120b-a12b@https://openrouter.ai/api/v1"
По умолчанию хостируемая модель `openai:` является **одноразовой** — она видит только один файл, вставленный в каждый промпт. Добавьте `--tools` (см. [Сканирование](#scanning)), чтобы предоставить ей цикл `read_file`/`grep`/`list_dir` только для чтения по проекту, чтобы она могла следовать за импортами и местами вызовов в другие файлы, прежде чем принять решение, что обнаружение реально. Это требует больше токенов, но обычно снижает количество ложных срабатываний:```bash
nelson scan --tools /path/to/project \
-m "openai:deepseek-v4-pro@https://api.deepseek.com/v1"
Тот же спецификация и OPENAI_API_KEY работают для nelson review — дешёвая хостинговая модель может сканировать, а более мощная — проверять, или наоборот:```bash
nelson review -m "openai:deepseek-v4-pro@https://api.deepseek.com/v1" --tools
Поскольку ограничения скорости действуют для каждого провайдера, вы можете смешивать хостинговую модель с локальной (или Claude/Gemini) в одном сканировании, и Nelson запускает один рабочий процесс на модель параллельно:```bash
nelson scan /path/to/project \
-m "openai:deepseek-v4-flash@https://api.deepseek.com/v1" \
-m "lmstudio:Qwen/Qwen3-27B" \
-m claude:haiku
Подсказка сканирования просит модель найти что-либо в одном файле, по одной подсказке на задание (файл, модель) (и вся матрица повторяется --repeat раз):```
You are a security researcher performing a vulnerability audit. Analyze the
following python file and find any security vulnerabilities.
Look for all classes of vulnerability including but not limited to:
IMPORTANT INSTRUCTIONS:
File: app/db.py
Модель сама определяет CWE; Nelson записывает его вместе с находкой и использует (плюс номер строки) для кластеризации дублирующихся отчетов при проверке. Проход проверки использует отдельный промпт, который передает рецензенту полный файл и найденную проблему и просит его проследить достижимость и вынести вердикт `confirmed` / `false_positive` / `needs_review`.
## Фильтрация файлов
Nelson автоматически исключает файлы, которые вряд ли содержат уязвимости в продуктивной среде:
- **Тестовый код**: `test_*`, `*_test.*`, `*_spec.*`, `tests/`, `__tests__/` и т.д.
- **Документация**: `docs/`, `*.md`, `*.txt`
- **Сгенерированный код**: файлы с заголовками "DO NOT EDIT" / "AUTO-GENERATED"
- **Вендорный код**: `vendor/`, `node_modules/`, `third_party/`
- **Большие файлы**: более 500 КБ
- **Не-исходные файлы**: сканируются только файлы с распознанными расширениями (`.py`, `.go`, `.ts`, `.js`, `.c`, `.cpp`, `.rs`, `.java`, `.rb`, `.php`, `.pl`, `.pm`, `.sh`)
Используйте `nelson inventory /path/to/project`, чтобы увидеть, какие именно файлы будут просканированы.
Эти фильтры применяются только при сканировании каталога. Если вы явно указываете файлы в командной строке (например, `nelson scan src/foo.py src/bar.py`), применяются только проверки расширения и размера — обнаружение тестовых/документационных/сгенерированных файлов пропускается, исходя из предположения, что вы имели в виду именно то, что набрали.
## Оценка инструментов безопасности
Nelson проверяет, использует ли ваш проект рекомендуемые инструменты статического анализа, и сообщает о пробелах. Это выполняется автоматически в рамках `nelson inventory` и `nelson report`. Например, будет отмечено, если:
- Ruff присутствует, но правила безопасности S (Bandit) не включены
- В проекте Go нет golangci-lint с gosec
- В проекте TypeScript нет eslint-plugin-security
- В проекте Perl нет конфигурации Perl::Critic
Идея в том, что инструменты статического анализа дешевле и быстрее ИИ для поиска уязвимостей по шаблонам, и Nelson должен дополнять их, а не дублировать их работу.
## База данных
Состояние сканирования хранится в базе данных SQLite (`nelson.db` в текущем каталоге по умолчанию). Используйте `--db`, чтобы указать другой путь.
Все результаты сканирования, находки и вердикты проверки сохраняются, что позволяет легко сравнивать результаты по моделям, режимам и времени.
## Отслеживание токенов
Nelson отслеживает использование токенов и стоимость каждого задания. Используйте `nelson status`, чтобы увидеть итоги.