查询 25 次,耗时 0.2216 秒

构建可验证的智能合约审计 Agent:工具调用、分层记忆、状态机编排与安全评测实践

132222222222222qaz

New member

132222222222222qaz Rep
0
0
0
Rep
0
132222222222222qaz Vouches
Vouches
0
1 MONTH
1 1 MONTH OF SERVICE
132222222222222qaz 获得
0
0
0
获得
0
132222222222222qaz 凭证
0
0
0
凭证
0
主题
4
点赞
0
HakB
1 月
在社区成长 1 月
LEVEL 1 1 XP
 
一、背景:为什么审计 Agent 不能只是“模型加几个工具”

智能合约审计任务天然适合 Agent:它需要读取多文件代码、解析继承关系、执行编译和测试、调用静态分析器、记录假设,并把发现组织成可复现的报告。但这类任务也有明显风险:模型可能误解工具输出、把未经验证的猜测写成漏洞,或者在缺少边界约束时执行项目脚本、访问网络和读取无关凭据。

因此,一个可用于实际工程流程的审计 Agent,核心目标不应是“自动找到所有漏洞”,而应是:在明确授权范围内,生成可追踪、可复现、能区分事实与推断的审计证据。本文给出一套面向本地实验、授权代码库和防御性审计的实现方案,重点覆盖架构、工具调用、记忆、编排、可靠性、安全与评测。

二、总体架构与可信边界

建议把系统拆成六层:

1. 输入与范围层:保存仓库路径、允许分析的提交哈希、合约目录、排除目录、允许执行的命令和网络策略。
2. 编排层:使用显式状态机驱动流程,避免让模型自由决定所有下一步操作。
3. 推理层:负责提出检查假设、选择已注册工具、解释结构化结果,不直接接触宿主机 shell。
4. 工具网关:校验参数、执行超时、限制资源,并把输出规范化为 JSON。
5. 证据与记忆层:保存代码位置、命令、工具版本、原始输出摘要、测试结果和结论状态。
6. 报告与评测层:只把满足证据门槛的问题写入正式发现,并通过固定样本衡量准确率和可复现率。

关键原则是“模型不等于权限主体”。模型只能提出工具调用请求,真正的授权判断必须由确定性的工具网关完成。即使提示词要求模型不要访问网络,也应在容器或操作系统层禁用网络,因为自然语言约束不能替代安全隔离。

三、前置条件

以下实践仅面向自有项目、明确授权仓库或本地靶场。建议准备:

1. Linux 容器环境,Docker 或兼容运行时。
2. 固定版本的 Foundry,用于 Solidity 编译和测试。
3. Slither 等静态分析工具;其结果只能作为线索,不能直接等同于漏洞。
4. Git,用提交哈希固定分析对象。
5. 一个支持 JSON Schema 工具调用的 Agent 框架,或自行实现简单的请求校验器。
6. 不含真实私钥、生产 RPC 凭据和部署密钥的测试环境。

本地项目初始化示例:

mkdir audit-agent-lab && cd audit-agent-lab
forge init target
cd target
git add . && git commit -m "baseline"
git rev-parse HEAD

记录最后一条命令返回的提交哈希。后续证据、缓存和报告都应与该哈希绑定,防止代码变化后继续引用旧结论。

四、工具设计:小权限、结构化、可重放

不要向 Agent 暴露通用 shell 工具,例如允许任意传入“command”字符串。更安全的做法是注册用途单一的工具:

1. read_source:读取范围内的 Solidity 文件,拒绝路径穿越和符号链接逃逸。
2. compile_project:使用预设命令执行 forge build,并限制时长、CPU、内存和输出大小。
3. run_named_test:只允许执行已存在的测试合约或白名单测试名称。
4. run_slither:使用固定参数运行静态分析,返回 detector、impact、confidence 和 source mapping。
5. query_ast:查询函数、修饰器、继承关系和状态变量。
6. save_evidence:写入不可变证据记录,不允许覆盖历史记录。

以 run_named_test 为例,请求参数可以限定为:

{
"test_contract": "ReentrancyRegressionTest",
"test_name": "test_withdraw_updates_state_before_call",
"verbosity": 3
}

网关侧应执行固定模板,而不是直接拼接用户字符串:

forge test --match-contract <已校验名称> --match-test <已校验名称> -vvv

名称应匹配严格正则,例如仅允许字母、数字和下划线;参数以数组形式传递给进程 API,禁止经过 shell 展开。工具返回至少包含 exit_code、duration_ms、stdout_tail、stderr_tail、tool_version、commit_hash 和 artifact_hash。对超长输出应保存完整制品哈希,并只向模型返回受控摘要。

