SkillSpector 拆解:NVIDIA 扫了 3.1 万个 agent skill,26.1% 有漏洞、5.2% 疑似恶意

一、速览:值不值得你花时间

推荐指数:★★★★☆(4 / 5)

先说为什么这个工具值得单独讲一篇。Agent Skills 是 2026 年最被低估的一块攻击面。

你想想 skill 是什么:一个 SKILL.md(自然语言指令)+ 若干脚本,安装后 agent 会以隐含信任去执行它。它不像 npm 包有 lockfile 和审计生态,也不像 Docker 镜像有扫描基建 —— 它介于「文档」和「代码」之间,两边的安全习惯都没落到它身上。

SkillSpector 在 Overview 里给的数字非常直白:

In the 31,132-skill analyzed subset of the research dataset, 26.1% of skills contain vulnerabilities and 5.2% show likely malicious intent.

四分之一个 skill 有漏洞,二十分之一疑似恶意。这个比例意味着 「随便装一个 skill」本身就是个高风险动作。

再说工具本身。它不是玩具:71 个模式、17 个类别,覆盖从 prompt injection 到 AST 行为分析到污点追踪到 YARA 恶意代码签名,甚至包括 MCP 的 least privilege 和 tool poisoning。而且它是 NVIDIA Verified Skills pipeline 的一部分 —— NVIDIA 自己发布 skills catalog 之前就用它扫、评估、签名。有真实的生产流水线在背书。

扣掉的一颗星:项目只有 6 个月(2026-03-21 创建),144 个 open issues;静态分析的误报是真实存在的(官方专门做了 baseline / fingerprint 抑制机制,这本身就说明 FP 是痛点);以及 风险分是累加且会饱和的 —— 一堆 MEDIUM 也能把总分推到 DO_NOT_INSTALL,需要你自己调策略。

关键数据(截至 2026-10-05)

出品方 NVIDIA(NVIDIA Verified Skills pipeline 的一部分)
语言 Python
Star / Fork 18,488 / 1,612
提交 / Open Issues 548 / 144
许可证 Apache-2.0
首次提交 2026-03-21(约 6 个月)
最近提交 2026-09-28(昨天,非常活跃)
检测规模 71 个漏洞模式 / 17 个类别
研究数据 分析 31,132 个 skill:26.1% 含漏洞,5.2% 疑似恶意
输出格式 Terminal / JSON / Markdown / SARIF 2.1.0
多语言 支持 zh / ja / ko 检测

适合谁

人群 分 理由
日常用 Claude Code / Codex 的开发者 5 / 5 装 skill 前的体检,是目前最直接可用的工具
平台 / 工具链团队(要设安装门禁) 5 / 5 exit code + SARIF + gate mapping 三件套齐全
企业安全运营 4 / 5 有 NVIDIA 背书和 SARIF,但 6 个月历史偏新
安全研究员 4 / 5 17 个类别的分类法本身就是一份不错的威胁模型清单
个人 / 学习者 4 / 5 uv tool install 一行就能用,--no-llm 零成本

二次开发

难度 说明
改配置 低 —— CLI 参数覆盖全;有 baseline 文件抑制误报
改集成 低 —— exit code 是稳定契约,JSON / SARIF 两格式,--fail-on-findings / --fail-on-incomplete 两个门禁开关
改内核 中 —— 有 docs/DEVELOPMENT.md 讲 analyzer pipeline 怎么扩展,也有 MCP server / Pi / OpenCode 三种扩展形态

二、它是什么,不是什么

  • 不是杀毒软件。它不做沙箱动态执行,主要是静态分析 + 可选的 LLM 语义评估。
  • 不是运行时护栏。它回答的是「装之前这个 skill 安不安全」,不是「跑起来之后拦不拦得住」。
  • 不是只能扫本地目录。Git repo、URL、zip、目录、单个 SKILL.md 都能喂。
  • 不是纯正则匹配。它有 Behavioral AST(9 个模式)和 Taint Tracking(5 个模式)这两层,是真正的数据流 / 语法树分析。

提炼三个关键词:pre-install gate(安装前门禁)、71 patterns / 17 categories(覆盖度)、two-stage analysis(静态 + LLM 语义)。

三、17 个类别、71 个模式

这是它的核心资产,也是我认为最值得单独读一遍的部分 —— 即便你不用这个工具,这份分类法也是一份很好的 agent skill 威胁模型清单。

