ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI代码审计不靠谱?用Skill把安全流程固化成可执行技能

AI代码审计不靠谱?用Skill把安全流程固化成可执行技能 我很久以来都有一个很深的感触让大模型帮忙做代码安全审计很多人第一反应是直接把仓库丢给它问一句“帮我看看有什么漏洞”。我自己也这么干过结果得到的不是一份能用报告的比一般级别严重的问题列表夹杂着大量误报和空泛的“建议”真正的业务逻辑漏洞又被完美错过。后来我接触到“Skill”这个概念才意识到问题不在模型本身而在于我根本没有把“一次完整的安全审计”这件事拆成模型能执行的流程。Security-audit-skill就是为解决这个断档而生的一套可复用技能包把安全审计的步骤、判别规则、证据采集脚本、报告模板固化成结构化的技能目录让Claude Code、Codex、OpenCode这类智能体在拿到代码仓库时能按照安全从业者真实的审计思路去干活。这篇文章我会完整复盘这个技能的构建思路和落地细节适合正在折腾AI辅助代码审计、或者想把自己某个领域的工作方法沉淀成Skill的人参考。1. 先说结论为什么我抛弃了“口头让大模型审代码”这种用法很多人以为“会写审计提示词”就等于“会用AI做安全审计”实践下来差距非常大。我见过最多的做法是在对话里写一大段“请你以安全专家的身份对以下仓库进行代码审计……”然后把主目录结构贴进去。这种用法不是没有价值但效果极不稳定。原因很简单大模型本身并不是“按步骤执行审计”的工具而是一个“根据上下文生成最可能结果”的推理器。你给它一个模糊任务它就会生成一个模糊回答把所有听说过的高频漏洞类型各写一小段每个都点到为止既不深入验证也不区分业务风险优先级。更麻烦的是它会把大量时间浪费在“看起来像漏洞但实际无害”的代码上真正藏在业务逻辑层的越权、竞态、认证绕过问题反而因为没有明确的排查路径被跳过了。后来我逐步意识到把安全审计做成一个Skill本质上是在做三件事把审计过程标准化、把证据采集工具化、把结论输出模板化。标准化是为了让模型知道每一步要做什么工具化是为了不依赖模型“肉眼扫描”整个仓库模板化是为了保证不同时间、不同仓库审计出来的结果格式和粒度是一致的。这三件事恰好就是Skill这种形态最擅长的。1.1 一个看似简单的问题Skill到底是个什么东西关于Skill的概念不同Agent生态里的叫法和实现细节不一样但核心思想是一致的它是一套附带有明确指令、知识条目和使用约束的“能力包”塞进Agent的运行上下文里当模型判断当前任务匹配某个Skill时就按Skill里的流程来执行。你可以把它理解成“给员工发的作业指导书”而不是“跟员工说的话”。说话容易发散指导书能把动作锁死。Skill一般包含几个层次的内容元信息用来让Agent决定什么时候该调用它主指令相当于标准作业程序辅助脚本和规则库把大量机械性工作从模型推理中剥离出去输出模板规定最终交付物的格式。开发security-audit-skill时这几个层次我全都用上了。1.2 为什么安全审计特别适合做成Skill一个领域适不适合做成Skill主要看它的流程能不能被稳定拆解、判断能不能被规则化、输出能不能被模板化。安全审计恰好三点全占。流程上一次审计必然经过“信息收集、威胁面绘制、危险模式扫描、数据流追踪、根因确认、报告输出”这些阶段顺序可以微调但骨架稳定。判断上虽然安全分析需要推理能力但大量线索其实可以通过关键词和代码模式快速定位比如硬编码密钥、反序列化入口、SQL字符串拼接、危险函数调用等这些是高度“正则友好”的。输出更是天然的模板化内容漏洞编号、位置、风险等级、修复建议每一项都可以固定下来。所以把审计技能化不是把安全分析这件事做“浅”了而是把重复劳动和推理判断分开脚本负责找线索模型负责看上下文流程负责保证不遗漏。这个分工一旦建立同样的仓库再拿出来审第二遍出来的结果一定比单纯对话要稳定得多。2. 设计阶段最重要的事把审计拆成智能体真正能执行的步骤很多人做Skill有个通病就是把希望寄托在一段很长的“角色设定”上写一大堆“你是一名资深安全专家请认真分析”然后就没有然后了。这种Skill装上去你会发现它跟普通对话没区别。真正的拆分得细到模型每走一步都知道自己在干什么、输入是什么、输出是什么。我在设计security-audit-skill时把整个审计过程拆成了五个阶段这五个阶段我详细写进了SKILL.md里后面会展示完整结构。先说说每个阶段的设计意图。2.1 阶段一项目概览与威胁面绘制这一阶段的目的是让模型对审计目标建立一个“全局坐标系”。不是眉毛胡子一把抓的“了解项目”而是带着安全意识去了解项目用什么语言和框架、有哪些对外暴露的入口HTTP路由、RPC接口、消息队列消费者、有没有明显的信任边界用户输入怎么进入系统、外部数据和内部数据在哪里交汇、关键的认证和鉴权逻辑集中在哪。很多AI审计翻车问题就出在这一步被跳过了。模型直接进入找漏洞环节结果找到一堆“理论上危险”的代码实际上那些代码根本不在攻击路径上属于无人可达的死代码。我在Skill里专门规定没有先画出入口清单和信任边界不允许开始扫描。2.2 阶段二危险模式初筛与证据定位这阶段不靠模型“读全文”而是先跑脚本做静态模式匹配。我把常见漏洞类型的代码特征整理成规则库按语言和框架分类脚本跑完以后直接输出一个“线索清单”每条线索包含文件路径、行号、命中的规则、命中的代码片段。模型拿到清单以后再针对每一条线索去读上下文。这个做法的收益非常明显。首先是把上下文窗口花在刀刃上模型不需要从头到尾“读”完整个代码库只需要针对线索去读相关文件。其次是大大降低了漏报率正则匹配和语义理解是互补的模型觉得“这行代码很普通”跳过的地方规则脚本依然会把它标记出来供复核。2.3 阶段三数据流追踪与上下文验证线索只是线索不是结论。这条规则我把Step 2产生的所有命中项强制要求模型进行二次验证这条数据从哪来、经过了哪些处理、最终流向了什么敏感操作、中间有没有过滤或转义。这个验证动作本质上是人肉审计里“从源头到尾部的路径分析”只不过现在由模型来完成。这阶段还有一个要求对于那些能直接确认的漏洞必须给出触发路径比如外部HTTP参数 → 经过Controller → 拼接到SQL → 执行否则不允许写入最终报告。这一步极大压缩了误报的比例因为很多模式命中的场景在具体上下文里其实是安全的。2.4 阶段四业务逻辑与权限控制定向排查这是整个审计里最依赖模型推理能力、也最有价值的部分。越权、水平权限、批量操作漏洞这类问题没有任何静态模式可以命中必须结合业务接口和角色模型来判断。我在Skill里给它单独开辟了一个动作单元要求模型列出所有关键业务操作和对应的权限校验函数逐一确认是否存在缺失。我承认这个阶段的效果跟模型本身的推理能力高度绑定不同模型表现差异很大。但Skill能做的是至少保证这个环节不会因为“流程没设”而被彻底遗忘。2.5 阶段五分级报告生成最后所有确认的发现项按严重程度排序生成结构化报告。输出必须包含漏洞名称、CWE编号、所在文件与行号、风险等级、触发路径、修复建议列清楚每一项。我还在模板里留了一个“人工复核优先级”字段帮助使用者在有限时间里先处理最危险的问题。3. 从零实现一个security-audit-skill目录、SKILL.md与辅助脚本讲完设计直接进入实现。我不会手把手教你怎么建文件夹但会把关键文件的组织逻辑和内容结构讲透你照着这个结构去搭基本一次能跑通。3.1 推荐的项目目录结构我的security-audit-skill最终长这样security-audit/ ├── SKILL.md ├── scripts/ │ ├── secret_scan.py │ ├── pattern_scan.py │ └── summarize_hits.py ├── rules/ │ ├── python.json │ ├── javascript.json │ ├── java.json │ └── general.json └── templates/ └── audit_report.md这是一个非常朴素的布局。SKILL.md是整个技能的心脏agents加载后弹出的就是这个文件。scripts目录放辅助工具脚本它们负责做规则引擎、密钥扫描、命中汇总。rules目录按语言存放危险模式规则这样同一套Skill可以适配不同技术栈。templates目录放报告模板保证输出结构稳定。3.2 SKILL.md其实就是一份标准作业程序SKILL.md我建议用Markdown写包含两大部分YAML格式的元信息和Markdown格式的指令正文。元信息里最重要的是description字段因为很多Agent会通过语义匹配来决定是否激活这个Skilldescription写得好不好直接影响命中率。--- name: security-audit description: 针对给定代码仓库执行结构化安全审计。当你需要识别代码中的注入、认证绕过、敏感信息泄漏、越权访问、危险反序列化等安全风险并输出分级漏洞报告时使用。 --- # Security Audit Skill 你正在执行一次代码安全审计。审计目标{target_repo} 项目技术栈{tech_stack} 业务背景{context} ## 执行前必读 1. 未完成项目概览和入口清单绘制禁止开始漏洞扫描。 2. 每个漏洞结论必须附带触发路径证据否则视为未确认。 3. 区分“确定漏洞”和“建议加固”不要在报告里互相污染。 4. 规则脚本的扫描结果是线索而不是结论必须二次验证。 ## 执行流程 ### Step 1: 项目概览与威胁面绘制 ... ### Step 2: 规则初筛 运行 scripts/pattern_scan.py 和 scripts/secret_scan.py 拿到命中清单后结合源代码验证每一条... ### Step 3: 数据流与上下文验证 ...你会发现SKILL.md的核心不是“解释什么是安全审计”而是“告诉Agent在什么时候、按什么顺序、做什么动作”。描述写得越具体模型在第一步该干嘛就越明确。我把执行前必读放在最前面是因为经验告诉我模型在长流程任务里最容易犯的错误不是能力不够而是顺序错乱。3.3 辅助脚本负责找证据不负责下结论辅助脚本是整个Skill里最容易被忽略、实际收益最大的部分。我写了三个脚本把工作分得很清楚。secret_scan.py做的任务很单纯扫高熵字符串、常见密钥前缀AKIA、ghp_、sk-等、.env文件引用、密钥赋值语句。它本质上是一个“密码学意义上的指纹扫描器”不关心这些密钥是否真是密钥只负责把可疑位置全部标记出来。这个脚本最大的好处是让模型不用靠肉眼在一千个文件里找花花的字符串。pattern_scan.py是我深度定制的规则引擎读rules目录下的JSON配置按语言匹配危险调用模式。举个最简单的例子Python模式下会匹配类似SQL拼接的写法{ checks: [ { id: PY-SQL-001, language: python, pattern: (execute|executemany|exec)\\(\\s*(f\|f|\\\\\s*\\\\s*|\\\\s*\\\\s*), cwe: CWE-89, severity: high, description: 疑似SQL语句采用格式化字符串或字符串拼接存在注入风险 }, { id: PY-DESER-001, language: python, pattern: (pickle|yaml)\\.load, cwe: CWE-502, severity: high, description: 不安全的反序列化入口 } ] }这里有个关键设计规则本身是线索不是结论。脚本把命中项导出成一个JSON或Markdown表格连同代码片段一起交给Agent去验证。通过这种方式一个40万行代码的仓库Agent需要细读的只有几百个命中点对应的文件切片而不是整个仓库。summarize_hits.py则负责把多个脚本的扫描结果合并成一个结构化摘要按严重性和文件路径排序避免Agent在上下文里翻找。它的输入是前面两个脚本的粗糙输出输出是带编号的线索列表。3.4 为什么规则库要独立成文件而不是写死在SKILL.md里这一点我得单独拿出来说。一开始我也尝试把规则写进SKILL.md的正文里后来发现维护起来很痛苦想加一条新规则要动主指令想按不同语言切换又得写满条件分支而且会把上下文撑得很臃肿。独立成rules目录之后好处非常明显。规则和流程解耦我新增一种语言的规则只需要加一个JSON文件主SKILL.md完全不用动。Agent执行到Step 2时可以按语言参数动态加载对应的规则文件而不是把所有语言规则全读一遍。对token消耗和加载速度都是质的改善。另一个容易被忽略的好处是规则文件可以被其他工具复用比如以后接CI流程做自动扫描脚本可以直接decode同一个JSON。4. 实测环节把Skill装进不同Agent差异和适配都藏在这些细节里我把自己搭的security-audit-skill在几个主流Agent环境里跑过一遍效果差异很大但这不是模型能力的差异而是Skill加载和调用机制的不同造成的。分享一些实测观察。4.1 通用对话型Agent需要手动触发但流程依然可复用在Claude这类通用对话型Agent里Skill没有明确的“安装目录”概念至少我常用版本里是这样。我的做法是把SKILL.md直接粘进对话作为系统指令的一部分再配合工作区里的脚本和规则文件。好处是控制力强整个流程完全在你的掌控下坏处是每次都要做一次装配而且上下文占用比较大。实测下来即使在这种手动模式下Skill的价值依然非常明显。同样的仓库直接用对话问和按SKILL.md流程走后者的输出质量高出一个量级。原因就是流程锁死了模型不会中途放飞自我。4.2 编码型Agent和文件操作结合得最自然在Codex、OpenCode这类以编码任务为主的Agent里Skill的表达方式是“让Agent去操作文件”。这时候SCRIPTS目录和RULES目录的优势就体现出来了。Agent可以在确认仓库路径后直接调用pattern_scan.py执行扫描把结果读回来做分析再调用报告模板生成Markdown。一个细节是这类Agent通常有很强的文件编辑能力所以我把“修复建议”输出成示例代码补丁的时候特别顺手。模型不仅能指出问题还能直接给出diff格式的修复方案体验非常顺滑。4.3 不同Agent对Skill描述字段的敏感度差异很大我在多次测试里发现Agent对SKILL.md里description字段的利用方式不完全一样。有的Agent会在启动时把所有Skill的description都读一遍然后根据当前任务做语义匹配有的Agent更依赖用户主动引用技能名。这意味着description写得好不好直接影响那些“自动匹配”的Agent能不能在关键时候想起你这个技能。我的建议是description里面一定要包含具体行为动词和关键名词比如“审计”“漏洞”“注入”“权限”“报告”这类词。别写得太文艺也别写得太泛。“帮助你进行安全相关的工作”这种描述几乎等于废的Agent根本不知道什么时候该用你。4.4 上下文窗口的取舍是绕不开的命题Skill本身也是要占用上下文的SKILL.md越长、规则越多Agent每次调用时被吃掉的空间就越大。我实测过程中发现一个比较理想的配比SKILL.md主指令控制在3KB以内规则文件按需加载脚本扫描结果按命中数量截断到大约50条线索以内报告模板单独存只在最后输出时引用。如果你发现Agent在审计过程中上下文溢出大概率不是模型问题而是Skill设计得太“重”了。我自己的优化思路永远是能在脚本里做的不要拖进上下文里。5. 报告输出让审计结果从“一串发现”变成“能直接做事的工作清单”一个审计Skill做得成不成功不看它发现漏洞的“数量”而看它的报告能不能直接驱动决策和修复。我为什么要单独做一份报告模板是因为我发现不设模板时Agent生成的报告结构千奇百怪有的按文件排序有的按时间排序有的干脆是散文根本没法拿来做后续跟踪。5.1 报告模板的设计原则我的audit_report.md模板分四个区域执行摘要、漏洞清单表、漏洞详情、修复任务分配。执行摘要用三到五句话总结整体安全状况方便管理层快速把握风险。漏洞清单表是核心每个漏洞占一行字段包括编号、严重性、类型、文件路径、简要描述。漏洞详情区对每个漏洞展开包含证据、触发路径、修复建议。修复任务分配区把漏洞和修复动作关联起来标注责任人模块名和优先级。严重性判定我让Agent按四个等级走Critical可直接被外部触发并导致严重危害High需要特定条件但影响面大Medium属于高风险习惯或边缘路径Low是加固建议。同时我要求Agent在判定等级时额外考虑两个因素该代码能否被外部输入触达、是否存在已有的缓解措施。这两条能在很大程度上压低误报优先级。5.2 明确告诉Agent哪些东西不能往报告里写这一点是我踩坑踩出来的。第一次跑真实仓库审计的时候Agent生成了一份长达几十页的报告里面大量内容是“建议优化异常处理”“建议增加日志记录”这类非安全问题还有一些是“此处使用了eval存在远程代码执行风险”但实际那个eval的执行参数是硬编码在配置文件里的根本不接受用户输入。这些噪音让报告的可信度大打折扣。后来我在SKILL.md里加了一条硬约束只报告有明确触发路径和可利用前提的安全问题加固性建议单独放在附录不允许和漏洞混在一起如果规则脚本命中了某个模式但验证后发现没有可利用路径不写入报告但在附录里保留一条“已排查项”记录。加了这条之后报告内容一下子清爽了很多。5.3 让审计结果形成闭环从报告到修复报告是给人看的但Skill能做的还可以更多。我最近在尝试把审计结论的“修复建议”字段标准化为带代码示例的格式这样Agent在后续对话里可以直接被用户要求“按报告第3条修复”从而生成补丁。这也意味着SKILL.md里需要提供一条“修复模式”指令。当用户要求修复时Agent要回到对应的漏洞详情读原文上下文改代码再重新跑一遍规则脚本验证。这个闭环一旦打通security-audit-skill就不只是在“检查代码”而是在参与整个软件安全质量的管理循环。我个人觉得这才是它最值得投入的方向。6. 我踩过的坑和认为值得试的扩展方向最后这部分纯粹是实践里沉淀下来的经验。Skill开发不复杂难的是让它真正好用。几句话不一定对但对你可能有启发。6.1 坑规则太多反而拖垮了Agent的判断力刚开始我恨不得把所有CWE都写进规则库觉得规则越多越安全。结果Agent每跑一步都要对照几十上百条命中项复盘上下文爆掉、判断速度慢、误报率还上去了。后来我砍掉了大量低signal规则只保留真正能一击致命的危险模式诸如SQL注入、反序列化、命令执行、硬编码密钥、不安全的文件操作、越权函数缺失其余的都靠模型本身的语义能力去识别。规则是给模型做粗筛的不是替它做全量思考的追求“小而准”远比“大而全”重要。6.2 坑不给业务上下文审计就会变成“纸上谈兵”同样一段代码在面向公网的服务和在内网离线工具里风险评估完全不同。我第一次测试时没给Agent任何业务背景它把一些只在内网依赖环境里运行的代码判成Critical实际上那些接口根本不受外部网络触达。后来我在Skill的使用说明里强制要求审计前务必备注部署环境、是否对外、认证方式、数据敏感级别。这一步操作成本极低但让审计结果的准确率提升非常明显。6.3 值得试的扩展接入CI、做Diff审计、沉淀团队规则security-audit-skill目前的形态还停留在“仓库级审计”我有几个正在实践的扩展方向分享出来供参考。一是接入CI流水线对每个MR的变更文件做Diff级审计。改动小、上下文占用少、反馈速度快是性价比非常高的用法。二是建立团队自己的规则库把公司历史事故里抽象出来的代码模式下沉成JSON规则这样每次审计都是用全公司在“安全上踩过的坑”来兜底。三是配合其它Agent技能使用比如让审计技能自动触发文档生成、自动给相关维护人发送报告组合成更复杂的多智能体工作流。以上这些方向Skill本身不用大改只需要在流程节点上预留好接口。这也是我坚持把Skill拆成“主指令规则脚本模板”三层结构的原因每一层都可以独立演进而不至于牵一发而动全身。如果你正在做自己领域的第一个Skill我建议你也从一开始就想清楚这个分层问题别把什么都堆在一个文件里。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进