
前阵子我把自己的安全审计流程整个重做了一遍核心变化不是换了哪个扫描器而是把审计经验写成了一份security-audit-skill交给我常用的 AI Agent 去执行。说实话第一次跑通的时候我是有点被震到的——之前人工审一个中型项目光梳理入口点、查依赖、比对漏洞模式就得折腾大半天现在把技能包加载进去Agent 能自己完成初步审计、标注风险点、给出修复建议我只需要做复核和决策。这篇文章我想把整个设计思路、目录结构、编写过程和踩过的坑完整记录下来给正在折腾 Agent skill 机制、以及想把手头安全审计经验固化成资产的朋友一个参考。这个技能包不挑场景只要是做代码安全审计的人都能用上你在给开源项目做漏洞排查、公司内部做上线前安全评审、或者像我一样维护一个老项目需要定期过一遍安全基线都可以把经验沉淀成 skill 让 Agent 帮你干体力活。我尽量把话说得直白里面涉及的安全知识会顺带讲清楚原理不熟的读者也能看懂大概。1. 为什么需要 security-audit-skill1.1 传统代码审计的四个痛点先聊聊我这几年的真实感受。代码安全审计这个活儿表面上是有章可循的拿到代码、看依赖、查输入输出、找危险函数、比对漏洞模式、出报告。但实际做起来四个痛点一天比一天明显。第一是人力跟不上。一个业务系统几十万行代码靠人肉去翻光把关键入口点找出来就够喝一壶。第二是覆盖不完整。人的注意力会疲劳经常会漏掉藏在深层调用链里的问题尤其是那种“看着不起眼但实际上能打到敏感接口”的路径。第三是经验太分散。团队里每个人的审计习惯不一样有人先看认证鉴权有人先看注入类漏洞同样一份代码不同人审出来的结果能差出一截。第四是跟进成本高。发现了一个漏洞从定位到验证再到修复后复测中间要来回沟通很多次所有信息散落在聊天记录和 PDF 里时间一长就找不到了。这些问题不是靠“多招几个人”能解决的必须要有一个能把经验标准化、能自动执行的东西。这是我做security-audit-skill的第一层动机——把审计这件事变得可复用、可度量。1.2 从 prompt 到 skill经验固化是本质区别很多人会问我直接写一段 prompt 让大模型帮我审计不就行了为什么要搞一个 skill我自己的体会是prompt 是“一次性的口述”skill 是“结构化的能力包”。你让 Agent 用 prompt 审计它每次都要重新理解你的要求审计的重点、格式、规则完全取决于这次 prompt 写得够不够详细一旦会话长了它还容易忘掉前面的约定。而 skill 相当于给 Agent 装上了“安全审计专业模块”——里面写清楚了审计的范围、优先级、判断标准、输出格式甚至带了可调用的脚本和工具描述Agent 在执行时是有一套明确 SOP 的不会随机发挥。打个比方。你让一个新来的实习生“去看一下这段代码有没有安全问题”他大概率东看看西看看最后给你一个很虚的结论。但你给他一本《代码审计操作手册》里面写了先看什么、后看什么、每种问题长什么样、发现之后怎么报他就能按流程产出稳定的结果。skill 就是这本手册而且是能直接执行的数字化手册。1.3 security-audit-skill 到底适合谁在动手之前我先想清楚了这个技能包的服务对象。它最适合三类人。第一类是安全工程师尤其是做应用安全AppSec的。你们的日常工作就是审代码、查依赖、盯上线把重复性工作交给 skill能把时间省下来去做更复杂的逻辑分析。第二类是后端和全栈开发者你们不一定是安全专家但希望在上线前自己能先过一遍常见风险skill 给了你们一套“安全基础检查单”。第三类是正在研究 Agent 工程化的人你们可能对安全不熟但对 skill 机制感兴趣这篇文章里的目录结构、规则写法、工具对接方式完全可以迁移到别的领域。当然它不能取代专业的人工审计也不能替代商业级 SAST 平台这个定位必须搞清楚。它的价值是“把审计动作标准化并且明显提速”而不是“声称自己绝对安全”。2. 技能包的整体设计思路2.1 目录结构SKILL.md 、规则、脚本、参考资料设计security-audit-skill的第一步是确定目录结构。目前主流的 Agent Skill 机制不管是 Claude Skills、Codex 的自定义技能还是 OpenCode 这类开源工具的 skill 目录都遵循类似的约定一个描述文件加上若干辅助资源。我采用的目录如下。security-audit-skill/ ├── SKILL.md # 技能入口文件Agent 先读它 ├── rules/ # 审计规则库按漏洞类型拆分 │ ├── injection.md │ ├── auth.md │ ├── xss.md │ ├── ssrf.md │ ├── deserialization.md │ └── dependency.md ├── scripts/ # 可执行的审计辅助脚本 │ ├── scan_deps.py │ ├── grep_patterns.sh │ └── report_gen.py └── references/ # 参考资料供 Agent 查证细节 ├── owasp_top10_notes.md └── cwe_dict.md这个结构不是我随便拍脑袋定的。SKILL.md是 Agent 的“启动入口”它决定了技能何时被触发、以什么逻辑运行rules/把审计知识拆成独立文件便于按需加载也方便我随时补充某类漏洞的细则而不用改动主文件scripts/放真正能跑起来的工具脚本让 Agent 从“看代码”升级为“能执行检查”references/则像一本工具书Agent 不确定某个漏洞的定义时可以去查。2.2 以 OWASP Top 10 为骨架的规则分层安全审计的知识面很宽如果不分层Agent 很容易抓不住重点。所以我把审计目标分成了三层每一层对应不同的检查深度。第一层是“必查项”对应 OWASP Top 10 2021 里的核心类别注入Injection、失效的身份认证Broken Authentication、敏感数据泄露Sensitive Data Exposure、XML 外部实体XXE、失效的访问控制Broken Access Control、安全配置错误Security Misconfiguration、跨站脚本XSS、不安全的反序列化、使用含有已知漏洞的组件、日志与监控不足。这一层是每次审计都要过的属于保底项。第二层是“高影响项”根据项目类型扩展。比如 Node.js 项目会额外查原型链污染、依赖供应链风险、命令注入Java 项目会查表达式注入SpEL/OGNL、Fastjson 反序列化、任意文件读写Go 项目会查 goroutine 泄漏、路径穿越、JWT 实现缺陷。这一层的价值在于贴合具体技术栈而不是拿着一套通用规则到处套。第三层是“纵深项”只在时间充裕或者项目风险等级高的时候启用比如业务逻辑越权、多步流程绕过、条件竞争、隐私合规问题等。这些通常需要理解业务语义纯靠模式匹配做不准我一般标记为“需人工复核”。2.3 两类执行模式静态扫描加动态验证设计的时候我特意把执行模式分成了两类避免 Agent 只会一种招数。第一类是“纯静态分析模式”。Agent 拿着规则去读代码、跟踪数据流、匹配危险函数然后输出存在疑问的位置和理由。这种模式的好处是快、不依赖运行环境适合初步筛查。第二类是“工具辅助验证模式”。在静态分析发现问题后Agent 调用脚本或者对接外部 SAST 工具做交叉验证比如用 Semgrep 的规则集跑一遍或者用 CodeQL 写查询去确认数据流是否真的能从入口点到达 sink。我个人的经验是两类模式要配合使用。纯靠规则匹配会有不少误报纯靠人工验证又太慢。skill 里我写明了流程先用模式匹配粗筛再用工具验证关键路径最后把无法自动确认的项列入“人工复核清单”。这样既保证了效率也留住了准确率。3. 实操落地从零编写一个可用的 security-audit-skill3.1 把 SKILL.md 写成 Agent 真正能执行的 SOPSKILL.md是整个技能包的大脑。如果这个文件写得模棱两可后面所有规则都白搭。我见过很多人把 SKILL.md 写成了功能列表只写“本技能可以做代码审计”但完全没有说清楚触发条件、执行步骤、输出格式——这种文件 Agent 根本不知道该怎么用。我自己的写法是把它当成一份给“聪明的初级审计员”的操作手册。开头用 YAML frontmatter 写清楚技能名称和触发描述描述里要包含足够多的触发关键词比如“审计”“安全”“漏洞”“注入”“XSS”“依赖风险”等这样 Agent 在遇到相关请求时能优先想到加载这个技能。正文部分我会按步骤写清楚审计流程先读项目结构确认技术栈和关键配置文件。列出所有入口点路由、接口、事件处理函数。按规则库逐项进行静态匹配记录候选风险。对候选风险做数据流分析判断是否真实可控。汇总输出按严重级别排序给出修复建议。这里有个很关键的心得不要指望 Agent 一次性把所有事儿干完。你在 SKILL.md 里要求它“严格按步骤执行”它确实会按但遇到复杂项目时更聪明的做法是让它在每个步骤结束后先汇报中间结果确认无异议再继续。我在文件里加了“每完成一个阶段先总结当前发现再进入下一阶段”的指令这样一旦它跑偏我能及时发现并纠正。3.2 规则文件怎么写Agent 才看得懂规则文件是 skill 和普通 prompt 最大的区别所在。它们把“什么样的代码算有漏洞”变成了 Agent 可以照着判断的检查项。我以injection.md为例给你看看我的写法。先说结论部分SQL 注入的本质是“不可信数据拼接进了 SQL 语句”。判断标准有三条——数据源是否来自用户输入HTTP 参数、请求头、文件上传数据是否未经验证或转义数据是否最终进入了数据库查询、命令执行、模板渲染等危险操作。这三条我会逐字写进规则文件因为 Agent 需要清晰的标准来推理而不是只给它一个名词。然后是代码示例部分。我会列出危险写法和安全写法。危险写法比如直接把参数拼进 SQL、用字符串拼接调用操作系统命令安全写法比如参数化查询、白名单校验、最小权限原则。每个示例都配上简单注释说明问题出在哪个环节。最后是特殊情况说明。比如某些 ORM 组件看起来是参数化的但order by后面的字段名没法绑定参数导致这里仍然可能出问题。这类边界案例如果不在规则里写清楚Agent 很容易漏掉。3.3 对接 Semgrep 和 CodeQL让 Agent 手里有真家伙光靠 Agent 读代码是不够的我还在 skill 里接了真实的安全工具其中用得最多的是 Semgrep 和 CodeQL。Semgrep 的规则是 YAML 格式核心是定义pattern可以精确匹配代码结构。skill 里的scripts/grep_patterns.sh会调用 Semgrep 的规则文件对目标项目做一次扫描。我在规则库里维护了一个精简规则集比如检测eval被执行、检测child_process拼接命令、检测innerHTML直接渲染用户输入。Semgrep 的好处是规则写得快、跑得快、误报也相对好控制。我摘一段简单的 Semgrep 规则示例你可以感受一下格式rules: - id: no-eval-user-input patterns: - pattern: eval($ARG) - metavariable-regex: metavariable: $ARG regex: .*(req|request|params|body|query).* message: 检测到 eval 执行用户可控内容存在代码注入风险 severity: WARNING languages: [javascript, python]CodeQL 比 Semgrep 重但能做跨函数的数据流分析。它的查询语言是 QL写起来像在“查数据库”——你把所有代码当成数据用查询去追踪污染源到汇聚点。我在 skill 里内置了一个最简单的查询 demo用来追踪“HTTP 请求参数是否流向了exec调用”。这个查询不是万能的但足以应对很多常见场景也给 Agent 提供了一条“工具能确认人工只做复核”的路径。3.4 在 Codex / Claude 里接上技能跑通流程写好了文件接下来就是接入 Agent。我平时主要用到两种方式。一种是直接把security-audit-skill目录放到 Agent 配置指定的 skills 目录下。以目前常见的 CLI Agent 为例启动时会扫描技能目录发现SKILL.md后就会把它注册进可用技能列表。你只需要在对话里说“加载 security-audit-skill 并审计这个项目”它就会按技能文件里的 SOP 执行。另一种方式是把脚本做成可调用的命令Agent 通过工具调用来触发。比如在工具配置里声明一个run-sast工具实际执行时调用scripts/grep_patterns.sh targetAgent 在流程中自行决定何时调用这个工具。这种做法在 Agent 需要执行真实扫描而不是纯粹“读代码”的场景下特别实用。我第一次完整跑通时是拿一个练习用的 Node.js 项目做的测试。技能加载后Agent 按照流程先识别出技术栈是 Express MongoDB然后逐个规则检查在“依赖风险”这一步发现lodash版本偏旧又在 XSS 检查中发现某个接口直接返回了未转义的用户输入。整个过程大约十分钟它生成了一份带严重级别和修复建议的报告。说实话这个初版表现已经超出了我对“AI 审代码”的预期。4. 现场实战一次完整的依赖与代码安全审计4.1 场景设定审计一个 Node.js 项目为了让效果更直观我拿了一个练手的 Node.js 项目作为审计目标。这个项目是个简单的博客后端用了 Express 框架数据库走 MongoDB身份认证用的是自己实现的 JWT 逻辑。我先用security-audit-skill跑了一遍完整流程然后人工对结果做了复核整个过程记录在下面。项目结构大概是这样的入口文件app.js路由目录routes/下面有auth.js和posts.js中间件目录middleware/下面放了authMiddleware.js模型层直接用 Mongoose。从风险角度看这个结构非常典型——认证逻辑手写、无统一异常处理、静态资源目录没有访问控制这些都是常见的审计切入点。4.2 Agent 审计输出与人工复核对照技能包先执行了依赖扫描。脚本读取package.json比对本地维护的已知漏洞清单发现lodash4.17.15存在原型链污染漏洞jsonwebtoken8.5.1有一个验签算法混淆风险在特定配置下。这一步结果准确因为lodash的漏洞在 4.17.20 之前确实存在而 JWT 库的算法混淆问题也确实是 8.x 的老毛病。接着进入代码规则匹配阶段。Agent 在auth.js中发现登录接口直接拼接 MongoDB 查询存在 NoSQL 注入风险在posts.js中发现删除文章接口只判断了用户是否登录没有校验该用户是否就是文章作者在authMiddleware.js中发现 JWT 校验逻辑里没有限制alg字段理论上攻击者可以通过把算法改为none来伪造 token。我逐条做了人工复核。NoSQL 注入的判定是对的——直接把req.body.username传进查询对象确实可能被改写成{ $ne: null }之类的操作符。越权问题也是真实存在的这是典型的缺失对象级授权校验。JWT 的alg问题需要再加一层确认我检查了jsonwebtoken的实际用法发现项目没有显式指定算法白名单所以这个风险成立。三条发现全部有效没有虚报。4.3 修复建议的下发与验证闭环技能包输出的报告里每条发现都配了修复建议并且给每一项标记了建议优先级。比如 NoSQL 注入的修复建议是改用 Mongoose 的 Schema 类型约束和sanitize中间件JWT 的问题建议在verify时显式传入算法白名单[HS256]越权问题建议新增一个checkOwnership中间件。我按报告修完后又跑了一次技能包做复测。这次 Agent 在日志里记录了“所有高优先级问题已确认修复残余风险集中在低优先级的配置加固项”并且把复测前后的审计结果做了对比。整个闭环走完我对这个技能包更有信心了——它不只是“发现一个问题”而是能支撑从发现到验证的完整流程。5. 常见问题与排查技巧实录5.1 Skill 加载失败或者 Agent 不按规则走这是很多人第一次接触 skill 机制时最常遇到的问题。明明把目录放进了指定位置但 Agent 就是不加载或者加载了之后不按SKILL.md里的流程走。我总结下来绝大多数情况是三个原因。第一个是描述文件写得不好触发关键词不够明确——Agent 根本没意识到当前任务应该用这个技能。对策是把关键词写得宽一些但又不要过于宽泛比如“审计”和“安全”要同时出现或者明确写出“查一下这个项目有哪些安全问题”“做一次渗透测试前置的代码审查”这类典型句式。第二个是技能目录的路径没配对——不同 Agent 对技能的默认加载位置不一样你放到 A 的位置但 Agent 实际读的是 B 的位置。这个只能靠多看日志确认没有捷径。第三个是 SKILL.md 里的指令太笼统Agent 无法转化为具体动作。解决方法是把流程拆得更细最好到“可验证”的颗粒度比如“检查routes/目录下的每个文件中是否出现了req.body或req.query如果有追踪它的流向”。还有一个小技巧你可以在技能文件里加上一句“如果用户没有明确指定审计范围默认按本文件中的步骤执行但开始前先输出你的执行计划等待用户确认后再继续”。这样 Agent 不会闷头跑偏你能在它动手前就看到它的思路。5.2 Fortify SCA / Audit Workbench 报许可证过期因为在安全圈子混难免会用到 Fortify SCA 这类商业工具。很多朋友遇到过audit workbench打开时报许可证过期的情况。我遇到的典型报错有两种一种是在扫描时提示license expired另一种是打开 Audit Workbench 时提示无法连接许可证服务器。先说第一种。Fortify SCA 的许可证分为评估版和正式版评估版有明确的有效期。如果你打开工具直接提示过期先检查一下系统时间有没有被改动以及许可证文件有没有被误删或替换。其次是确认你所在的环境是否能正确访问许可证服务器——很多公司内网策略会屏蔽工具默认连接的地址这种情况需要找管理员把许可证服务器地址加白名单而不是自己硬改本地时间那样反而会导致工具校验失败。第二种情况常见于用 Docker 部署的扫描环境。工作台需要和扫描器通信如果你在容器里运行 Audit Workbench要注意端口映射和容器内外的网络策略。排查时先用fortifyclient -listSSCProjects试试能不能连通服务端再逐层检查容器网络。我的建议是能直接用命令行扫描的场合尽量不依赖 GUI 工作台减少一次环境依赖就少一个坑。5.3 误报太多规则怎么收敛Agent 审计最常见的抱怨就是“报了一堆问题但很多是误报”。这通常不是 Agent 笨而是你的规则写得太宽。比如一条规则写“检测到innerHTML就报警”但项目里大量使用前端框架做数据绑定innerHTML的赋值源是静态模板而非用户输入这种就会误报。我的收敛方法是给规则加“条件限定”。检测到危险函数只是第一层第二层要看数据来源。如果数据源不是用户可控的比如来自后端渲染好的常量就降低置信度如果来源是请求参数、文件上传内容、第三方 API 返回就升高置信度。在 skill 的规则文件里我会明确写“报告问题时必须附带数据流路径路径闭环才标记为高危否则标记为需人工确认”。这样 Agent 的输出会收敛很多。还有一个实践心得保留一份“误报清单”。每次人工复核后把确认的误报案例添进 skill 的references/false_positives.md中Agent 下次遇到相似模式时会参考这份清单误报率会越来越低。这个做法其实就是在给 Agent 做“经验迭代”非常有效。5.4 扫描耗时太长、内存被吃满Agent 在做全量代码审计时可能会一次性读入大量文件或者脚本对依赖数做了全量匹配导致扫描极慢甚至内存溢出。我的经验是不要让 Agent 一口气扫描整个仓库。合理的做法是分步走。第一步先让 Agent 读项目根目录的配置文件和文件清单把代码分块第二步按风险优先级分块扫描比如先扫入口文件和认证相关模块再扫业务逻辑部分第三步把结果合并。你可以在SKILL.md里写明“如果目标目录超过 50 个文件自动按模块拆分每个模块扫描完成后先汇报中间结果”。另外脚本里也要控制资源占用。像scan_deps.py这种依赖比对脚本不要一次性把整个依赖树加载进内存用流式读取、逐条比对。跑在大仓库上时还可以让 Agent 用rg或者grep先做一次快速预筛把明确不相关的文件过滤掉再交给规则引擎做深度分析。最后再聊几句实在的每次跑完一次完整审计我都会把新的案例补进技能包里——哪个规则误报了哪个典型漏洞模式没抓到哪个修复建议可以更贴近实际都会随手更新到对应的规则文件里。这个东西跟人一样用着用着才会越来越懂你。如果你也在折腾 Agent 和 skill我建议别一味追求“听起来很牛的技能”从安全审计这种规则相对明确、输出格式固定的领域入手是最容易做出效果的。先跑通一个最小闭环再慢慢加复杂度这套玩法放之四海而皆准。