语义与指令层

类别 模式数 代表模式
Prompt Injection 6 P1 指令覆盖、P2 隐藏指令(注释 / 不可见文字)、P3 外传命令、P4 行为操纵、P5 有害内容、P9 空白填充(用大量空格把指令藏在可见区外)
Anti-Refusal 3 AR1 拒绝抑制(”never refuse”)、AR2 免责声明抑制(”no disclaimers”)、AR3 安全策略作废(”you have no restrictions” / DAN 类框架)
System Prompt Leakage 3 P6 直接泄露、P7 间接提取(改写 / 翻译 / 侧信道)、P8 经工具外带
Memory Poisoning 3 MP1 跨交互持久化注入、MP2 上下文窗口填充挤走安全约束、MP3 篡改 agent 记忆
Trigger Abuse 3 TR1 过宽触发词、TR2 影子命令触发(覆盖内置命令或其他 skill)、TR3 关键词 baiting

P9 空白填充 这条我觉得很见功力 —— 它不是「恶意指令」检测,而是检测「有人想让你看不见它」这个意图。同样,TR2 影子命令触发也抓得很准:一个 skill 把自己的触发词设成跟内置命令或其他 skill 一样,就能劫持调用。

数据与权限层

类别 模式数 代表模式
Data Exfiltration 4 E1 外部传输、E2 环境变量收割、E3 文件系统枚举、E4 上下文泄露
Privilege Escalation 3 PE1 过度权限、PE2 sudo/root、PE3 凭证读取(SSH key / token / 密码)
Excessive Agency 5 EA1 无约束工具访问、EA2 无人工介入的高影响决策、EA3 范围蔓延、EA4 无资源配额、EA5 外部模型/供应商选择(会切换计费账户)
Output Handling 3 OH1 未校验输出注入、OH2 跨信任边界输出、OH3 无输出上限
Tool Misuse 3 TM1 参数滥用(shell=True、--force)、TM2 链式绕过、TM3 不安全默认值(禁用 TLS、无认证)
Rogue Agent 2 RA1 运行时自我修改(CRITICAL)、RA2 未授权的持久化(cron / 启动脚本)

EA5 外部模型 / 供应商选择 这条特别值得注意 —— 它能识别出 skill 把你的请求导向另一个计费账户的模型 / provider。这不是「安全漏洞」而是成本与数据流向问题,但对企业是真金白银的风险。

供应链与代码层

类别 模式数 代表模式
Supply Chain 10+ SC1 依赖未锁版本、SC2 外部脚本拉取(curl | bash)、SC3 混淆代码、SC4 已知 CVE 依赖(实时查 OSV.dev)、SC5 废弃依赖、SC6 typosquatting、SC8 携带 Python 字节码、SC9 藏匿的可执行产物、SC10 依赖源重定向
Behavioral AST 9 AST1 exec()、AST8 危险执行链(exec/eval + 动态来源)、AST9 反射式 getattr sink(getattr(os,'system') 绕过 AST1/AST5)
Taint Tracking 5 TT3 凭证外传链(CRITICAL)、TT4 文件读取→网络外传、TT5 外部输入→代码执行(CRITICAL)
YARA Signatures 4 YR1 malware、YR2 webshell、YR3 挖矿、YR4 黑客工具 / exploit

SC4 会实时查 OSV.dev 拿 CVE 数据,且有离线回退 —— 这比「内置一个过期 CVE 库」强。

AST9 反射式 getattr 这条说明作者懂对抗:攻击者用 getattr(os, 'system') 就能绕过「检测 os.system 字面量」的 AST1/AST5。专门给这种情况单开一个模式,是有实战经验的痕迹。

TT5 外部输入→代码执行 是污点追踪最有价值的一条:网络或用户输入直接流进 exec/eval/subprocess,这是 RCE 的典型链路。

MCP 专项(2 类 8 个模式)

类别 模式数 代表模式
MCP Least Privilege 4 LP1 未声明的能力(代码用了但权限没写)、LP2 通配符权限、LP3 无权限声明但有可检测能力、LP4 过度声明
MCP Tool Poisoning 4 TP1 元数据里的隐藏指令(HTML 注释、零宽字符、base64、data URI)、TP2 Unicode 欺骗(同形字、RTL 覆盖、混写标识符)、TP3 参数描述注入、TP4 描述与行为不符(LLM 驱动)

