SKILL 30 / SAFETY · DEEP DIVE

装别人的 Skill 有多危险

两条路:以你的身份执行代码,或以文档的身份改写指令——而它们都不经过沙箱

2026-10-01 · BigCat

官方那页第一句话就把结论写完了:「一个你装上的 Claude Code plugin 可以用你的用户权限在你机器上执行任意代码。」本期给三条当天就能跑的准入命令、官方的四步审查、三层信任到底保证了什么,以及最反直觉的一条——你根本不用「装」,git clone 加一次 claude -p 就够了。

同系列 → Skill 12 接 MCP 的安全边界 · Skill 29 安全类 Skills 榜单

// WHY THIS MATTERS

skill 与 plugin 已经是软件供应链的一段,只是没人把它当依赖管。NVIDIA 自己的扫描器在 README 里给了个刺眼的数:其研究数据集的子集里 26.1% 的 skill 存在漏洞、5.2% 疑似带恶意意图(据 NVIDIA/SkillSpector GitHub 仓库页,2026-10)。本期不停在吓人这一步:先给能跑的准入流水线,判据放后半程,用来在这几个已经点名的工具之间取舍。

// 01

它替你干掉什么:把「装之前看不见」变成「装之前能列清单」

论断:审查第三方 skill 不需要你会读漏洞,只需要把它要往你机器上放的东西列出来。这件事有现成的四条命令。
# ① 先盘点机器上「已经」有什么(覆盖十几种 client 的配置位置)
uvx snyk-agent-scan@latest

# ② 对下一个候选:静态扫一遍(--no-llm = 不调模型,纯静态)
uv tool install git+https://github.com/NVIDIA/skillspector.git
skillspector scan ./candidate-skill/ --no-llm

# ③ 官方:不起会话,列出它到底往你这儿装什么
claude --plugin-dir ./that-plugin plugin details <plugin-name>

# ④ 结构体检——注意它只验结构,不验安全
claude plugin validate ./that-plugin

四条分别替你干掉什么、什么时候别用

面板只说「有一个 hook」,不说它跑什么。 官方的审查步骤因此点名要手读三个文件:hooks/hooks.json(每个 hook 实际执行的命令)、.mcp.json(每个 server 的命令或 URL)、bin/(目录里的每一个文件)。这三个文件没看过,前面三条命令都只是热身。
// 02

机制:为什么权限规则和沙箱接不住这件事

论断:plugin 的危险不在「它能调工具」,而在它有一半内容根本不走工具调用这条路——那一半沙箱和权限都看不见。

代码执行路(四个入口)

官方把边界划得很直白:权限规则与沙箱管的是 Claude 发起的工具调用,不管 plugin 自己跑的代码——hooks 和 MCP server 由 Claude Code 跑在沙箱之外,带你完整的用户权限。只有当 Claude 用 Bash 去调 bin/ 里那个可执行文件时,那才算一次工具调用,你的权限规则才拦得到。

指令注入路(这条更难防)

skills、slash 命令、agents 不执行代码,它们作为指令进入上下文,改写模型打算用已有工具干什么。而 skill 的 allowed-tools 能自己给自己授权:官方原话是工作区信任不 gate 这个字段——「一个 skill 可以给自己授予很宽的工具访问权,所以在一个仓库里跑 Claude Code 之前,先审它签入的 skill 的 allowed-tools」。skill 正文里的 ! 命令行同理:那是在内容送给模型之前在你机器上跑的。

最反直觉的一条:你不用「装」。 官方那张「信任目录前都有什么会跑」的表里——项目 skill 的 hooks 与 allowed-tools:Used(claude -p 跑在从未信任过的目录里也 Used);.mcp.json 里的 server:-p 下不问你就连,批没批都一样;而 permissions.allow:Not used,要等你点信任。危险的那一半默认生效,保护你的那一半默认不生效。

所以陌生仓库里这么跑

# 一次性最省事:什么项目侧定制都不读
claude --bare -p "审一下这个仓的结构"
# 或者按需关:只读用户设置 / 关掉全部 hook / 点名拒某个 .mcp.json server
claude --setting-sources user -p "…"
claude --settings '{"disableAllHooks": true}' -p "…"
# 常驻两条:杀掉 skill 正文里的 shell 执行;按名字拒某个 skill
{ "disableSkillShellExecution": true,
  "permissions": { "deny": ["Skill(skill:deploy)"] } }