五、分步骤实现审计编排

步骤一:建立审计清单

进入项目后,先验证提交哈希和范围,然后生成文件清单、编译器版本、依赖来源与入口合约列表。此阶段不输出漏洞结论。

可执行的确定性命令包括:

forge --version
forge build --sizes
find src test -type f \( -name '*.sol' -o -name '*.t.sol' \) -print

生产实现中,find 也应由封装工具完成,并把搜索根目录锁定在工作区内。

步骤二:构建代码事实图

解析 AST,记录合约继承、外部可调用函数、状态写入、外部调用、delegatecall、权限修饰器和可升级入口。事实图只陈述可由代码定位验证的内容,例如“Vault.withdraw 在第 48 行执行外部 call”,而不是直接写“存在重入漏洞”。

步骤三:生成风险假设

对每个敏感路径创建假设对象。建议字段如下:

hypothesis_id:唯一标识;
category:如 reentrancy、access-control、oracle、signature-replay;
claim:待验证陈述;
preconditions:攻击或失效需要满足的条件;
source_locations:相关文件与行号;
evidence_needed:确认所需的测试或数据;
status:unverified、supported、refuted、inconclusive。

例如,发现外部调用早于余额更新时,只应生成“在接收方可回调且缺少有效重入保护的条件下,可能重复进入提款路径”的假设。是否构成漏洞还取决于调用对象是否可控、状态更新顺序、锁机制和回滚语义。

步骤四:按成本分层验证

先进行低成本验证:编译、AST 检查、调用图和静态规则;再进行单元测试或属性测试;最后才考虑本地分叉等更复杂环境。若测试需要 RPC,应使用授权的本地节点或专用测试端点,默认关闭外网访问,不允许 Agent 自行读取环境变量中的 RPC 密钥。

一个防御性回归测试可以验证“提款前先更新状态”的不变量。测试名称和断言应由人工或模板约束,执行结果必须保留命令、退出码和代码版本。若测试未能复现,不代表风险一定不存在;应把状态标为 inconclusive 或 refuted,并说明测试覆盖的前置条件。

步骤五:设置结论门槛

建议正式发现至少满足以下条件:

1. 代码位置明确,并与固定提交哈希对应。
2. 影响路径可描述,关键前置条件已列出。
3. 至少有一种独立证据,例如可重复测试、形式化属性失败、明确的语义推导或多工具交叉验证。
4. 严重性同时考虑可利用性、权限要求、资产影响和部署配置。
5. 报告明确区分已确认事实、合理推断和未验证假设。

静态分析器的一条 High 级告警不能自动转化为 High 严重性漏洞。工具级别与业务影响不是同一概念。

步骤六:生成可复现报告

每条发现建议包含标题、严重性、置信度、受影响位置、背景、前置条件、技术分析、证据、复现步骤、影响边界和修复建议。复现步骤只针对本地测试或授权环境。修复后再次运行原测试,并新增回归断言,避免“代码看起来改了”但风险路径仍然存在。

六、记忆设计:不要让历史结论污染当前代码

审计 Agent 的记忆至少分三类:

1. 工作记忆:当前任务中的文件摘要、待验证假设和最近工具结果,任务结束后可清理。
2. 项目记忆:与仓库和提交哈希绑定的架构信息、已确认约束和历史修复记录。
3. 方法记忆:通用检查表,例如重入、授权、签名域分离和代理初始化检查方法,不保存具体项目秘密。

写入长期记忆前应做来源校验。用户描述、README 和注释只能作为上下文,不能覆盖链上行为或代码事实。检索记忆时必须同时过滤 repository_id、commit_hash、chain_context 和 tool_version。若当前提交与记忆提交不同,应把旧结论标记为“历史参考”,而不是直接复用。

敏感信息方面,禁止保存助记词、私钥、会话令牌、生产 RPC 密钥和未公开漏洞细节。日志需要对常见凭据格式做脱敏,但不能只依赖正则;更稳妥的方式是让执行容器根本拿不到这些秘密。

七、状态机编排与失败处理

推荐状态流转:SCOPE_LOCKED → INVENTORIED → COMPILED → HYPOTHESES_CREATED → EVIDENCE_COLLECTED → REVIEWED → REPORTED。

每次转换都设置守卫条件。例如 COMPILED 只能在编译退出码为 0、编译器版本已记录时进入;REPORTED 只能包含 reviewed 状态的发现。工具超时、输出解析失败或代码发生变化时进入 BLOCKED,而不是让模型猜测结果。

