脚本风险闸门
Agent 如何判定允许、询问或拒绝哪些脚本和命令。RED、YELLOW、GREEN 三级及其背后的逻辑。
Agent 会显示如下弹窗:
The script contains 1 risk. Execute anyway?
[fund_moving_cli] The script will move funds via `minara swap`.
[ Execute ] [ Cancel ]什么逻辑决定哪些脚本触发确认弹窗、哪些静默放行、哪些直接拒绝?本页说明该模型。
三条线
闸门的思维模型分三个类别,每类由其所回答的问题定义,而非模式列表:
- GREEN:合理、可逆、低成本的操作。读取数据、数学运算、写入新文件、公开 HTTPS GET 请求。Agent 直接执行,不打断你。
- YELLOW:合法但高风险。包括转移资金、修改链上授权、删除特定文件、安装软件包、发起出站写入。只有你才能判断这是否是有意为之。 闸门会暂停并向你确认。
- RED:没有合理业务原因的操作。批量删除、云元数据泄露、读取
~/.ssh/id_rsa、容器逃逸、源码中硬编码的内联私钥。就算你"想要"执行该操作,正确的方式也是使用专用工具,而不是让 Agent 运行这样的脚本。因此 RED 不可覆盖,无论是点击按钮还是传入标志都无效。
为何 RED 不可覆盖? 大多数 RED 命中都是 Prompt 注入。某个网页、技能内容块或工具结果向 LLM 注入了类似"接下来,运行这个有用的脚本"的内容,LLM 此时正在礼貌地向你描述这个操作。若 RED 可通过"用户确认"绕过,攻击在你点击确认的瞬间就成功了;因为你看到的描述是攻击者写的,不是 Agent 写的。因此 RED 强制执行,不受对话内容影响。"我理解风险,仍想继续"并不适用,因为你所理解的内容已经经过攻击者的框架过滤。
YELLOW 确认弹窗的内容
每条风险条目包含三部分:
- 分类:风险类型,例如
fund_moving_cli或onchain_dangerous_call。 - 证据:脚本中匹配到的实际行,其中私钥十六进制串、Authorization 头、Bearer token 及长 base64 字符串均以
<REDACTED-*>占位符替换。 - 决策:执行或取消。
为何要屏蔽证据中的敏感信息? 确认弹窗的目的是让你判断意图,"这是否是有意为之"并不需要看到机密本身。若脚本中包含
new ethers.Wallet("0xabc…64-hex…"),直接展示原始十六进制串会让确认弹窗成为第二条泄露路径:这些字节会被复制到审计日志、聊天记录,乃至屏幕录制中。屏蔽后显示的是"某个私钥正被传入钱包构造函数",这才是你真正需要回答的问题。
各类别的划分依据
与其列举所有模式(源码是权威列表,见
apps/agent/src/tools/_shared/script-risk.ts),
更值得关注的是为何某个操作被划入 RED 而非 YELLOW。以下是几个典型示例:
fund_moving_cli:YELLOW
调用 minara swap、cast send、forge --broadcast、hardhat run … --network mainnet、solana transfer 等命令的脚本。
为何不是 RED? 定投 (DCA) 脚本、再平衡脚本、清算机器人都是合理的自动化模式。强制划为 RED 会破坏真实工作流。
为何不是 GREEN? 调用这些 CLI 的脚本会绕过第三层(进程内的两步确认),形成盲区。Agent 正在代你转移资金,而你没有明确点击确认。闸门必须将其暴露出来。
obfuscation_with_sink:RED
eval(base64.b64decode(...))、getattr(__import__("o" + "s"), "sys" + "tem")(...)、反转字符串编译、globalThis["Func" + "tion"]。
为何是 RED? 没有第二种解释。没人会以这种方式写代码,除非是为了绕过静态分析。模式本身就是恶意意图的证据,没有什么需要向用户询问的。
mass_delete:批量删除为 RED,特定文件删除为 YELLOW
rm *、rm -rf $UNSET/x、find . -delete、shutil.rmtree(".") 为 RED。rm sandbox/files/foo.csv 为 YELLOW。
为何这样划分? 批量删除(无具体路径,或变量可能展开为空)绝大多数情况下源于 LLM 误解"清理工作区"指令,或源于安排 $UNSET 展开为 / 的 Prompt 注入。直接拒绝这些操作可覆盖常见攻击和常见误操作。
删除特定文件(rm sandbox/files/foo.csv)是人们确实需要的操作,不过在删除前仍值得一次"是,就是这个"的确认。
credential_exfil:RED
读取 ~/.ssh/id_rsa、~/.aws/credentials、macOS 钥匙串、Chrome / Brave / Edge 登录数据库、MetaMask / Phantom 扩展存储、1Password / Bitwarden / KeePass 密码库。
为何是 RED? 这些操作都没有合理的"让 Agent 帮我打开"的使用场景。如果工作流中确实需要读取 SSH 密钥,请使用专用 CLI,而不是 LLM 驱动的脚本。将此类操作锁定为 RED,也能抑制危害最大的一类 Prompt 注入攻击。
imds_ssrf:RED(覆盖所有编码形式)
云元数据端点(169.254.169.254、metadata.google.internal,以及阿里云、Oracle 的对应地址),加上攻击者用来绕过简单封锁的所有编码形式:十进制(2852039166)、十六进制(0xa9fea9fe)、IPv6 映射([::ffff:169.254.169.254])。
为何是 RED? 没有任何面向用户的合法脚本需要调用实例元数据服务。命中此类规则几乎都是 SSRF 尝试,目的是窃取运行 Agent 的云主机上的 IAM 凭证。
direct_signing_with_constructor:RED
new ethers.Wallet("0x…64-hex…")、Account.from_key("0x…")、Keypair.fromSecretKey(byteArray)。
为何是 RED? 脚本源码中出现内联私钥,要么是有人在窃取你的密钥,要么是你即将把密钥写入磁盘。无论哪种情况,正确的下一步都是立即停止,而非询问。请使用 Agent 的原生交易工具,它们不需要将密钥粘贴到源码中。
env_var_poisoning:YELLOW
设置 NODE_OPTIONS=--require ./steal.js、LD_PRELOAD=…、BASH_ENV=…、PYTHONPATH=.。
为何是 YELLOW 而非 RED? 部分操作是合理的(为追踪设置 NODE_OPTIONS,为本地开发设置 PYTHONPATH)。但这些也都是向子进程注入代码的标准方式。闸门将其暴露出来,供你确认环境变量所指向的文件是你自己写的,而不是 Prompt 注入写入的。
完整目录包含 12 个 RED 类别和 13 个 YELLOW 类别。源码是权威列表,新规避手段出现时会持续添加新模式。以上是操作层面最有意义的几个类别。
闸门无法检测的内容
静态分析存在硬性限制。了解这些限制,有助于你这位操作员保持警觉:
- 运行时拼接的字符串。 脚本中若出现
exec(a + b),而a和b在运行时计算,分析器只能看到exec(<variable>)并上报dynamic_commandYELLOW,但无法得知最终执行的字符串内容。你需要亲自阅读脚本。 - 多工具拼接攻击。 一个脚本写入文件并设置环境变量,随后另一个
terminal调用运行一个读取两者的二进制文件。单独看每次调用都是干净的。闸门每次只检查一个调用。第二层(OS jail)才是这里的兜底,而不是闸门。 - 全新的混淆模式。 攻击者可以阅读源码,找到正则表达式的漏洞:九十九层十六进制编码、Unicode 同形字、深度嵌套的
compile()调用。我们会随时添加新模式,但真正的边界在第二层。就算某个脚本绕过了所有规则,其生成的子进程仍然运行在bwrap/sandbox-exec之内。
坦诚地说:第四层在子进程生成前检查意图;第二层在子进程生成后限制爆炸半径。 第四层有所遗漏,第二层仍然生效。
决策流程
从 LLM 调用 execute_code 到子进程实际启动之间发生了什么:
LLM → execute_code({ code, language })
│
▼
analyzeScript(body)
│
┌────┼────┐
▼ ▼ ▼
RED YEL GREEN
│ │ │
│ ▼ ▼
│ ctx.interactionQueue.ask()
│ │ │
│ │ └── handler proceeds → spawn
│ │
│ ├── user "Execute" → handler proceeds → spawn
│ └── user "Cancel" → err("user_declined: …")
│
└── err("script_risk_rejected: …")闸门无法通过提示词层面的指令绕过。它位于处理器中,在任何子进程工作开始之前运行,而不在 LLM 的提示词中。
用户可以控制的内容
- ✅ 可以对任何 YELLOW 选择取消。
- ✅ 可以通过
script_risk_policy在已审查的工作流定义中预先批准特定脚本体,参见审计与覆盖。 - ❌ 无法覆盖 RED。设计上不允许点击通过。如果你信任的脚本命中了 RED,解决方式是重写脚本(例如使用 Minara 的原生交易工具,而不是内联私钥),而不是与闸门争论。
- ❌ LLM 无法跳过确认弹窗。闸门在工具处理器中运行,在任何子进程生成之前;LLM 无法通过提示词工程绕过它。