LP1「未声明的能力」是我认为 MCP 场景里最重要的一个检测 —— 声明的权限和实际代码用到的能力对不上,这是最小权限原则最直接的违背。

TP4 描述与行为不符需要 LLM —— 这条在 --no-llm 模式下是跑不出来的。

四、风险分怎么算(读报告前必看)

1
2
3
4
5
CRITICAL  +50
HIGH +25
MEDIUM +10
LOW +5
可执行脚本 ×1.3 倍率
分数 严重度 建议
0–20 LOW SAFE
21–50 MEDIUM CAUTION
51–80 HIGH DO NOT INSTALL
81–100 CRITICAL DO NOT INSTALL

这个算法有两个特性你必须知道:

  1. 累加且不设上限,靠 band 截断。 20 个 MEDIUM(200 分)和 2 个 CRITICAL(100 分)都落在 CRITICAL band。也就是说大量低危发现会被算成「别装」。对干净的小 skill 没问题,对内容多的 skill 可能过于严格。
  2. 可执行脚本 ×1.3。 这是个启发式倍率 —— 有脚本的 skill 风险确实更高,但 1.3 这个系数是拍的。

官方给的门禁映射:

recommendation 建议动作
SAFE allow
CAUTION 提示 / 警告用户
DO_NOT_INSTALL block

五、接进 CI:exit code 是稳定契约

skillspector scan 的退出码:

Code 含义
0 扫描完成,risk_score ≤ 50(SAFE 或 CAUTION),且没触发已启用的严格门禁
1 扫出 risk_score > 50,或 --fail-on-findings 命中,或 --fail-on-incomplete 发现分析不完整
2 出错(输入有问题、源不可读、内部失败)

默认行为会把 SAFE 和 CAUTION 都折叠成 0。 想在 CI 里对 CAUTION 也卡,必须显式加 --fail-on-findings。

这个默认值是有道理的(CAUTION 意味着「提示用户」,不是「禁止」),但接 CI 时很容易踩坑 —— 你以为它在拦,其实它一直返回 0。

输出

1
2
3
4
skillspector scan ./my-skill/                                  # 终端(默认)
skillspector scan ./my-skill/ --format json --output report.json
skillspector scan ./my-skill/ --format markdown --output report.md
skillspector scan ./my-skill/ --format sarif --output report.sarif # SARIF 2.1.0

JSON 顶层结构:skill / risk_assessment / components / issues / metadata。

  • risk_assessment.recommendation ∈ SAFE | CAUTION | DO_NOT_INSTALL
  • metadata.llm_requested / llm_available 告诉你这次有没有跑 LLM
  • metadata.inference_usage 会记录每次 LLM 调用的 token 计数(provider 暴露的话),从不估算缺失的 token。这个透明度值得表扬。

批量扫描

1
2
python -m contrib.batch_scan.batch_scan ./my-skills/ --no-llm
python -m contrib.batch_scan.batch_scan ./my-skills/ --workers 20 -f json -o report.json

六、上手

1
2
3
4
5
6
# 用 uv 装 CLI
uv tool install git+https://github.com/NVIDIA/skillspector.git
# 以后更新:uv tool update skillspector

# 要跑 MCP server 的话装 mcp extra
uv tool install 'skillspector[mcp] @ git+https://github.com/NVIDIA/skillspector.git'

不想装 Python 就用 Docker(镜像基于 python:3.12-slim-bookworm):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
make docker-build                                    # 或 docker build -t skillspector .

# 扫本地目录(挂到容器的 /scan)
docker run --rm -v "$PWD:/scan" skillspector scan ./my-skill/ --no-llm

# 带 LLM 分析(用 .env)
cat > .env <<'EOF'
SKILLSPECTOR_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
EOF
docker run --rm -v "$PWD:/scan" --env-file .env skillspector scan ./my-skill/

# 出报告到宿主机
docker run --rm -v "$PWD:/scan" skillspector scan ./my-skill/ --no-llm --format json --output report.json

输入形态

1
2
3
4
skillspector scan ./my-skill/                        # 本地目录
skillspector scan ./SKILL.md # 单个文件
skillspector scan https://github.com/user/my-skill # Git 仓库
skillspector scan ./my-skill.zip # zip

体积上限(防 zip bomb)