disableAllHooks 写在你的用户设置里不够——项目设置优先级更高,能把它改回 false,所以要走 --settings。deny 规则里裸写 Skill 是把整个工具从模型上下文里摘掉;Skill(skill:<name>) 这种写法则能按它的任何别名命中同一个 skill。

// 03

用法与边界:三层信任各自保证了什么,以及怎么把版本钉死

论断:信任层级只决定你要多审一层还是少审一层,它从不替代逐个审——官方自己就这么写:marketplace 的名字说明谁发的目录,不说明目录里每个 plugin 干什么。
层级 marketplace 名字 谁审过 auto-update 默认 你还要做什么 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 官方 claude-plugins-official 等 14 个 Anthropic 维护 开(两例外) 读 Will install 面板 保留名(agent-skills / life- 或其合作方 就够;留意它会自更新 sciences / claude-for-legal …) 社区 claude-community claude.ai 提交 关 自动扫描≠逐个人工审 (仓库 anthropics/claude- +自动化安全扫 条目近乎都钉 commit SHA plugins-community) 描+获批分发 第三方 其余全部(含同事的、你自己的) 没人审 关 官方四步全套:面板→源码 →hooks.json/.mcp.json/bin/ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 冒名装不进来:官方与社区这些名字,Claude Code 只接受源在 github.com/anthropics/ 下的 marketplace。 名字对、源不对 → 整个 marketplace 停止加载,连已经从它装的 plugin 也一起停。

社区目录的 README 把它的保证写得很具体:每个条目都经 claude.ai 提交、通过自动化安全扫描、获批分发;仓库本身是只读镜像,直接提 PR 会被自动关闭,每夜从内部流水线同步。配套的硬约束在客户端:该目录给近乎每个条目钉了 commit SHA,你想装另一个 commit,Claude Code 直接拒绝。第三方那一层就没有这些——官方原话是 Anthropic 不审第三方 marketplace。

审完即变:把版本钉死

卸载不等于清干净

claude plugin uninstall <plugin> --scope <你装的那个 scope> 之后,它的文件还会留在 ~/.claude/plugins/cache/ 里十四天才被后台清理,要立刻没就自己删那个目录。真正不信任的是 owner 的话,连 marketplace 一起删——那会把从它装的每个 plugin 都卸掉。

团队侧:六个具名的托管设置键

strictKnownMarketplaces(源白名单,别名 allowedMarketplaces;空数组 [] 连官方一起挡)· blockedMarketplaces(黑名单,先于白名单判)· disableSideloadFlags(拒 --plugin-dir / --plugin-url / --agents 与 CLAUDE_CODE_PLUGIN_DIRS)· strictPluginOnlyCustomization(只许 plugin、托管设置或内置来源的 skills / agents / hooks / mcp)· allowManagedHooksOnly · pluginTrustMessage(往安装前那条信任警告后面接你自己的话)。

// 04

代价与替代品:扫描器给的是线索,不是判决

论断:这一类工具最大的代价不是误报,是风险分长得太像绿勾。而最便宜的替代品一直在那儿:只抄正文,不装它。

两笔要认的账

不装它,你还能怎么办(从便宜到贵)

  1. 只抄不装。 把它的 SKILL.md 读完,把真正有用的那二十行抄进你自己的 .claude/skills/——不要它的 hooks、不要 bin/、不要它捎带的 MCP server。方法论型 skill 的价值几乎全在正文里,而正文是唯一你能一眼读完的部分。
  2. 一次性试。 claude --plugin-dir ./that-plugin 只为这一次会话加载,不写进 enabledPlugins(注意:团队开了 disableSideloadFlags 时这条会被拒)。
  3. 先读官方的,把「正常」校准出来。 anthropics/skills(据其 GitHub 仓库页 2026-10 约 17.9 万 star;多数 Apache-2.0,docx/pdf/pptx/xlsx 四件套为 source-available)与 anthropics/claude-plugins-official。读过几个正常的,你才看得出第三方那个为什么多了一个 hook。
锁死也有代价。 strictKnownMarketplaces: [] 会把官方 marketplace 一起挡掉;设了任何白名单却漏写 {"source": "skills-dir"},你自己放在 ~/.claude/skills/ 下带 .claude-plugin/plugin.json 的那些会停止加载(纯 SKILL.md 不受影响)。而且一台机器只要在挡掉官方的策略下启动过一次,policy 改回来也不会自动重试注册。
连官方自己的 skill 都没把自己说成成品:anthropics/skills 的 README 原话是「这些 skill 仅供演示与教育用途」,并要求「在依赖它们处理关键任务之前,先在你自己的环境里充分测试」。星数不是安全属性。

