
把AI编码代理当成安全审计员来用听起来很高效但真正落地上手之后你会发现它要么漏掉关键风险要么把正常代码当成漏洞疯狂误报。我最近做的security-audit-skill项目就是为了解决这个能用但用不精的问题。简单来说这是一个面向大模型编码代理的安全审计技能包里面包含整套审计指令、扫描脚本、检查清单和报告模板让代理从能回答安全问题的聊天机器人变成一个知道该按什么流程查代码、查依赖、查配置的审计工具。如果你正在给 AI 代理写 skill或者想用 Codex、Claude Code、OpenCode 这类工具做安全审计这篇博文里的设计思路、目录结构和踩坑记录可以直接拿去参考。1. 从 security-audit-skill 这个项目说起到底在解决什么问题1.1 AI 代理安全审计为什么不能只靠临场提问最初我用大模型做代码安全审计时走的是最朴素的路子把仓库代码贴给对话窗口然后问一句有没有漏洞。结果不稳定到让人怀疑人生——同一个仓库上午问和下午问给出来的结论重点完全不同隔一次对话它又会盯着某个无关紧要的console.log说存在信息泄露风险。后来我想明白了问题在哪大模型本身是概率语言模型不是确定性扫描器。它擅长的是理解和归纳而不是稳定复现同一套检查流程。代码安全审计恰恰是最依赖标准化流程的工作什么时候查什么、查出来的东西怎么分级、从哪里开始查、怎么确认误报每一步都要有约定。你把流程交给对话运气结果当然飘忽不定。这就是security-audit-skill要补的位它不是让模型自由发挥去做审计而是把审计方法论固化成一个可复用的技能包。模型调用这个 skill 之后会按照既定顺序执行先收集项目清单再扫描依赖和密钥接着查危险 API 与注入点最后汇总成带严重级别的报告。整个流程被强制住了模型只负责在每个环节做判断和补充解释而不是从头自由发挥。1.2 这个 skill 适合谁、在什么场景下真正有用从我的实际使用情况看下面这几类人最值得尝试使用人群典型场景能解决的问题独立开发者自己维护的开源项目没预算买商业扫描工具在发版前快速过一遍常见风险安全工程师拿到一个不熟悉的代码仓库需要前置摸底减少人工逐文件翻代码的时间DevSecOps 工程师把安全审计接入 CI/CD 或 PR 检查让 AI 代理按统一标准执行检查团队技术负责人给团队引入 AI 编码代理又担心代码质量给代理装上安全红线意识需要注意它不能替代真正的 SCA 商业产品或者人工代码审计。像业务逻辑漏洞、复杂的权限绕过这类问题靠静态扫描和模型推理很难百分百命中。它的价值在于把低垂的果实快速摘掉——硬编码密钥、过期的高危依赖、SQL 拼接、危险函数调用这些高频且模式化的问题用 skill 来查效率非常高。1.3 这个项目最初的形态是什么样的项目刚起步时我没有直接写代码而是先整理了一份人工安全审计清单。我把平时做渗透测试和代码审计时的检查项按依赖安全、密钥泄漏、注入风险、危险函数、配置风险分成五类然后逐条想这条能让 AI 代理怎么判断。这一步特别关键。因为大模型不像传统扫描器那样读正则就行它需要的是判断规则 判断上下文。比如检查 SQL 注入你不能只给一条正则你得告诉它哪些参数会进入数据库查询方法、这些方法的参数是否来自用户请求、前面有没有经过参数化处理。这些都是上下文判断模型擅长但你得把上下文供给它。所以我为每个检查项都写了对应的references文件这为后续脚本开发打下了基础。2. skill、agent、脚本名字虽多但别搞混2.1 skill 和 agent 到底有什么区别我在搜索相关资料时看到最多的疑问就是skill 和 agent 的区别。网上说法五花八门不少文章把这两个概念混成一团实际上去做项目时根本不能稀里糊涂。我的理解是这样的Agent 是拿主意、做决策和执行循环的主体。它能看到用户需求、决定调用什么工具、分几步完成任务、自己做中间检查最后交付结果。它是一个干活的人。Skill 是这个人手里的标准作业手册。它不负责决策它只负责告诉 agent面对这类任务时按什么步骤走、用哪些工具、输出什么样的格式。Agent 可以在不同场景下调用不同 skill一个 agent 完全可能挂载十几个 skill。脚本则是更底层的执行单元。skill 内部可以包含脚本用来跑确定性扫描、生成中间结果但不是说有个脚本就叫有了 skill。脚本没有指令上下文它不知道什么时候该跑、跑完输出给谁看、结果要怎么组织。层面职责典型例子Agent决策、规划、多轮执行、总结Claude Code、Codex CLI、OpenCodeSkill任务方法论与流程手册security-audit-skill、会议纪要素材脚本具体执行某项原子操作密钥扫描脚本、依赖版本检查脚本打个不太严谨的比方Agent 是你请来的顾问skill 是他手里的检查表脚本是检查表里那台用来测量的仪器。顾问决定做哪几项检查仪器负责把读数测出来检查表保证每一项都不漏。缺了 skill顾问容易凭经验随机发挥缺了脚本检查全凭肉眼效率太低。2.2 主流框架里的 skill 通用结构目前我接触过的三个主流编码代理里对 skill 的支持方式不完全一样但底层思想高度一致都是在一个约定目录下放结构化文件模型启动后读取并理解它。Claude Code使用.claude/skills/skill-name/SKILL.md或用户目录下~/.claude/skills/。SKILL.md 带有 YAML 前置元信息描述何时启用、用什么工具。Codex较新的 Codex CLI 支持~/.codex/skills和项目级.codex/skills同样读取 SKILL.md 作为入口。OpenCode支持~/.config/opencode/skill/与项目级.opencode/skill/用类似机制加载技能。通用结构基本是SKILL.md技能的入口文件包含名称、描述、触发条件、步骤概述。scripts/存放可执行脚本供模型按需运行。references/静态参考资料比如规则表、模式库、最佳实践文档。templates/输出模板比如审计报告模板。assets/其他辅助文件。我建议不管目标是哪个框架文件结构都按这个通用形式组织。这样后续迁移框架时只需要改路径和少量元信息核心逻辑不用重写。2.3 设计 skill 时的边界感哪些不该写进 skill见过很多刚上手的朋友恨不得把 AI agent 能做的所有事情都写进 skill结果把 SKILL.md 写成了几百行的大型文档。这个方向是错的。Skill 里应该写的是稳定的方法论而不是每个问题的标准答案。比如安全审计 skill稳定的方法论是收集项目结构、扫描密钥、检查依赖、查危险函数、汇总报告。这五步在几乎任何代码仓库里都适用属于可复用流程。而那些会变的东西——比如某个项目特殊的框架版本、某个团队自定义的安全规范、某次审计的具体目标范围都不该写死在 skill 里。它们应该由 agent 在执行时从用户对话或项目配置中读取动态拼接到执行流程里。我当时给自己定了一条规则能在运行时获取的信息就不要写进 skill 文件。写进 skill 的内容必须同时满足两个条件跨项目复用、执行路径相对稳定。违反这条规则skill 很快就会变得臃肿且难以维护。3. security-audit-skill 的目录结构与元信息设计3.1 一套可以直接复制的目录结构我最终落地的目录结构是这样的security-audit-skill/ ├── SKILL.md ├── scripts/ │ ├── collect_manifest.py │ ├── run_secret_scan.py │ ├── run_dep_check.py │ └── generate_report.py ├── references/ │ ├── owasp_top10_mapping.md │ ├── secret_patterns.json │ ├── dangerous_apis.txt │ └── risk_levels.md ├── rules/ │ ├── context_rules.md │ └── output_rules.md └── templates/ └── audit_report_template.md每个文件都有明确职责collect_manifest.py自动识别项目类型收集package.json、requirements.txt、go.mod、Cargo.toml等依赖清单以及源码文件分布。它是后续扫描的基础。run_secret_scan.py基于正则与熵检测扫描硬编码密钥包括 API Key、Token、私钥块等。run_dep_check.py读取依赖清单中关键包版本与内置漏洞库进行比对。dangerous_apis.txt记录危险函数/方法名列表供模型在代码搜索时参考。比如 Python 的eval、exec、pickle.loadsJavaScript 的eval、new FunctionSQL 拼接常见的字符串与执行方法。context_rules.md给模型看的上下文约束告诉它哪些情况算误报哪些情况需要进一步确认。output_rules.md规定报告输出格式、严重级别定义和置信度说明。audit_report_template.md最终报告模板避免模型每次生成格式都不一样。这套结构的好处是脚本负责跑确定性扫描模型负责上下文判断规则文件负责统一口径。三层各管一摊互不越权。3.2 SKILL.md 的关键字段怎么设计SKILL.md是整个技能包的入口模型首先读取它。用了一段时间后我觉得开头这几个元信息字段最重要--- name: security-audit-skill description: 对当前代码仓库执行安全审计包括依赖风险、密钥与敏感信息、危险 API 调用、注入类风险并输出带严重级别与修复建议的报告。 allowed-tools: grep, find, bash, file, glob, git, python only-if: 用户要求进行安全审计、代码漏洞检查、依赖风险评估、密钥泄漏排查 ---description千万别写得太泛。有些模型的 skill 触发机制是靠语义匹配描述写得越具体越容易在合适的时候被准确触发。如果你写成帮助用户检查代码质量模型可能在用户问这段代码有没有 bug的时候就错误加载审计技能反而干扰正常对话。allowed-tools很实用。它限制了 skill 在执行时能调用的工具范围防止模型为了找某个字符串而做出特别离谱的操作。比如我这边只放开文件搜索、目录遍历、简单脚本执行这几个能力不允许它随便调用网络请求工具。only-if这个字段不是所有框架都支持但如果框架支持条件加载这个字段能大幅提高触发精度。我写了几个典型的用户意图让模型能快速判断现在是不是该用这个 skill。3.3 规则文件是给模型看的不是给人看的很多人在做 skill 时容易把规则文件写成团队公约语言抽象充满原则性表达。但你要记住规则文件的读者是模型不是同事。我后来重写了context_rules.md把里面所有抽象表述改成了可判断可操作的描述。举几个实际例子不要写注意误报而要写当 .env 文件处于 .gitignore 中时该密钥不视为泄漏当密钥仅出现在测试文件且使用明显测试值时降级为信息级。不要写检查危险的 SQL 写法而要写若参数直接拼入 SQL 字符串且不是通过参数化查询接口执行标记为高危 SQL 注入风险若参数经过白名单校验或类型转换后进入查询标记为低危或正常。这两条改动带来的效果提升是肉眼可见的。模型对模糊指令的理解方差很大但对带明确条件的指令输出稳定性会明显提高。写规则时尽量把你的判断经验翻译成条件分支而不是风格描述。4. 从审计流程到脚本实现这套 skill 到底怎么工作4.1 审计流程可以拆成哪三个环节整个 skill 在运行时会被拆成三个环节收集、扫描、汇总。这样一个简单分层让模型在执行时的思路非常清晰不会在某个环节无限深挖导致流程失控。环节一收集。由collect_manifest.py完成。它会扫描仓库根目录识别常见的包管理文件判断技术栈统计源码文件类型和数量并输出一个简明的项目清单。这个清单决定了后面模型把精力放在哪里。如果检测到只有 Python 代码就不必花时间查node_modules的依赖。环节二扫描。这是脚本最密集的部分。密钥扫描脚本、依赖风险脚本、危险函数扫描脚本会依次执行。每个脚本只做一件事输出原始结果给模型。环节三汇总。脚本不负责下结论结论由模型基于脚本输出和上下文规则来下。模型把原始结果逐条带入context_rules.md里的判断条件过滤误报确定严重级别最后按照audit_report_template.md的格式输出。这个分层带来一个额外好处每个环节都能独立测试。我可以在没有大模型的情况下直接跑脚本确认识别逻辑没问题再让模型去装配。省掉了很多调试时间。4.2 一次完整扫描的标准操作步骤以典型的 Node.js 仓库为例整个 skill 的执行路径如下运行collect_manifest.py发现项目根目录有package.json、src/、config/技术栈为 Node.js Express。读取package.json中的依赖列表运行run_dep_check.py发现某个版本存在已知原型链污染漏洞输出依赖风险高危。运行run_secret_scan.py在config/prod.env.example和src/auth.js中发现疑似密钥。模型再判断prod.env.example中的值明显是占位符降级为信息级src/auth.js中的密钥看起来像真实 token且该文件未被 gitignore标记为高危密钥泄漏。模型读取dangerous_apis.txt用 grep 在源码中定位eval、child_process.exec、字符串拼接 SQL 等调用点。模型对每个调用点做上下文分析。比如发现eval(req.query.code)并且输入直接来自用户参数没有过滤逻辑判定为高风险代码执行注入。所有结果按严重级别汇总生成带文件路径、行号、问题描述、修复建议的 Markdown 报告。整个过程脚本运行不到一分钟模型判断和报告生成大概两三分钟。对一个中小型仓库来说这个耗时完全可以接受。4.3 实测复盘一份报告里的结果到底长什么样拿一份我实际测试过的 Demo 项目为例报告核心部分大致长这样风险类别位置严重度置信度问题简述密钥泄漏src/auth.js:42高危高疑似 OpenAI API Key 硬编码依赖漏洞package.json高危高axios 版本存在 SSRF 相关已知漏洞SQL 注入src/db.js:88高危中用户输入直接拼接 SQL危险函数src/utils.js:12中危中使用eval处理外部输入配置风险config/index.js低危高CORS 配置为*允许所有来源这份报告的价值在于每一条都有关键文件位置和具体原因修复时不需要再从头翻代码。特别是密钥泄漏和依赖漏洞这两条在高置信度条件下可以直接交给开发者处理省下了大量重复劳动。4.4 置信度分级防止模型自嗨我最开始在报告模板里只放了严重度没放置信度结果模型经常自信地给出错误结论。后来我在output_rules.md里强制要求每条发现必须附带置信度高、中、低三档。置信度的判断规则也写得很细高脚本输出明确匹配 上下文规则中没有任何降级条件 文件不是测试/示例文件。中模式匹配但上下文信息不完整比如无法确定用户输入是否真正可控。低只是模式相似大概率是误报仅作为提示信息保留。把置信度从人工经验变成模型的输出要求之后报告的可用性有了质的提升。看到高置信度高危漏洞可以直接安排修复看到低置信度的提示不会浪费时间去深究。5. 把这套 skill 装进主流代理框架安装与调用实测5.1 Claude Code 中的安装与权限细节在 Claude Code 中使用时我把 skill 放在项目根目录.claude/skills/security-audit-skill/下。Claude Code 会自动扫描SKILL.md并在对话中按条件加载。这里有一个权限相关的细节要注意Claude Code 对工具调用有权限提示机制模型运行脚本之前会征询用户确认。如果目录里脚本很多频繁弹权限确认会影响体验。我的做法是在项目配置文件里明确授权该 skill 目录下的脚本执行权限例如只允许python解释器运行scripts/下的脚本其他路径的脚本一律不给权限。既减少了打断也把风险控制在可接受范围内。调用方式很简单直接在对话里说对当前仓库做一次安全审计即可。模型会根据only-if条件判断是否启用 security-audit-skill。如果仓库里既有代码审计需求也有普通的代码补全需求它会优先处理触发条件更明确的任务。5.2 Codex 和 OpenCode 的挂载方式Codex 的 skill 目录既支持用户级也支持项目级。我习惯把通用型 skill 放在~/.codex/skills/把跟具体仓库相关的规则放在项目.codex/skills/下。security-audit-skill属于通用型审计技能我放在用户级目录这样无论打开哪个项目都能用。OpenCode 的配置路径是~/.config/opencode/skill/或项目级目录加载逻辑类似。在这两个框架里SKILL.md 的元信息已经足够触发加载不需要额外注册中心。有一点需要提醒不同框架对allowed-tools字段的解析方式不一样。有些框架会严格按照字段限制工具调用有些只把它当成参考。因此在换框架运行时一定要先在测试仓库里跑一遍确认脚本执行权限符合预期再拿到真实项目上使用。5.3 上下文管理不要把所有输出都塞给模型我踩过最深的一个坑是脚本输出太多导致上下文爆掉。比如密钥扫描脚本默认会输出所有疑似命中的行一个稍大的仓库能跑出几百行结果。模型根本看不过来后面的判断质量急剧下降。解决思路是让脚本预聚合。run_secret_scan.py不会输出每一行命中而是按文件名聚合输出文件路径命中数量最可疑的前 3 个位置具体匹配类型AKIA 开头、sk- 开头、PEM 私钥块等模型只需要看聚合结果就能判断哪些文件值得深入检查。这项改动让整个 skill 的可用性提升了一大截上下文占用至少减少了 60%。5.4 把审计输出接进 CI 或 PR 流程除了交互式调用我还把 skill 包装成了 CI 脚本。做法是在 CI 里用命令行启动代理框架传入执行 security-audit-skill的指令指定审查范围是本次git diff --name-only列出的文件。这样每次 PR 提交时代理只审计变更文件既快又有重点。这种用法的好处是代码审查不只是看 diff 风格还能自动带出安全风险提示。坏处是 CI 跑大模型有额外成本所以我在 YAML 里做了限制只在主干分支和包含依赖文件变更的 PR 上触发完整审计其他分支只做密钥扫描加依赖检查。6. 每次做完审计后我把这几个坑认真记了下来6.1 skill 编写阶段的三大误区第一个误区是过于详细。SKILL.md 写得像操作手册全文每个步骤都有十行解释模型反而失去弹性只知道按步骤执行遇到异常情况不会灵活处理。后来我把 SKILL.md 压缩到逻辑框架级别把为什么的内容挪到references里模型既能看到流程也能在遇到具体问题时查到背景知识。第二个误区是脚本输出不设上限。前面提到上下文管理本质上是编写阶段就犯下的错。现在我的所有脚本都强制带输出裁剪参数默认只输出前 N 条结果。不是怕结果多是怕结果多到模型处理不了。第三个误区是忽视 git 上下文。没有明确审查范围时模型容易把整个历史代码都扫一遍浪费时间结果还分散。现在 SKILL.md 里明确写着优先询问用户要审计全部代码、最近改动还是某个目录。只有用户没给范围时才默认全量扫描。6.2 运行时高频问题误报、权限、路径假设我把运行阶段遇到的问题整理成了清单问题原因解决方式模型把测试密钥报成高危没有判断测试目录在 context_rules 中明确测试/示例文件降级规则脚本找不到项目根目录不同仓库的目录层级不同先运行 collect_manifest.py 确认根目录密钥扫描对短随机串误报高熵检测阈值过低提高阈值并要求至少匹配已知前缀特征依赖检查结果过期本地漏洞库更新不及时增加更新时间提示建议定期拉取最新数据模型在某个脚本上反复运行缺少执行次数上限在 SKILL.md 中规定每个脚本最多运行 N 次权限问题是最容易被忽略的。以我的经验在非本地环境下部署时脚本解释器的路径可能不同python3和python的差异就能让 skill 直接跑不起来。我在脚本入口统一做了解释器探测同时保留错误提示避免模型卡在运行脚本那一步。6.3 后续扩展与更细粒度规则引擎联动security-audit-skill 目前的定位是静态审计常用项但它预留了很清晰的扩展口。接下来我打算把语义化的风险规则也纳入进来也就是在rules/下增加按语言分类的规则文件比如rules/python_security_rules.md、rules/javascript_security_rules.md。这些规则会描述特定语言的高危模式供模型在扫描阶段参考。还有一个方向是接入真实的实时漏洞库。因为离线比对只能覆盖内置数据库里的已知漏洞对于新发布的 CVE 无能为力。如果脚本在检测依赖时能主动获取最新漏洞信息审计结果的时效性会大幅提升。不过这个功能涉及网络调用放在代理框架里需要单独设计权限策略不能无条件放行。根据我个人的使用体验skill 类的项目最忌讳一次做太大最好的迭代方式是先跑通最小闭环再做增量扩展。security-audit-skill 从最初一份检查清单到现在能稳定输出审计报告中间改了很多版但每一步都只解决一个具体问题。如果你也在做类似的 agent 技能建议你先把收集—扫描—汇总这个闭环跑通再逐步往里加规则和参照库。安全审计这件事流程稳定比功能多更重要。