

Nicholas Carlini の講演と Ralph loop に触発された Nelson は、プロジェクト内のすべてのファイルに対してループ処理を行い、エージェントに脆弱性の有無を問い合わせるツールです。スキャンモード(Carlini の bash ループと同様に、ファイルまたはファイルのディレクトリ内の任意の脆弱性を見つけるようモデルに依頼する)、レビューモード(通常はより賢いモデルが報告された各脆弱性を再評価し、人間のレビューアにエスカレーションする価値があるかどうかを判断する)、そしてその間の重複排除ステップ(同じバグが何度も発見された場合、一度だけ判断される)を備えています。
広範なベンチマークから得られた大きな教訓は、バグを表面化させるのは反復である ということです。以前のバージョンには「フォーカスモード」があり、モデルに特定の CWE クラスを一度に1つずつ探すように指示していましたが、それが役に立っているように見えました。しかし、それは錯覚でした。CWE ごとの拡張により、モデルが各ファイルを何度も見るようになっただけで、実際に効果を発揮していたのは 反復 であり、CWE のターゲティングではありませんでした。バグクラスの命名、チェックリスト、その他のプロンプト形成は、制御された A/B テストでは実質的な改善をもたらしませんでした。そのため、フォーカスモードは廃止されました。代わりに、--repeat N を使用すると、ファイル×モデルのマトリックス全体が N 回(デフォルトは3回)実行され、同じトークンをはるかに有効活用できます。検出は本質的に不安定であり、発見可能なバグは3回のパスのうち1回しか現れないことが多いため、同じモデルであっても反復が標準となりました。
報告される問題の数が増えても、偽陽性が増えるのであれば必ずしも良いことではありません(小規模なモデルでは偽陽性が発生します)。反復だけでは状況が悪化します。同じバグが毎回現れるため、Nelson は発見結果をクラスタ(同じファイル/CWE、数行以内)に重複排除してからレビューを行います。各ユニークなバグは一度だけ判断され、その評決はすべてのコピーに適用されます。これにより、(多くの場合高価な)レビューモデルが同じ発見を何度も確認するためのコストを支払わずに済みます。一度本当のバグであれば、二度目も本当のバグです。賢いモデルをレビューに使用するのは良いアイデアですが、愚かなモデルでもレビューにおいて自身の間違いを検出できる場合があります。
Nelson は Claude Code、Gemini CLI、OpenAI 互換の API を介してさまざまなモデルと連携します。単一のモデル内では、ジョブは一度に1つずつ実行されます。サブスクリプションプランにはロールリングトークン制限があり、ローカルモデルは比較的小規模なハードウェアで動作するため、1つのプロバイダーで追加の並行処理による利点はありません。ただし、異なるモデル間ではレート制限が独立しているため、複数の -m 仕様を渡すと、Nelson はデフォルトでモデルごとに1つのワーカーを並列に実行します(例:Claude、Gemini、LM Studio 経由のローカル Qwen がすべて同時にキューを処理します)。--no-parallel を渡すと、逐次処理に戻ります。
最良の結果を急いでおらず、無制限のトークンバジェットがある場合を除き、トークンの賢い使い方は、Gemma 4 31B や DeepSeek V4 Pro のような安価で実績のあるモデルを数回繰り返してレポートを生成し、そのレポートをより高価なモデルでレビューし、最後に好きなフロンティアモデルで注意深くインタラクティブセッションを行って問題を修正するか、単にエディタを開いて自分でバグを修正することだと考えています。手取り足取りのサポートなしでモデルが自動的に修正できるほど単純な問題は、おそらく静的解析ツール(例:S ルールを有効にした Python 用の ruff や 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 回繰り返す)、重複を排除し、すべての一意な発見を1つの強力なレビューモデルで判断します。少なくとも2つのスキャンモデルと1つのレビューモデルが必要です。config file にそれらを設定するのが最も簡単で、nelson haha /path/to/project と入力するだけで実行できます。詳細は haha mode を参照してください。
nelson scan は、Carlini のアプローチと同様に、各ファイルを各モデルに「あらゆる脆弱性を見つける」という広範なプロンプトで送信します — 1つのジョブあたり (file, model) です。重要な調整パラメータは --repeat です。これにより、行列全体を N 回(デフォルトは3)実行します。実際にバグを発見するのは、CWE ごとのターゲティングではなく反復であり、検出は十分に不安定であるため、実際のバグは3回のパスのうち1回だけに現れることが多く、単一のモデルでも反復は価値があります。パス間(およびモデル間)の重複する発見は、レビュー時にマージされます。```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はすでにエージェントであり、必要なファイルは自分で読み取ります。素のOpenAI互換エンドポイント(`openai:`、`lmstudio:`、`ollama:`)はそうではありません。デフォルトでは、プロンプトに貼り付けられた単一のファイルしか認識しません。`--tools`を渡すと、これらのモデルにスキャンされたツリーをルートとした読み取り専用の`read_file` / `grep` / `list_dir`ツールループが提供され、脆弱性が実際に存在し到達可能かどうかを判断する前に、インポート、呼び出し元、ヘルパーを他のファイルに追跡できるようになります。(`grep`ツールには[ripgrep](https://github.com/BurntSushi/ripgrep)をインストールしてください。)これにより、ファイルごとにより多くのトークンが使用されます。`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
また、ディレクトリ全体ではなく、1つ以上の個別ファイルを指定して nelson scan を実行することもできます。これは、1つのファイルをスポットチェックしたり、シェルグロブが展開されたものをスキャンするのに便利です。ファイルを明示的に指定すると、通常のパスベースのフィルター(テスト/ドキュメントパターン、生成ファイル検出)はスキップされます — 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)内の行番号)。次に、各クラスターから1つの代表的な結果を、完全なソースファイルとともにモデル(より賢いものが望ましい)に送信し、実行フローをトレースして脆弱性が到達可能で現実的かどうかを評価するよう依頼します。結果の判定はクラスター内のすべての結果に適用されるため、--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:` の場合は、すでに独自にファイルを読み取るため、このフラグは何も行いません。レビューは冪等であり、再度実行しても未レビューの検出結果のみが処理されるため、あるモデルでレビューした後、別のモデルで2回目のパスを実行できます。
### レポート```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
A "cluster"は一つの顕著な問題を指します:同じファイル、同じ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
詳細レポートは、各ファイルごとにグループ化されたすべての検出項目を、信頼度バッジ、レビュー判定、コードスニペット、トークン使用量とともに表示します。エグゼクティブサマリーは1ページの要約で、すべてのスキャンに対して確認済み/誤検知/要レビューの件数、および各スキャンごとの確認済み検出項目の内訳を示します。
nelson inventory /path/to/project
nelson scan)nelson list
nelson status nelson status 3
### Hahaモード
`haha` コマンド(ネルソンの決めゼリフ)は、コードに対して一気にすべてを実行します。
1. **スキャン** — すべてのスキャンモデルがすべてのファイルを監査し、それぞれ `--repeat` 回(デフォルト3回)実行します。
2. **重複排除** — 統合された検出結果がユニークなバグにクラスタリングされます。
3. **レビュー** — 1つの強力なレビューモデルが各ユニークなバグを1回判断します。
4. **サマリー** — 確認済み/誤検出/要レビューの件数を出力します。
これには **少なくとも2つのスキャンモデルと1つのレビューモデル** が必要です。コマンドラインで指定するか、より便利には [設定ファイル](#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
すべての結果は1回のスキャンに集約され、その後 nelson report <scan_id>、nelson html-report <scan_id>、または nelson compare <scan_id> で確認できます。
トークン使用量に関する警告: 大規模なプロジェクトでは、haha は多くのトークンを消費し、時間がかかります — これは files × scan_models × repeat のスキャンジョブと、ユニークなバグごとに1つのレビュージョブを実行します。ペースとコストをより細かく制御したい場合は、個別の 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` では、上書きしない限り同じデフォルト設定が適用されます。
## モデル設定
モデルは `type:model` 構文で指定します。
| 指定 | 説明 |
|------|-------------|
| `claude:haiku` | CLI 経由の Claude Haiku |
| `claude:sonnet` | CLI 経由の Claude Sonnet |
| `claude:opus` | CLI 経由の Claude Opus |
| `gemini:gemini-2.5-flash` | 特定モデルを指定した Gemini CLI |
| `gemini:` | デフォルトモデルを使用した Gemini CLI |
| `lmstudio:google/gemma-4-26b-a4b` | localhost:1234 上の LM Studio |
| `ollama:llama3` | localhost:11434 上の Ollama |
| `openai:model@http://host:port/v1` | OpenAI 互換 API エンドポイント(ローカルまたはホスティング) |
| `openai:deepseek-v4-pro@https://api.deepseek.com/v1` | DeepSeek(ホスティング) |
| `openai:nvidia/nemotron-3-super-120b-a12b@https://openrouter.ai/api/v1` | OpenRouter(ホスティング) |
`openai:` タイプは、OpenAI chat-completions API を話す任意のもの(ローカルサーバーまたはホスティングプロバイダー)と通信します。ローカルサーバー(`lmstudio:`、`ollama:`、または `openai:...@http://localhost...` の指定)にはキーは不要です。ホスティングプロバイダーについては、下記の[ホステッドAPIモデル](#hosted-api-models-deepseek-mimo-openrouter)を参照してください。
複数のモデルを1回のスキャンで使用して、効果を比較できます。デフォルトでは並列で実行されます — 各モデルに1つのワーカー、レート制限はプロバイダーごとに設定されます。```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 を使用してください(例:同じマシン上の2つのローカルモデル間での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は、コストパフォーマンスに優れています。フロンティアモデルが見つけるものの大部分を、ごくわずかなコストで見つけることができ、Nelsonのブルートフォースかつ全ファイルを対象とするアプローチに適しています。
**認証.** Nelsonは `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)** — MiMoのOpenAI互換エンドポイントを指定します:```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 — 1つのキーと1つのベースURLで、単一のアカウントを通じてほとんどの主要モデルにアクセスできます。モデルIDは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:` モデルは **シングルショット** です — 各プロンプトに貼り付けられた1つのファイルのみを認識します。
`--tools` を追加すると( [Scanning](#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)を1回のスキャンで混在させることができ、Nelsonはモデルごとに1つのワーカーを並行して実行します:```bash
nelson scan /path/to/project \
-m "openai:deepseek-v4-flash@https://api.deepseek.com/v1" \
-m "lmstudio:Qwen/Qwen3-27B" \
-m claude:haiku
スキャンプロンプトは、モデルに対して単一ファイル内の何かを見つけるよう要求します。プロンプトは (ファイル, モデル) ジョブごとに1つずつ設定され、マトリックス全体は --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` を判定するよう求める別のプロンプトを使用します。
## ファイルフィルタリング
ネルソンは、本番の脆弱性を含む可能性が低いファイルを自動的に除外します。
- **テストコード**: `test_*`, `*_test.*`, `*_spec.*`, `tests/`, `__tests__/` など
- **ドキュメント**: `docs/`, `*.md`, `*.txt`
- **生成コード**: "DO NOT EDIT" / "AUTO-GENERATED" ヘッダーを含むファイル
- **ベンダーコード**: `vendor/`, `node_modules/`, `third_party/`
- **大きなファイル**: 500KB以上
- **非ソースファイル**: 認識された拡張子(`.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 inventory` および `nelson report` の一部として自動的に実行されます。たとえば、以下の場合にフラグを立てます:
- Ruff は存在するが、S (Bandit) セキュリティルールが有効になっていない
- Go プロジェクトに gosec を備えた golangci-lint がない
- TypeScript プロジェクトに eslint-plugin-security がない
- Perl プロジェクトに Perl::Critic 設定がない
考え方としては、静的解析ツールはパターンマッチングによる脆弱性検出においてAIよりも安価で高速であり、Nelsonはそれらの作業を重複させるのではなく補完するべきであるということです。
## データベース
スキャン状態は SQLite データベースに保存されます(デフォルトではカレントディレクトリの `nelson.db`)。`--db` を使用して別のパスを指定します。
すべてのスキャン結果、発見、レビューの判定が保存され、モデル、モード、期間をまたいだ結果の比較が容易になります。
## トークン追跡
ネルソンはジョブごとのトークン使用量とコストを追跡します。合計を確認するには `nelson status` を使用します。