// 一页行动卡

今天就做这三件:① uvx snyk-agent-scan@latest——先把机器上已经有的盘一遍,多数人会在这一步发现自己忘了装过什么。② 下一个第三方候选按顺序走完:skillspector scan ./x --no-llm → claude --plugin-dir ./x plugin details <name> → 手读 hooks/hooks.json、.mcp.json、bin/。③ 写两行常驻配置:"disableSkillShellExecution": true,并让第三方 marketplace 的 auto-update 保持关闭(默认就是关的)。

按需再加:claude plugin validate(你自己发 marketplace)· strictKnownMarketplaces + disableSideloadFlags(给团队定策略)· Skill(skill:<name>) deny 规则(某个 skill 留着但不许模型自己触发)· claude --bare -p(要在陌生仓里跑非交互)· trailofbits/mcp-context-protector(它捎带了 MCP server,要首次信任即固定)。

明确别做:在陌生仓里直接 claude -p · 把「通过扫描」当成「安全」 · 以为 star 多就安全 · 给第三方 marketplace 开 auto-update · 把 --dangerously-run-mcp-servers 写进脚本 · 只卸 plugin 不删 marketplace · 读了 Will install 面板就以为读过 hook · 把 disableAllHooks 只写在用户设置里。

// 术语表

workspace trust(工作区信任)
你第一次在某个目录起交互会话时那个信任对话框。它 gate 的是 permissions.allow 这类「给仓库更多权」的东西,不 gate hooks 与 skill 的 allowed-tools。
Component inventory
claude plugin details 打印的清单:skills 与命令、agents、hooks(含每个 hook 的事件)、MCP 与 LSP server。装之前用 --plugin-dir 就能看。
保留 marketplace 名
十四个只属于官方的 marketplace 名字。用这些名字但源不在 github.com/anthropics/ 下的,Claude Code 拒绝加载,所以第三方无法冒名。
审完即变(TOCTOU)
你审查的那一刻和它实际运行的那一刻之间,内容被换掉。plugin 里由 auto-update 造成,对策是关掉自动更新并钉 #ref 或 sha256。
allowed-tools
skill frontmatter 字段,在调用它的那一轮内免提示地预批一批工具。它只放宽不收紧,而且工作区信任不 gate 它。
skills-dir plugin
放在 ~/.claude/skills/ 或项目 .claude/skills/ 下、带 .claude-plugin/plugin.json 的那种。设了 marketplace 白名单就得补一条 {"source": "skills-dir"},否则它们停止加载。

// 三个想不通的地方

社区目录既然过了自动化安全扫描,为什么还要自己审一遍?
因为「自动化扫描通过」和「有人读懂了它要干什么」是两件事,而前者的边界就是上面 SkillSpector 自己列的那串:静态、英文、不跑运行时。更关键的是扫描器判不了意图匹配——一个声称格式化代码的 plugin 带一个往外发请求的 hook,模式上完全合法。所以层级的正确读法是「还要补多少审」:官方那层补「读一眼 Will install」,社区那层补「它为什么需要这个 hook」,第三方那层四步全套。社区目录真正给你的硬保证其实是另一条——条目钉了 commit SHA,审过的那一版就是装进来的那一版,这比扫描结论值钱。
skill 只是一段文本,为什么反而比 MCP server 难防?
因为 MCP server 是进程,而进程有边界可谈:换个容器、限制出口、只读模式、全限定名——Skill 12 那期那一整套旋钮都还在。skill 没有进程,它直接变成模型的指令,而「模型该不该听这段话」没有系统调用层面的拦法。更麻烦的是它看起来无害:一份 Markdown,diff 很短,review 时很容易一眼扫过。真正的不对称在这儿——MCP 的风险你能配置掉,skill 的风险只能读掉。所以这一类唯一真正有效的门禁是「正文我读完了」,这也是为什么「只抄不装」不是将就,而是这一层最合理的默认做法。
SkillSpector 给了 70 分,到底该不该装?
这个问题问错了,而问错的方式很典型:风险分是线索的密度,不是结论。正确的用法是拿分数决定看多久——低分抽查三个文件,高分逐行读或者直接放弃。真正该回答的是三个具体问题:这些命中落在哪个文件(hook 和 bin/ 里的命中跟正文里的措辞完全不同量级)、它声称的功能需不需要这个能力、以及不装它你损失什么。第三个问题通常最有决定性:绝大多数时候答案是「损失二十行提示词」,那就根本不用纠结 70 分该怎么读。

// 延伸阅读