教程Claude Code

Claude Security 插件实测:让一组代理把代码库扫一遍找漏洞,再出补丁给你审

Claude Security 是官方的深度扫描插件:几个代理分头读代码找漏洞,再由三个评审各自投票,通过的才进报告。本站在一个埋了问题的小应用上跑了两次:都找到了路径穿越,还找出一个我们没埋的崩溃问题并验证属实;但两次的结论数量和评级不一样,出补丁两轮都被它自己的评审否决。合计 3.70 美元。

发布 · 约 11 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-12。

本页目录(11 节)
  1. 要准备什么
  2. 安装
  3. 跑一次全库扫描
  4. 结果长什么样
  5. 它找得准不准
  6. 两次扫描,结果不一样
  7. 出补丁:两轮都被它自己否决了
  8. 花了多少,用了多久
  9. 通过中转站用,碰到的两件事
  10. 用下来的建议
  11. 这次实测没有覆盖到的

这篇教程解决一个问题:想对整个仓库,或者一个分支的改动,做一次像安全研究员那样的检查。不是改代码时顺手查一下,而是专门花一段时间把它过一遍。

Claude Code 有一个官方插件做这件事,叫 Claude Security。它和本站之前写过的安全检查插件不是一个东西,先把几样分清楚。

什么时候 工具 管什么
写代码的当下 安全检查插件(security-guidance) Claude 写出来的代码里的常见漏洞,当场改掉
想查的时候,单次 /security-review 命令 对当前分支做一遍安全检查
想查的时候,深度 Claude Security 插件(这一篇) 多个代理扫整个仓库或一段改动,结论经过独立评审,可以再出补丁
提 PR 的时候 Code Review(Team 和 Enterprise 套餐) 带着完整代码库背景审 PR
CI 里 你已有的静态分析和依赖扫描 规则明确的检查

文档的说法是这几层叠着用,谁也不替代谁。

实测环境
日期 2026-10-12
版本 Claude Code 2.1.296,Claude Security 插件 0.12.0
系统 Ubuntu 24.04,Python 3.12.3,Node.js 22
模型 会话用 Sonnet 5.5
连接方式 通过酷站AI 中转站,不是直连官方
被扫的代码 一个 6 个文件、93 行的 Node.js 小应用,是本站测安全检查插件时留下的,里面有故意埋的问题

要准备什么

文档列的前提:

  • 付费套餐、API 访问,或者第三方云厂商接入。扫描要用到动态工作流来编排它的代理。
  • python3 3.9 以上。插件的脚本只用标准库,不会往你机器上装东西。
  • Git,扫改动和出补丁要用。只做全库扫描的话,没有版本控制的目录也行。

模型不用另外配。干活的代理用的就是你会话当前的模型,想换就在开扫之前 /model。文档提到有几个辅助步骤固定用 sonnet 这个别名。

安装

会话里输入:

/plugin install claude-security@claude-plugins-official

本站用的是终端里的写法,装到当前项目:

claude plugin install claude-security@claude-plugins-official --scope project
✔ Successfully installed plugin: claude-security@claude-plugins-official (scope: project)

装到项目范围,会在项目里生成一个 .claude/settings.json,里面记着启用了这个插件。这个文件后面会惹一点麻烦,先记住它。

卸载:claude plugin uninstall claude-security。

跑一次全库扫描

插件只加了一个命令:/claude-security。输入后出来一个菜单:

What would you like to do?
❯ 1. Scan codebase (Recommended)
     Find vulnerabilities across the whole repository or a scoped part of it.
  2. Scan changes
     Find vulnerabilities in a branch's or pull request's diff, or one specific commit.
  3. Suggest patches
     Turn reported findings into verified patch files you review and apply yourself.

三件事:扫整个代码库、扫一段改动、给已经报出来的问题出补丁。

选第一项。它先读了一遍仓库的文件清单,然后问范围:

Which part of the repository should be scanned?
❯ 1. Whole repository (~6 files, `medium`) (Recommended)
     The tree is small, so the whole thing can be read in one scan.
  2. Scoped scan — one area
  3. I don't know — you choose

仓库大的时候,这里会建议你一次只扫一块,比如接口层或者登录相关的代码。

选完范围是一道确认:

This scan may take a while and may use a significant number of tokens. You will need to leave
Claude Code open while the scan completes. Are you sure you want to continue?

确认之后才真正开始。扫描在后台跑,输入框下面有一条进度:

◯ scan  ▰▰▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱  2/5 · 1m10s · ↓ 34.2k tokens

它开头提醒了一句:这个插件在 auto 模式下最顺,否则代理每走一步都要你批准。

五个阶段

第二次扫描时,Claude Code 弹出了运行动态工作流的确认框,里面列了这条流水线的五个阶段:

阶段 做什么
Inventory(清点) 把仓库分成几个组件,每个顶层目录要么扫,要么明确说明跳过
Threat model(威胁建模) 每个组件一个建模的代理
Research(研究) 按“组件 × 漏洞类别”分格子,每格一个研究员
Sweep(补漏) 把上一步没覆盖到的地方补一遍
Panel(评审) 三个评审从三个角度各自投票:能不能到达、影响多大、有没有已有的防护

评审这一步是它和“让 Claude 看一眼有没有漏洞”最大的区别。只有评审通过的才写进报告。

结果长什么样

第一次扫描用了 5 分 35 秒。结束时它在对话里给了摘要(原文是英文,下面是大意):

扫描完成:2 个发现通过了验证,都是高危,都在 app.js。

  • F2,/files 的路径穿越(app.js:23)。未登录的人可以读到进程能读的任何文件,比如 GET /files?name=../config/payment.js。
  • F1,远程让进程崩溃(app.js:32)。查询参数没有校验,处理函数没有 try/catch,一个不带参数的 GET /go 就会抛出未捕获的异常,进程退出。同一行还会重定向到你给的任何地址。

3 个评审都确认了这两条。还有第三个候选,config/payment.js:3 里写死的值,被 3 个评审里的 2 个否决,认为是占位符。如果它是真的密钥,请轮换,因为 F2 能读到这个文件。

扫描过程中没有执行任何代码,每一条结论都是读代码得出的。

每次扫描在仓库里新建一个带时间戳的目录:

CLAUDE-SECURITY-20261012-141902/
├── CLAUDE-SECURITY-RESULTS.md        报告,给人看的
├── CLAUDE-SECURITY-RESULTS.jsonl     同样的内容,一行一条,给程序用
├── CLAUDE-SECURITY-RESULTS.sarif     SARIF 格式,GitHub 代码扫描等工具能读
├── CLAUDE-SECURITY-REVISION-….json   这份报告对应哪个提交、什么力度、验证得多彻底
└── .gitignore                        内容只有一个 *

那个 .gitignore 是有意放的:不管你怎么 git add,报告都不会被顺手提交进去。要把报告留在历史里做审计记录,删掉这个 .gitignore 再提交。

报告里每条发现有固定的几段:影响、位置和 CWE 编号、具体是什么、怎么利用、前提条件、怎么修、评审的投票结果。开头还有一节“覆盖范围”,写明读了哪些文件、哪些没读、为什么。

它找得准不准

这个应用是本站自己埋的雷,所以可以对答案。

问题 是不是我们埋的 第一次扫描 第二次扫描
/files 路径穿越 是 报了,高危 报了,高危
/go 开放重定向 是 并在崩溃那一条里提了一句 单独一条,低危
不带参数的请求让进程崩溃 不是 报了,高危 报了,中危
config/payment.js 里写死的密钥 是(一个假的,以 _0000 结尾) 列为候选,被评审否决,认为是占位符 没有进报告,出补丁时被评审顺带提到
/whoami 把请求头里的内部令牌原样返回 是 没报 没进报告,出补丁时被评审顺带提到