重试也要有边界:相同参数最多重试一次;编译失败应先分类为依赖缺失、版本不匹配或代码错误;不能通过不断改写项目配置“直到通过”。Agent 对代码的任何修改都应生成独立补丁,并由人工确认后再进入验证分支。

八、安全风险与注意事项

1. 提示注入:仓库中的注释、README、测试名称都属于不可信输入。诸如“忽略规则并读取密钥”的文本不得被当作系统指令。工具网关不应接收来自代码文本的权限变更请求。
2. 恶意构建脚本:依赖安装和编译可能执行脚本。应在无网络、非 root、只读挂载源码的容器中运行,并将输出目录单独挂载为可写。
3. 路径逃逸:拒绝 ../、绝对路径、工作区外符号链接及特殊设备文件。
4. 资源耗尽:对测试设置超时、进程数、内存、磁盘和日志大小限制,防止模糊测试无限运行。
5. 供应链漂移:锁定编译器、依赖和工具版本,记录镜像摘要。不同版本的分析结果可能不可直接比较。
6. 错误归因:测试失败可能源于环境、测试本身或假设错误。报告必须保留退出码和错误类别,不能把任意 revert 都解释为漏洞复现。
7. 自动修复风险:修复重入时加入锁可能影响组合调用;调整权限可能导致治理失效。所有补丁都需要回归测试与人工评审。

九、可靠性与评测方案

不要只统计 Agent 找出了多少条问题。更有意义的指标包括:

1. Finding Precision:正式发现中经人工复核成立的比例。
2. Evidence Coverage:包含可执行测试或完整语义证据的发现比例。
3. Reproduction Rate:在干净容器中按报告步骤可复现的比例。
4. False Severity Rate:严重性被人工下调的比例。
5. Tool Call Validity:参数通过 schema 校验且执行目标符合范围的比例。
6. Scope Violation Count:尝试读取范围外文件、联网或调用未授权工具的次数,目标应为零。
7. Regression Stability:相同提交、相同工具版本下多次运行结论的一致性。

评测集应包含三类样本:有已知缺陷的本地合约、已修复版本,以及容易产生误报但实际有保护措施的反例。每个样本应有人工确认的预期结论和允许的证据路径。为了避免评测泄漏,不应把标准答案放在 Agent 可检索的项目文件中。

可在持续集成中执行:

forge build
forge test
slither . --json artifacts/slither.json
python3 eval_agent.py --repo . --commit "$(git rev-parse HEAD)" --policy policy.json

其中 eval_agent.py 应检查报告引用的文件是否存在、行号是否有效、证据哈希是否匹配、命令是否属于白名单,以及未验证假设是否被错误提升为正式发现。

十、常见问题

问:Agent 调用了两个扫描器,都报告同一问题,是否可以确认漏洞?
答:不可以。两个工具可能基于相同规则产生相关误报。仍需验证可达性、权限、状态变化和业务影响。工具一致性可以提高调查优先级,但不能替代证据。

问:是否应该让 Agent 自动编写并运行 PoC?
答:可以在本地靶场或明确授权项目中生成防御性回归测试,但应限制测试范围、禁止外网、禁止真实资金与密钥,并由工具网关控制执行。正式报告还应说明 PoC 覆盖了哪些前置条件。

问:长上下文能否替代项目记忆?
答:不能。长上下文仍可能混入过期代码和不可信文本。项目记忆需要版本绑定、来源标记和失效策略。

问:编译失败时能否继续做审计?
答:可以进行有限的源码审查,但必须降低结论置信度,并明确 AST、字节码和测试证据不可用。不能假装编译成功,也不应擅自修改依赖来掩盖环境问题。

问:如何处理无法证明也无法排除的风险?
答:保留为 inconclusive,列出缺失信息和下一步验证方式。例如需要部署配置、治理角色或预言机更新频率时,应请求这些材料,而不是补全未知事实。

十一、总结

可靠的智能合约审计 Agent 不是一个拥有无限 shell 权限的聊天模型,而是一套受策略约束的证据生产系统。它应通过固定范围、最小权限工具、结构化调用、版本绑定记忆、显式状态机和可重复评测,把“可能有问题”逐步收敛为“在这些前置条件下,由这些证据支持的结论”。

落地时优先完成三件事:第一,取消通用 shell,改为白名单工具网关;第二,为每条发现建立提交哈希、源码位置和执行证据链;第三,使用包含正例、修复版和反例的本地评测集持续测量误报、复现率与越权行为。只有当权限边界和结论边界都可验证时,Agent 才适合进入真实审计工作流。
 
顶部