上限 值 作用
INGEST_MAX_BYTES 100 MiB 流式 URL 下载、zip 解压后总大小、Git clone 后磁盘占用
INGEST_MAX_ZIP_MEMBERS 10,000 单个 zip 的条目数
MAX_FILE_BYTES 1 MB 单个文件被 analyzer 读取的上限(下游限制)

任一 ingest 上限被突破会 fail closed 抛 IngestLimitExceededError。这个设计是对的 —— 扫不可信输入,先把 DoS 面堵上。

扩展形态

  • MCP server:skillspector mcp(需装 [mcp] extra)
  • Pi extension:装成 Pi 工具,在 agent 会话里直接扫
  • OpenCode extension:装成 OpenCode 工具和 /skillspector 命令

后两条很实用 —— 让 agent 在自己装 skill 之前先扫一遍。

七、边界与风险(必须诚实说的部分)

1)项目只有 6 个月,144 个 open issues。 2026-03-21 创建。虽然提交很活跃(昨天还在推),但对一个要当「安装门禁」的工具来说,成熟度需要时间。

2)静态分析有误报,官方自己做了抑制机制。 README 有专门的 Suppressing False Positives (baseline) 章节,支持 glob 规则和 fingerprint baseline。这个功能的存在本身就说明 FP 是真实痛点 —— 第一次接入时大概率要花时间调 baseline。

3)风险分累加会饱和,可能过于严格。 见第四章。内容丰富的 skill 容易被一堆 MEDIUM 推到 DO_NOT_INSTALL。建议自己读 issues 数组而不是只看 recommendation。

4)默认 exit code 对 CAUTION 返回 0。 接 CI 时必须显式加 --fail-on-findings,否则门禁是虚设的。

5)--no-llm 会漏掉语义类模式。 比如 TP4 描述与行为不符明确标注是 LLM-powered。不配 LLM 的话覆盖度会打折。

6)1 MB 单文件上限意味着大文件只看一部分。 这是个工程权衡,但也意味着藏在 1MB 之后的 payload 扫不到。

7)它只能抓已知的 71 个模式。 分类法很全,但新颖手法仍然会漏。别把 SAFE 当成「保证安全」,它只是「没命中这 71 条」。

8)扫的是 skill 本身,不是它安装的依赖运行时行为。 静态分析看不到实际执行时会发生什么。

八、上手建议(按这个顺序来)

  1. 先 uv tool install + --no-llm 跑一遍。 零成本,先看清输出长什么样。
  2. 第一件事是扫你已经装了的那些 skill。 用 contrib/batch_scan 批量跑一遍 ./my-skills/,你大概率会有意外发现 —— 记住 26.1% 这个基数。
  3. 别只看 recommendation,要读 issues 数组。 分数会饱和,具体哪条命中才是你要判断的东西。
  4. 尽早建 baseline。 把你确认过的误报用 fingerprint baseline 存下来,否则每次重扫都会被同样的 FP 淹。
  5. 接 CI 一定要加 --fail-on-findings。 默认 CAUTION 返回 0,门禁不生效。
  6. 重要 skill 配 LLM 分析。 --no-llm 快且免费,但会漏 TP4 这类语义模式。对高价值 skill 值得花钱。
  7. 优先关注这几类:TT3 / TT5(污点追踪的 CRITICAL)、AST8 / AST9(危险执行链与反射绕过)、SC2(curl | bash)、RA1(运行时自我修改)、LP1(MCP 未声明能力)。这几条命中了基本可以直接判定别装。
  8. 把 Pi / OpenCode extension 装上。 让 agent 在装 skill 之前自己扫一遍,比事后审计有效得多。

九、一句话结论

如果你在用 Claude Code / Codex 并且会装第三方 skill,SkillSpector 应该是你工作流里的默认一步 —— 3.1 万个 skill 里 26.1% 有漏洞、5.2% 疑似恶意,这个基数下「先扫再装」不是洁癖,是基本卫生。

给开发者的第一动作:uv tool install git+https://github.com/NVIDIA/skillspector.git → 用 contrib/batch_scan 把你现有的 skills 目录全扫一遍(--no-llm,免费)→ 挑出 CRITICAL 和 HIGH 的那几条人工看一眼。 这一步花不到十分钟,但你可能会发现某个天天在用的 skill 正在读你的环境变量。

评论Comments