如果你也做过代码审计,应该对那种感觉不陌生:仓库越来越大、漏洞报告越堆越多、安全团队永远在救火,而真正能拍板“这里必须改”的人就那么几个。我们组之前接了一个系统改造的审计需求,单是核心服务就8万多行代码,按老办法一个个文件过,至少要三周,而且大家最怕的不是慢,是漏——某个不起眼的位置被人传了参数进来,最后变成了注入点,这种案例在历史漏洞库里一抓一大把。
后来我们换了一条路:把大语言模型拉进审计流程,做了一个内部代号 OpenClaw 的代码审计辅助系统。目前这套东西已经在我们这边跑了小半年,大版本迭代过三轮,误报压下来之后,人工复核量差不多少了一半。这篇文章把 OpenClaw 从设计到落地整个链路拆开讲一遍,包括为什么这么设计、核心实现怎么做、实际跑出来的效果如何、以及我们踩过的一堆坑。适合想用大语言模型提升代码审计效率的安全工程师、研发负责人,也适合刚接触 AI 安全方向的同学当个参考。
1. 传统代码审计的痛点:为什么我要自己做一套 OpenClaw
1.1 人工审计的死穴:量太大、人太少、经验断层
先说说为什么放着现成的静态扫描工具不用,非要折腾大模型。传统的人工代码审计,本质上是“让有经验的安全工程师去读代码,然后凭脑子里的漏洞模式做判断”。这个方法没有问题,问题出在三点。
第一是量不够。我们这边一个常规迭代可能涉及几十个文件、几千行改动,遇到大版本重构,代码量直接翻几倍。安全团队的人数不可能跟着代码一起涨,人工审计永远只能覆盖最核心的部分,边缘模块基本上处于“裸奔”状态。第二是经验断层。有经验的审计员看一眼代码就知道这里有没有问题,新人却需要大量时间培养,而且培养出来的经验往往非常个人化,很难沉淀成团队的通用能力。第三是手感波动。同样一段代码,状态好的时候能看出来,赶工的时候可能就略过了,人的注意力天然不适合做长时间、大范围的重复检查。
所以我们在 OpenClaw 设计初期就定了一个原则:不跟人工抢“判断权”,只帮人工解决“看不完、记不住、容易漏”的问题。大语言模型天然擅长处理大量文本,而且它的“注意力”不会因为加班而下降,这正是审计流程里最值得被替代的那部分。
1.2 传统扫描器为什么越用越鸡肋
很多人第一反应是:不是已经有静态分析工具了吗?是,有,而且很多,但用久了你会发现问题也很明显。
传统静态分析工具的核心思路是“模式匹配”——把历史上已知的漏洞特征抽象成规则,然后拿规则去扫代码。好处是确定性强、速度快、不会瞎说,但坏处是它只能识别它见过的漏洞。我们之前遇到一个真实的例子:一段 SQL 查询经过了三次字符串拼接,中间还夹了一个数组映射函数,某老牌扫描器完全没报,因为这条路径没有命中任何一条内置规则。后来人工分析确认是高危注入点,工具在这里基本是失明的。
更让人头疼的是误报。老牌工具特别喜欢报“疑似”“可能”,扫一遍下来几百条告警,真正能用的不到三分之一。安全工程师每天花大量时间去筛误报,反而没时间去看真正有价值的部分。我们也试过通过配置文件调规则,但规则越调越紧,漏报率又上去了——这是一个很难绕开的跷跷板。
大语言模型的思路不一样。它不需要人给它写一条条精确规则,它通过大量代码训练出来的语义理解能力,能感知到“这段代码的处理逻辑像是在把用户输入直接拼到底层查询里”,哪怕这段代码的模式非常小众。这正好补上了传统工具“只认见过的模式”的短板。
1.3 大模型进场:它到底解决了什么问题
把大模型拉进代码审计,本质上不是把模型当成一个“更聪明的扫描器”,而是让它充当审计流程里的“分析员预备役”。
我理解的大模型代码审计价值有三个层次。最低的层次是帮你看:把代码切片丢给模型,问它这里有没有问题,它能给出初步判断,这一步替代的是“人工逐行读代码”的体力活。中间层次是帮你推理:不只是判断有没有问题,还能解释数据从哪里来、经过什么处理、最终流到哪里,这个能力已经接近初级审计员的分析逻辑。最高的层次是帮你决策:结合修复成本、攻击面、业务影响给出综合建议,让负责人能快速决定优先处理哪个。
OpenClaw 在设计时,目标就直接定在第二和第三层之间。我们很清楚大模型会胡说八道,所以没有让它直接出“最终结论”,而是让它产出“线索”和“推理过程”,由人来拍板。这个定位在后面整个系统设计中贯穿始终。
2. OpenClaw 整体架构:不是替代审计员,而是给审计员配了三个 AI 助手
2.1 系统定位:LLM 该放在审计流程的哪个环节
架构这件事,我们前后推翻过两版。第一版特别天真,想着把整个仓库丢给模型,让它输出一个漏洞清单,结果烧了一堆 token,输出了一堆模棱两可的话,根本无法落地上线。后来我们退了一步,认真想了 LLM 在审计流程里到底应该扮演什么角色,最后得出了一个结论:它应该当一个“有经验但不负责拍板”的助理。
这样定位之后,整个流程就顺了。审计流程被拆成四段:
- 预处理:先把代码切片、建立函数调用关系、做基础静态特征提取。
- 初筛:用传统规则和模型判断结合,把明显没问题的部分过滤掉。
- 深挖:对剩下来的可疑点,交给大模型做跨函数推理和漏洞确认。
- 复核:安全工程师只看模型深挖的结果,做最终裁决。
在这个流程里,大模型不是唯一的引擎,而是处在“初筛之后、人工之前”的关键位置。它看得最多、跑得最快、但只拥有“建议权”。这个设计从根上避免了模型幻觉直接变成生产事故的可能。
2.2 五个核心模块与数据流转
OpenClaw 的模块划分是我们迭代过程中逐步稳定下来的,一共五个核心模块,各管一段:
| 模块 | 职责 | 关键点 |
|---|---|---|
| 仓库接入层 | 拉取代码、解析仓库结构、触发增量扫描 | 支持 Git 分支增量,避免每次全量导入 |
| 代码切片器 | 把仓库拆成适合模型处理的片段,重建调用关系 | 切片粒度是后续准确率的最大变量 |
| 静态预扫描器 | 用传统规则找出可疑触发点,给切片器提供锚点 | 不追求覆盖全部,只追求不漏重点 |
| LLM 分析引擎 | 对可疑切片做跨函数推理、输出结构化结论 | 提示词、温度、切片质量共同决定效果 |
| 结果聚合与复核台 | 汇总评分、去重、生成报告、对接人工复核 | 所有结论必须可回溯到代码位置 |
数据流向是这样的:仓库接入层拿到代码后,先交给静态预扫描器做一轮粗扫,产出一批“风险锚点”。代码切片器以这些锚点为中心,回溯它的数据来源和调用链路,生成切片。然后 LLM 分析引擎逐切片分析,产出结论和置信度。最后结果聚合器把零散结论按文件、按漏洞类型归并,形成一个按威胁等级排序的审计清单。
这套设计的核心逻辑是:让传统工具负责“找”,让大模型负责“想”,让人负责“定”。传统工具擅长的事情不去重复建设,大模型不擅长的确定性判断也不强行让它做。
2.3 关键设计决策:为什么先静态预扫描、再走 LLM
其实做架构的时候我们纠结过一个问题——既然大模型这么强,为什么前面还要加一个传统静态预扫描器?这不是多此一举吗?
后来用数据回答了自己。我们拿一个 5 万行左右的中型项目做过对比测试:如果直接把切片后的代码全量喂给模型,每轮审计耗时大约需要 2 小时,token 消耗巨大,而且因为一次要处理的切片数量太多,模型在后面的切片上明显出现“注意力疲劳”,结论质量下降。加了静态预扫描之后,先通过规则把 70% 无风险代码过滤掉,实际送进模型的切片数量只有原来的三分之一左右,成本和耗时就都降下来了。
还有一个更重要的原因:静态预扫描可以提供“锚点”。大模型在没有明确目标的情况下阅读代码,很容易被无关逻辑带偏,它并不知道哪些路径真正通向敏感操作。但你先告诉它“这段代码里存在一个通向 exec / eval / SQL 查询的危险路径”,它再去针对性推演,准确率会显著上升。这和给人分配任务是一样的——直接说“你帮我看看这个项目有没有问题”,对方一定会抓瞎;但你说“帮我看下用户输入走到这个函数之前经过了哪些处理”,他就知道该往哪儿使劲了。
所以这个前置设计的核心价值不是“过滤”,而是“给模型指路”。
3. 代码切片与提示词设计:OpenClaw 识别漏洞的核心链路
3.1 别按函数切,按数据流切
代码切片是整个系统的地基。我们第一版实现特别简单——按函数切,一个函数一个片段,交给模型分析。结果发现效果很差,因为很多安全问题恰恰发生在函数之间的数据传递上。比如一个函数接收了外部输入,经过处理后传给了另一个函数,而这个函数里面存在注入点。如果你只给模型看第二个函数,它完全不知道这段数据是用户可控的,自然判断不出风险。
后来我们改成按数据流切片。具体做法分四步:
- 先用解析器对代码做 AST 分析,重建函数调用关系图。
- 静态预扫描器找出所有“危险操作点”,比如 SQL 查询、文件写入、命令执行、反序列化调用等。
- 从每个危险操作点出发,反向追踪数据的来源,看它是否经过了外部输入口。
- 把从“输入口”到“危险操作点”这条完整路径上的关键代码提取出来,打包成一个带上下文的切片。
切片里除了路径本身的代码,还会附带两个东西:一个是相关函数的签名和注释,帮助模型理解上下文;另一个是危险操作点的类型标识,比如“这里是 SQL 执行点”,方便模型聚焦。
这样切出来的切片,才是模型真正能看懂的“故事”,而不是一堆孤零零的代码行。我们后来统计过,切片方式从按函数改成按数据流之后,模型对注入类问题的识别率大概提升了 30%,这个提升非常可观。
3.2 三个角色的提示词模板与分工
OpenClaw 的 LLM 分析引擎内部并不是只有一个模型实例在跑,而是按“角色”拆成了三份提示词,让模型分别扮演三个不同职责的分析员。这样分工是为了避免一个问题:让同一个模型同时做“发现问题”和“评估问题”,它往往会为了自洽而牺牲某一方面的准确度。
第一个角色叫“基础审查员”,负责对切片做初步漏洞模式识别。它的提示词核心是要求它对照常见漏洞类型清单,逐项判断是否存在风险。
你是一名资深代码审计工程师。下面是一段从项目X中提取的代码路径片段,起点是外部输入,终点是一个敏感操作点。 请逐项检查是否存在以下风险类型: 1. SQL 注入 2. 命令注入 3. 路径遍历 4. 反序列化漏洞 5. 硬编码密钥或敏感信息 对每一项输出:存在/不存在/存疑,并给出判断依据,引用具体代码行号。 注意:如果你不能确定,请输出"存疑",不要强行下结论。第二个角色叫“跨过程追踪员”,负责分析数据在函数之间流动时有没有被绕过或错误处理。这个角色的价值在于,它能补上“基础审查员”只看切片数据流、但无法判断路径上是否有校验逻辑的缺口。
你是一名数据流分析专家。下面是代码中从输入源到敏感操作点的完整路径。 请回答以下问题: 1. 数据从输入源到操作点之间经过了哪些处理函数? 2. 这些处理函数是否对数据做了有效的校验、过滤、编码? 3. 是否存在绕过校验的路径(例如分支条件、类型混淆、二次编码)? 4. 最终到达敏感操作点时,数据是否仍然可控? 请按"路径描述 -> 校验分析 -> 结论"的结构输出。第三个角色叫“安全评审员”,它的任务是综合前两个角色的输出做复核,给出最终置信度和建议。这个角色模仿了人工审计里的“复审”环节,能纠正前两个角色的过拟合和误判。
下面是同一段代码的两份分析报告: 第一份是漏洞模式识别结果,第二份是数据流分析结果。 请综合两份报告,判断该代码路径是否构成实际可利用漏洞。 输出JSON格式,字段包括: - risk_level: high/medium/low/none - confidence: 0-100 - reason: 简明扼要的结论依据 - remediation: 建议的修复方向三个角色的提示词都要强调一点:不确定就说不确定,不要猜。这是对付模型幻觉最有效的办法之一。你越允许它“存疑”,它反而越不会瞎编一个结论出来。
3.3 威胁评分与结果排序规则
模型输出的是单个切片的判断,但审计员真正需要的是一个按风险排序的清单。结果聚合器会做这几件事:按文件去重、按漏洞类型归类、按严重程度排序。
威胁评分不只看模型的 confidence,而是四个维度加权算出来的:
| 维度 | 权重 | 说明 |
|---|---|---|
| 模型置信度 | 30% | 三个角色的一致程度,越高越可信 |
| 数据可控性 | 25% | 输入是否直接来自外部接口、用户参数 |
| 漏洞类型固有风险 | 25% | 命令执行、注入类天然高于信息泄露 |
| 可达性 | 20% | 危险代码是否真的能被执行到,还是死代码 |
得分超过 75 的进入“必审列表”,50 到 75 的进入“抽查列表”,低于 50 的自动挂起,留给人工复核时参考。这个分级很关键——它保证了安全专家的精力永远花在最有价值的地方,不会淹没在几百条低质量告警里。
4. 实操全过程:从接入仓库到输出审计报告
4.1 接入配置:两条规则搞定大部分项目
OpenClaw 接入一个新项目不算复杂,重点就是两条配置:一条告诉它仓库在哪、怎么拉代码,另一条告诉它哪些目录要重点关注、哪些可以跳过。
project: name: demo-service repository: git@example.com:group/demo-service.git branch: main scan: include: - src/** - api/** exclude: - test/** - vendor/** - generated/** model: engine: internal-llm-service temperature: 0.1 max_tokens: 4096 sensitive_operations: - sql_exec - system_command - file_write - deserializeinclude 和 exclude 这两个字段值得多说一句。我们一开始图省事,直接全仓库扫描,结果把 vendor 目录和自动生成的代码也扫了一遍,模型无辜地分析了大量根本不属于业务逻辑的代码,既浪费时间又污染结果。后来明确排除了生成代码、测试目录和第三方依赖之后,效果干净了很多。
所以接入配置这件事上,我的建议是:宁可多花十分钟把目录范围理清,也不要省这两步直接开扫。AI 审计和人工审计一样,输入质量决定了输出质量。
4.2 完整跑一轮审计:步骤、耗时与产出物
拿我们最近一次模拟项目X的审计举例,这个项目规模大约 3 万行代码,涉及 200 多个文件。完整流程大概是这样:
- 仓库接入层拉取最新 main 分支代码,耗时约 30 秒。
- 静态预扫描器跑一轮,命中 124 个风险锚点,耗时约 1 分钟。
- 代码切片器以锚点为中心生成 380 个切片,耗时约 40 秒。
- LLM 分析引擎分批处理切片,调用内部模型服务,耗时约 25 分钟。
- 结果聚合器归并去重后输出 47 条初步发现,耗时约 5 秒。
人工复核环节,我们两个安全工程师大概花了半天时间过完了这 47 条。最终确认其中 12 条是有效漏洞,包括 2 条高危、4 条中危和 6 条低危。有效率达到 25.5%——这个数据看起来不算特别高,但对比之前用传统扫描器时大约 10% 左右的确认率,已经翻了不止一倍。
更重要的是,那 2 条高危漏洞里,有一条是跨三个函数、经过两次间接调用才触发的注入问题,以前这种级别的漏洞,往往要等到渗透测试阶段才被发现,那时修复成本已经很高了。OpenClaw 在代码审计阶段就把它捞了上来,这就是整套系统的最大价值。
4.3 人工复核工作台:如何把效率再翻一倍
模型给出的结果如果不能快速复核,前面节省的时间会在最后一步全吐回去。所以我们花了挺多精力做复核工作台。
工作台的基本形态是一个网页应用,左边是模型分析结果,包括风险等级、置信度、漏洞类型、修复建议,右边是嵌入的代码视图,自动高亮模型引用的行号。工程师不需要来回切换窗口去翻代码,直接在同一个界面里就能做判断。
界面上还提供两个按钮,“确认漏洞”和“标记误报”。每次点击都会作为一条训练反馈记录下来,进入样本库,后续用于提示词的迭代和模型的微调数据准备。这里有个很实际的收益:三个月后,我们的复核工作台积累了几百条带人工标注的样本,这些样本比任何公开数据集都贴合自己团队的业务代码风格,它们后来成为改进提示词最重要的依据。
复核工作台设计上有一条核心原则:人工操作必须“一键可达”。任何需要超过两次点击才能完成的操作,都会降低评审员的使用意愿。宁可界面简单一点,也坚决不做花哨但低效的交互。
5. 常见问题与排查技巧实录(OpenClaw 落地避坑速查)
5.1 上下文窗口不够塞怎么办
做代码审计的人永远会遇到一个问题,代码路径太长,塞不进模型的上下文窗口。三五个函数的调用链还好,一旦涉及复杂业务逻辑,几十个函数连在一起,切片一下就爆了。
我们尝试过几种方案,最终稳定下来的是“分层摘要 + 关键路径保留”。具体做法是:把切片中非关键路径上的函数,先用模型生成一段简要摘要,保留输入输出签名;关键路径上的代码不做任何压缩,原样保留。这样既保证模型能看到完整的数据流主干,又不会因为旁路代码过多而超出上下文限制。
这个方案的关键在于“怎么判断哪些是关键路径”。我们的策略是:只要路径中存在数据被赋值、拼接、编码、解码变换的节点,都算关键路径;只起传递作用的函数,用摘要替代。启动成本比较低,但效果立竿见影,上下文能承载的逻辑路径长度提升了将近一倍。
5.2 输出格式乱、结论忽左忽右怎么治
模型输出不稳定是很多人放弃 AI 审计的直接原因。同一个切片,上午跑出来是高危,下午跑出来是中危,你说这让审计员怎么信?
我们踩过之后整理出三条对策。
第一,温度调到接近 0。代码审计不是创意写作,不需要模型发挥想象力,温度越低输出越稳定。现在是固定 0.1,几乎不再出现完全漂移的结论。
第二,强制结构化输出。在提示词里明确要求 JSON 格式,并且给出字段规范。解析层设计成容错模式,就算模型偶尔输出不规范也能兜底,不会直接崩掉。
第三,引入 few-shot 示例。在提示词里固定放一个“标准切片 + 标准输出”的示例。千万不要小看这个动作,一个高质量示例的效果,比你在提示词里写一百遍“请严格按照格式输出”都管用。
5.3 误报和漏报的平衡术:评分阈值与回测集
误报和漏报是一对永恒的矛盾,OpenClaw 也不可能消灭它,只能通过配置把它控制在一定范围内。
我们维护了一个小型的“内部审计回测集”,大概 50 个切片,混合了确认漏洞、确认误报和边界情况。每次调整提示词或者评分规则之后,先用回测集跑一遍,看整体识别率是升了还是降了。如果为了压低误报导致漏报率升高,我们会觉察到,并有依据地回调。
评分阈值也允许按项目类型调整。比如对新项目,我们更担心漏报,会将“必审列表”的阈值从 75 下调到 65,宁可让工程师多看几条,也不能放过可能的高危漏洞。对已经稳定运行的老项目,阈值上调到 80,减少噪音,让团队聚焦在最值得关注的变化上。
5.4 可信度与可解释性:怎么让业务方敢用 AI 结论
技术问题解决完之后,还有一个绕不过去的问题:怎么让研发负责人、业务方这些“非安全专业人士”接受 AI 给出来的结论?
我们的做法是:所有模型结论,必须能回答三个灵魂拷问——问题在哪里、为什么是这个问题、必须改哪里。OpenClaw 输出的每一条漏洞发现,都必须附带具体的代码行号、数据流转路径说明和修复建议,缺一不可。如果模型给不出完整的链路,它的结论置信度会被自动调低,甚至降到“存疑”等级。
另外一个有用的实践是“可追溯报告”。每次审计跑完,系统自动生成一份 PDF 格式的审计报告,包含问题清单、严重程度、修复建议,每一页都标注切片编号和代码快照。这个报告可以直接拿去跟业务方开会,不需要安全工程师再花半天时间整理汇报材料。这个细节看起来很琐碎,但对工具的实际采纳率影响巨大。
6. 踩过的三个大坑与后续扩展方向
6.1 坑一:一上来就让模型看全仓,效果差还烧钱
第一个版本是最典型的“技术理想主义”产物。我们天真地以为模型越强,喂得越多越好,于是直接把整个仓库的代码分批塞给模型,让它输出全仓库的漏洞报告。结果跑了一整夜,token 费用飙升,输出里充斥着一堆“可能存在风险”“建议人工确认”的车轱辘话,真正能用的发现不超过五条。
这次失败给我最深的教训是:能力越强的工具,越需要给它划定边界。你让一个资深审计员从头到尾看十万行代码,他也一定会遗漏,因为他也是人,注意力有限。模型也一样,不是输入越多输出越好。后来我们确立了“先锚点、后切片、再分析”的流程,本质上就是在给模型划定它能力范围内最高效的工作区间。
6.2 坑二:直接要漏洞清单,不如让模型回答问题
第二版我们学乖了一点,开始切片分析,但提示词设计得还是太偷懒。当时让模型“分析这段代码,列出存在的漏洞”,结果输出质量很不稳定,有的切片能找出问题,有的切片完全跑偏。
后来在一次复盘讨论里,某同事说了句点醒我的话:“你这是在让模型做判断题或者问答题,判断题它最容易糊弄你。你不如让它做填空题,一个空一个空填,填到哪一步卡住你一眼就能看出来。”我们这才意识到,更有效的提示词不是问“有没有漏洞”,而是问“数据经过了哪些处理函数,每一步做了什么”。当模型老老实实把处理链描述出来之后,有没有漏洞其实你自己都能看得出来了。
这就是把“直接要结论”改成“要推理过程”的核心逻辑。模型在推理过程中很难说谎,如果中间某个环节它答不上来,或者回答得很模糊,那大概率就是问题所在。
6.3 坑三:没有给模型“漏洞知识底座”,结论飘
第三版系统跑通之后,我们一度以为大功告成,直到某次审计中发现一个典型的 CWE-89 SQL 注入模式,模型居然给出了“低风险”的结论。我们排查提示词才发现,模型的知识虽然覆盖了常见漏洞类型,但对某些偏门漏洞的判断标准并不稳定,尤其是在面对业务定制化的代码风格时,容易误判。
后来我们做了一件事:在提示词里注入“漏洞知识底座”。我们整理了过去几年自己团队处理过的历史漏洞案例,去掉敏感信息后做成一份内部漏洞模式库,按漏洞类型、触发条件、危害等级、修复建议四个维度组织。每次模型分析切片之前,系统会把与该切片最可能相关的几条知识记录拼到提示词里,相当于给模型发了一张“考前小抄”。
加了知识底座之后,识别准确率提升非常明显,尤其是那些在通用语料里很少出现、但在我们业务场景里反复出现的漏洞类型。这个优化让我确信,AI 审计系统的效果,最后拼的其实是内部知识沉淀的质量。
6.4 后续扩展:CI/CD 集成、修复建议机器人、漏洞知识库
OpenClaw 目前已经能稳定产出审计结果,但这显然不是终点。我们现在在推进三件事,也都顺带说下方向,给大家参考。
第一是接入 CI/CD 流水线。让每次代码合并前自动触发增量扫描,只分析变更涉及的文件和调用链,把审计从“阶段性盘点”变成“日常关卡”。这里的关键是控制单次扫描耗时,我们目前的目标是“合并请求产生后 10 分钟内给出初步结论”。
第二是做修复建议机器人。基于模型产出的漏洞描述和修复建议,自动生成可执行的代码修改建议,并在 Merge Request 上直接评论,让研发在修的时候不用再翻文档。这个方向能不能做成,很大程度取决于前面积累的人工标注样本够不够多、够不够干净。
第三是持续沉淀内部漏洞知识库。我们把每一位安全工程师对这个系统的每一次“确认漏洞”和“标记误报”都视为知识积累,定期从人工标注里提炼新的漏洞模式,反哺知识底座。这是一个滚动迭代的飞轮:系统用的人越多,判断越准;判断越准,用的人就越多。
我个人在实际操作中的体会是,OpenClaw 这类 AI 审计系统,它的天花板不在模型选得多强,而在于周围这套工程体系有没有做好——切片切得够不够准、提示词有没有持续迭代、反馈闭环有没有跑起来。大语言模型只是给了我们一个更聪明的起点,真正决定它能走多远的,还是使用它的人和组织沉淀知识的能力。如果这篇文章里有一点对你有用,那我觉得核心就一句话:别让模型替你下结论,让它替你把推理过程摆到桌面上来。剩下的判断,永远要握在自己手里。