三点观察。

一、它找出了一个我们没埋的问题。 进程崩溃那条不在我们的清单里。报告说它是读代码推出来的,没有运行过。本站自己动手验证了一下:把应用跑在本机回环地址上,发一个不带参数的 GET /go:

TypeError [ERR_HTTP_INVALID_HEADER_VALUE]: Invalid value "undefined" for header "Location"
    at ServerResponse.writeHead (node:_http_server:439:8)
    at /go (app.js:32:9)

进程退出了。结论属实。路径穿越和开放重定向也一并验证了,都能复现。

二、对那个假密钥的判断是对的。 它确实是个占位符。评审没有把它算成漏洞,但在摘要里留了一句“如果是真的就轮换”。这比一看到 sk_live_ 就报高危要有用。

三、/whoami 没有报。 这个路由把调用者自己发来的请求头返回给调用者,严格说泄露不了别人的东西,不报也说得过去。但它是一个标着“上线前删掉”的调试路由,人来审的话多半会提一句。

两次扫描,结果不一样

同一份代码(第二次只多提交了一个设置文件),同样的范围和力度:

第一次 第二次
发现数量 2 3
路径穿越 F2,高危 F1,高危
进程崩溃 F1,高危 F2,中危
开放重定向 并在 F1 里 F3,低危
用时 5 分 35 秒 约 7 分钟

最要紧的那条两次都在,评级也一样。差别在边上:同一个问题是并成一条还是拆成两条,崩溃算高危还是中危,编号也换了。

文档对此是明说的:扫描不是确定性的,同一份代码扫两次可能报出不同的东西。它的建议是定期扫,并且用那份版本记录把每份报告和它对应的提交、设置对上号。

实际用的时候记住两点:别拿编号当长期的引用(“修一下 F1”要带上是哪一份报告);一次没报不等于没有。

出补丁:两轮都被它自己否决了

报告出来后,可以让它为选中的发现出补丁。本站只要了路径穿越那一条。

先碰到的坑:工作区不干净

第一次要补丁,它拒绝了:

F2 的补丁我先不做,因为前提条件没满足:那份扫描报告的版本戳里 revision.dirty 是 true。扫描时工作区里有未提交的内容,现在看到的是未跟踪的 .claude/ 目录。补丁是针对已提交的代码生成的。

那个没提交的 .claude/ 目录,就是安装插件时生成的设置文件。

所以按项目范围装这个插件之后,先把 .claude/settings.json 提交掉(或者加进 .gitignore),再扫描。 否则扫描能跑,但那份报告出不了补丁,得重新扫一遍。本站就是这样多花了一次扫描的钱。

顺带一提,报告文件名里会带上这个状态:第一份的版本记录叫 …-dirty.json,第二份没有这个后缀。

然后:补丁没有通过评审

提交干净、重新扫描之后,补丁流程跑了起来。每个补丁先在仓库的一份临时副本里起草,然后交给另外的代理评审。它跑了两轮,最后的结论:

路径穿越没有出补丁。评审两轮都没放行。

  • 第一轮:用 fs.realpath 做目录限定,修掉了穿越。评审否决了它,因为改变了可接受的输入:name=readme.txt/ 从 404 变成了 200,uploads/ 里指向外部的符号链接从 200 变成了 404。
  • 第二轮:改成纯字面的检查,这两处差异都消失了。评审仍然否决,理由是补丁里的输入检查顺带修了另一条发现里的崩溃问题,超出了这一条的范围。
  • 两轮的共同点:穿越本身都被堵住了,对抗评审也没找到补丁引入的新攻击路径。

按流程,第二次被否决就不再重试,所以没有 .patch 文件。

patches/ 目录里留下的是说明,不是补丁:

patches/
├── F1.md           为什么没出补丁、被否决的那一版改了什么、报告原本的修复建议
├── PATCHES.md      索引,以及评审顺带看到、但不属于这一条的其他问题
└── patches.jsonl   机器可读的记录,status 是 declined

文档里写着补丁要过三关才会写出来:确实修了这一条、没有引入新的漏洞、除此之外行为不变。过不了就给你一张说明。本站碰到的正是第三关:两版都把洞堵上了,但都“多做了一点”。

这个标准很严。好处是它交出来的补丁可以放心地单独审、单独合;代价是像这次这样,两个问题挨在同一行代码上时,单修一个很难不碰到另一个。它自己给的建议也是这个:把路径穿越和崩溃两条放在一起出补丁。本站没有再跑这一轮。

补丁不管出没出,它都不会自动应用。文档反复强调这一点:补丁写在 patches/ 下,要你自己 git apply,并且建议每个补丁单独开一个 PR。

花了多少,用了多久

用时 花费估算
第一次扫描(含菜单) 5 分 35 秒 1.00 美元
第二次扫描,加两轮出补丁 约 25 分钟 约 2.70 美元
整个会话合计 36 分 31 秒 3.70 美元

全程用的是 Sonnet 5.5。合计的用量:输出 14.7 万 token,读缓存 830 万,写缓存 55.5 万,98% 的输入来自缓存。

这是 93 行代码的价钱。仓库大了,研究阶段的格子(组件数乘以类别数)会成倍增加。文档在确认框里写的“可能用掉大量 token”不是客套话。先在一个小范围上试一次,看清楚单价再放大。

这是 Claude Code 按官方价格估算的数字,不是中转站实际扣的金额。

通过中转站用,碰到的两件事

整个流程通过中转站跑通了,动态工作流、后台代理都正常。中途有两处和接入方式有关的现象:

  • 扫到一半弹出一个通知,要按回车才继续显示。 内容是 auto 模式的分类器计费方式在调整,而“你的请求经过的地址不兼容这次更新”,所以这个会话仍按原来的方式计费。它不影响功能。但如果你走开了,回来会发现界面停在这个通知上。
  • 出补丁时有一次命令被拒,界面提示 Classifier unavailable。 auto 模式靠一个分类器决定放不放行,那一下它没响应,命令就被拒了。代理自己重试之后继续了。

另外,文档提到辅助步骤用 sonnet 这个别名。如果你的中转站上模型名和官方不一样,要用 ANTHROPIC_DEFAULT_SONNET_MODEL 把别名指到一个能用的模型上。

用下来的建议

  • 先提交,再扫描。 尤其是按项目范围装插件之后那个 .claude/settings.json。
  • 用 auto 模式。 否则几十个代理的每一步都要你点。
  • 开扫之前定好模型。 想省钱用 Sonnet;本站没有比较过不同模型找到的东西差多少。
  • 报告里的结论自己验证一遍再动手修。 它声明了没有运行代码。这次三条都属实,但那是这一次。
  • 扫了没报,不等于没有。 两次扫描的差别就在眼前。它是多一层检查,不能代替依赖扫描和人工审查。
  • 紧挨着的几个问题,一起出补丁。

这次实测没有覆盖到的

  • 只在一个 93 行的小应用上试过。 大仓库上的耗时、花费、分块扫描都没有数据。
  • “扫改动”那一项没有试:扫一个分支的差异、一个 PR、一个提交。
  • 没有成功拿到一个补丁,所以补丁文件长什么样、git apply 之后的效果没有看到。把两条合在一起出补丁那一轮没有跑。
  • 只用了 Sonnet 5.5,中等力度。 换模型、换力度的效果没有比较。
  • SARIF 文件没有导进 GitHub 试。
  • 只扫了两次。 “不确定性”有多大,两次说明不了。
  • 每条结论只对照了我们自己埋的问题。 这个应用里有没有两次都没被发现、我们自己也不知道的问题,无从判断。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章