eddielebelle/ringclaw

GitHub: eddielebelle/ringclaw

一个通过任务级权限分离和凭证隔离来从架构上限制 prompt 注入爆炸半径的 LLM agent 研究原型框架。

Stars: 0 | Forks: 0

# RingClaw 一个权限分离的 LLM agent 框架,建立在一个令人不安的前提之上:**任何浏览开放互联网的模型,迟早都会被它读取的内容所劫持。** 就像 Openclaw,只是更安全了…… 研究原型。 ## 前提 浏览型 agent 的工作是读取陌生人编写的文本,而 LLM 的核心能力是遵循文本中的指令。这两者是同一套机制。没有任何模型更新、系统提示词或注入分类器可以干净地将它们分开——每一项试图*识别*攻击的防御,都是在和一个可以针对冻结目标自由迭代的攻击者对赌。学术文献也得出了相同的结论:[*AI Agents May Always Fall for Prompt Injections*](https://arxiv.org/abs/2605.17634)(Abdelnabi & Bagdasarian, 2026)。把这场对赌视为已经输了:一旦你的 agent 读取了受攻击者影响的内容,就假定攻击者正在选择它的下一步行动。 RingClaw 正是基于这一假设,并提出了仅剩的一个问题:**被劫持的模型实际能做什么?** 目标是答案是*除了它已经被授权做的事情之外什么也做不了*——攻击者继承的是在他们获得控制权之前就已选定的范围,而不是你账户的密钥。 ## 由前提推导出的结论 如果假定 worker 一旦接触到不可信内容就会被入侵,那么就会得出四条设计规则: 1. **安全性不能依赖于模型。** 模型只生成 tool-call JSON;而工具才接触外部世界。每一项结构性防御——主机白名单、凭证管控、收件人检查——都在工具边界处强制执行,因此无论模型是否诚实,它都成立。 2. **没有任何组件持有致命的三要素。** 私密数据、不可信暴露面、外部通信——数据窃取需要全部三者([Willison's framing](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/))。每项任务都被主分类器拆分为 `public` 和 `sensitive` 两个层级,它们作为没有桥梁的独立 agent 运行:读取陌生人网页的 worker 永远不会持有你的凭证,而持有凭证的 worker 永远不会读取陌生人的网页。 3. **范围在暴露前锁定。** 主控者在 worker 读取哪怕一个字节的不可信内容*之前*,就定义好每个 worker 的精简任务和主机白名单,并且用户批准该计划。被劫持的 worker 无法扩大其自身的范围,而被劫持的主控者也无法在批准后对其进行扩展。 4. **凭证绝不进入模型上下文。** 凭证库仅在工具边界处释放 secret,并 URL 限制为它们所属的主机。一个被完全攻破的 worker 无法泄露从未出现在其对话历史中的内容。 现有的浏览器代理(OpenClaw、Claude Computer Use)接受了同样的前提,但在**外部**对其进行遏制——OS 沙箱、通道访问控制、部署端白名单。这些方法很有效,RingClaw 可以与它们组合使用;它增加的维度是**在 agent 层进行内部的、针对单个任务的权限分离**,从而在结构上使能力与已批准的任务相匹配,而不是与沙箱允许的范围相匹配。 最接近的设计血统是 [CaMeL](https://github.com/google-research/camel-prompt-injection)([Debenedetti et al., *Defeating Prompt Injections by Design*](https://arxiv.org/abs/2503.18813))——通过构造来击败注入,使用特权规划器和隔离的 worker,而不是通过检测。RingClaw 将同样的直觉应用于长生命周期的个人代理:由操作者定义的信任根、凭证库、作为明确用户契约的计划批准,以及在相同机制下的周期性(cron)任务。 ## 隔离带来的是实用性,而不仅仅是安全性 对隔离防御的标准反对意见是,它们会给 agent 增加过重的负担导致其毫无用处——Abdelnabi & Bagdasarian 直接指出:“一种隔离了哪怕单一数据(例如一封关键的电子邮件)的防御,可能会使任务无法完成。”当你在任务中途隔离*数据*时,这确实成立。RingClaw 的押注在于,当你拆解*任务*时,情况就会发生逆转:以正确的粒度进行拆分,限制波及范围的举动也会让每一个部分更容易执行。 其机制是完全一样的。主控者交给每个 worker 一份简短的**精简任务**——目标、相关的控制句柄、主机范围,仅此而已。从安全性的角度来解读,这是被劫持的 worker 无法逃避的契约。从实用性的角度来解读,这是一项小巧、明确的工作,没有上千轮对话的历史记录,也没有相互冲突的目标——这正是那些小型、廉价、本地模型停止幻觉并开始完成任务的机制。拆解的质量成为了同时左右两者的唯一调节旋钮:太粗略你就会重新造出单体 agent,太细致 worker 就会在碎片上停滞不前。 实际结果是,敏感的工作流不再需要前沿模型。在 Abdelnabi & Bagdasarian 自己的配套数据集中的 100 个委派场景中,RingClaw 下的本地 gemma 级别模型在严格实用性上得分 90/100,安全违规为 0/100,收件人信任关卡拦截了全部六次外部发送尝试。测试框架已包含在此 repo 中——使用 [`examples/flow_separation/`](examples/flow_separation/README.md) 进行复现。按次调用进行模型路由正是为了这种拆分而存在的:在规划质量至关重要的地方使用前沿主控者,而在其他所有地方使用本地 worker。 ## 公开邀请:尝试攻破它 像“被劫持的 worker 无法离开其范围”这样的声明,其价值只取决于它能经受住什么考验,因此这个 repo 提供的不仅是用来欣赏它的工具,更包含了用来攻击它的工具。 [`examples/flow_separation/`](examples/flow_separation/README.md) 测试框架是一个完全植入检测设备的模拟工作环境——agent 采取的每一个动作都会被记录在审计日志中,评分器会标记任何漏网的外部发送,并且其场景格式让你很容易编写自己的对抗性变体(将你的 payload 植入到线程正文中,将该流程标记为未授权,看看有什么能蒙混过关)。 真正重要的目标是**结构性的底线**,而不是模型:威胁模型已经假定 worker 可能会被越狱,因此如果一次成功的越狱仍然停留在其范围内,说明系统正按预期工作。真正的攻破是指公开的 worker 接触到了凭证或敏感主机、范围在计划批准后被扩大,或者凭证库在不匹配的 URL 上释放了凭证。已知的遗留发现已记录在 [`SECURITY.md`](SECURITY.md) 中,免得你白白浪费一晚上的时间去重新发现它们;如果是超越该列表的任何内容,我们希望能收到反馈——对于任何可利用的内容请私下联系(**wrh9ppyzo@mozmail.com**),或者作为 issue 提交给社区。这里没有漏洞赏金(单人研究项目);我们能提供的是鸣谢,以及快速、坦诚的回复。 有关设计理念和威胁模型,请参阅 [`DESIGN.md`](DESIGN.md)。 有关各组件的参考,请参阅 [`ARCHITECTURE.md`](ARCHITECTURE.md)。 有关范围、假设和已知的遗留发现,请参阅 [`SECURITY.md`](SECURITY.md)。 ## 状态 **研究原型。** RingClaw *限制了 prompt 注入的波及范围*;它不能消除注入,不能使 agent 变得“无法黑客攻击”,也不具备生产或企业级可用性。其价值在于架构和坦诚的威胁模型——请参阅 [`SECURITY.md`](SECURITY.md) 了解哪些在范围内、哪些不在范围内,以及已记录的遗留发现。 已通过真实的 LLM(本地使用 Ollama gemma4-agent,更高质量的运行使用 Anthropic Claude)和真实的 Telegram bot 频道进行了端到端验证。2230 项测试通过。已实现的防御: - 由用户进行计划批准作为信任契约(手动 / 低于阈值的自动模式,在可配置的波及级别之上实行 2FA 门控) - 在敏感任务调度时重新检查主机信任根 - 在批准时锁定每个任务的 worker 范围;主控者之后无法扩展它 - Sanitiser(确定性的按类型解析器;模式:passthrough / parse_strict) - 轨迹监视器(独立的小型 LLM 裁决器;接受 / 拒绝 / surface_to_user) - 动作监视器(worker 循环内部的按轮次思维漂移关卡) - 针对敏感 `type()` 动作的收件人信任根,作用于 email 字段 - 计划任务,带有冻结的计划模板,默认 10 天过期,撤销时实行协作-中止终止,先前触发的 prompt 注入防御框架 ## 快速开始 需要 Python ≥ 3.12 和 [`uv`](https://docs.astral.sh/uv/)。 ``` git clone && cd ringclaw uv sync ``` 选择一个后端: * **Ollama**(本地,免费,默认)。内置配置使用 `gemma4-agent:default`,这是基于公开的 Gemma 权重的一个轻量级自定义构建版本——根据 [`models/README.md`](models/README.md) 构建它(只需一个 `ollama create`),或者通过 `--model` 指向你已有的任何支持工具调用的模型。 * **Anthropic**(`uv sync --extra anthropic`,然后设置 `ANTHROPIC_API_KEY`)。 进行交互式设置(将写入 `$XDG_CONFIG_HOME/ringclaw/config.toml`): ``` uv run ringclaw setup ``` 运行一个一次性任务: ``` uv run ringclaw run "summarise https://example.com" ``` 或者启动 Telegram bot(需要 `telegram` 扩展和一个 bot token): ``` uv sync --extra telegram export TELEGRAM_BOT_TOKEN=… # from @BotFather uv run ringclaw telegram ``` ## 桌面仪表盘 (GUI) 一个双窗格的仪表盘:左侧是聊天和计划的批准/修改/拒绝,右侧是 agent 的**实时浏览器**,带有查看与接管功能(你直接处理登录/CAPTCHA;agent 看不到你输入的内容)。还包括内存审查、MCP 添加、surfaces 和配置编辑器。 将其作为本地 Web 应用运行(仅限 localhost,token 门控): ``` uv sync --extra gui uv run ringclaw gui # prints http://127.0.0.1:8787/?token=… — open it ``` 如果想要原生的桌面应用,请构建 Tauri 外壳。它使用 PyInstaller 将 Python 后端打包为**侧载应用**,因此最终结果是完全自包含的(目标机器上不需要单独安装 RingClaw)。只需一条命令(在执行 [`desktop/README.md`](desktop/README.md) 中的一次性前置要求——Rust,`cargo install tauri-cli`,Tauri 的 Linux 库——之后): ``` ./desktop/build.sh # bundles the sidecar + builds the installer # → desktop/src-tauri/target/release/bundle/ (.deb / .AppImage / …) ``` 浏览/支付任务还需要 Playwright 的浏览器: `uv run playwright install chromium`。 ## 一图看懂架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ User (Channel: CLI / Telegram) │ └────────────────────────────┬────────────────────────────────────┘ │ task ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Master classifier │ │ • Sees raw user task EXACTLY ONCE │ │ • Produces a Plan: tier per item + distilled_task per item + │ │ per-item host allowlist (sensitive only) │ │ • Fail-safe: ambiguous tasks default to refuse rather than │ │ widening trust │ └────────────────────────────┬────────────────────────────────────┘ │ plan ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Plan-approval gate (the trust contract) │ │ • Shows the user the plan in plain language │ │ • User approves / rejects; promotes unrecognised hosts │ │ for this run only (with explicit warning) │ │ • Optional TOTP 2FA gate above a configurable blast level │ └──────────────┬────────────────────────────────┬─────────────────┘ │ approved │ approved ▼ ▼ ┌───────────────────────┐ ┌───────────────────────┐ │ Public worker │ │ Sensitive worker │ │ • Tool surface: │ │ • Tool surface: │ │ narrow / openclaw / │ │ SensitiveSurface │ │ computer_use / │ │ • Host allowlist │ │ anthropic_rl │ │ locked at dispatch │ │ • Blocklist on │ │ • URL-gated vault │ │ sensitive hosts │ │ release │ │ • Sees attacker pages │ │ • Recipient trust │ │ │ │ root for type() │ └───────────┬───────────┘ └───────────┬───────────┘ │ tool results │ tool results │ (sanitised) │ (sanitised) ▼ ▼ ┌─────────────────────────────────────────────────────────┐ │ Defense-in-depth components │ │ • Sanitiser (parse_strict: deterministic type parsers) │ │ • Trajectory monitor (post-turn judge, separate LLM) │ │ • Action monitor (per-turn thinking-drift gate) │ │ • Plan-approval re-prompt on host promotion / drift │ └─────────────────────────────────────────────────────────┘ ``` Public worker 和 sensitive worker 不共享环境、surface、后端或对话历史。主控者永远看不到 worker 的输出,除非它们通过了 sanitiser。主控者侧的第一层(tier-1)内存与 worker 是隔离的。 ## 子系统 ### 通道(用户 ↔ agent) * **CLI** —— 一次性执行,打印最终答案。 * **Telegram** —— 长轮询 bot,受白名单限制,重启后通过 tier-2 保持持久会话。 通道仅用于入站;计划任务的外部传递通过相同的通道适配器进行。 ### 工具面 worker 可以调用的一组工具。根据操作者配置逐层选择。 * **`narrow`** —— 极简的 `navigate`、`click_text`、`type`、`screenshot`、`wait`。防御性最强;能力最弱。 * **`openclaw`** —— OpenClaw 风格的坐标 / DOM 工具。能力更高,攻击面更广。 * **`computer_use`** —— Anthropic Computer Use API。 * **`anthropic_rl`** —— Anthropic 专门构建的浏览器 RL 工具。 * **`SensitiveSurface`** —— 仅用于敏感层;具有主机白名单限制,通过凭证库进行表单填充,并对 email 字段进行收件人信任检查。 ### 环境 * `NullEnv` —— 无浏览器;用于纯 LLM 任务。 * `PlaywrightEnv` —— 通过 Playwright 使用真实浏览器,按 `--browser-profile` 提供持久化配置,带有用于路由的主机映射,配置目录权限为 0700。 ### 凭证库 内存中的凭证存储,预先从 `--vault-file` 或在主机提升阶段通过 `bw-helper` 风格的 secretRef.exec 填充。URL 门控释放:sensitive worker 仅在位于绑定到这些凭证的主机上时,才能通过 `SensitiveSurface` 检索凭证。凭证绝不会以文本形式进入 agent 上下文。 ### Sanitiser 由操作者配置驱动,包含两种模式: * `passthrough`(默认)—— 工具输出原封不动地流向主控者。 * `parse_strict` —— 按字段类型的确定性解析器(decimal、带白名单的 url、bool、short_text 等)。任何无法干净解析的内容都会被丢弃。宁缺毋滥(偏向于假阴性)。 ### 轨迹与动作监视器 * **轨迹监视器** —— 轮次后的 LLM 裁决器,输出范围很窄(接受 / 拒绝 / surface_to_user)。捕捉那些通过了结构性过滤器的偏离目标的动作。 * **动作监视器** —— worker循环内部的按轮次思维漂移关卡。累积违规次数;在达到配置的限制时停止 worker。 两者默认均为 `disabled`;在操作者配置中更改模式以启用。 ## 计划任务 计划批准契约在时间上的延伸。`CronTemplate` 是相同的计划批准时刻,被冻结成一个可重新触发的工件,默认在 10 天后过期。重新触发不会再次提示;过期机制强制人类以已知的频率重新参与,从而防止范围漂移在不知不觉中累积。 在 [`src/ringclaw/cron.py`](src/ringclaw/cron.py) 中实现。完整的设计说明见 [`DESIGN.md` § Scheduled tasks](DESIGN.md)。 ### 批准周期性任务 ``` uv run ringclaw cron approve morning-brief \ --task "Summarise overnight email and reply with the highlights." \ --tier public \ --cron "0 7 * * *" --tz "Europe/London" \ --deliver-channel telegram --deliver-to "telegram:123456789" ``` 运行调度程序(长期运行): ``` uv run ringclaw cron scheduler --interval 30 ``` 检查: ``` ringclaw cron list # tabular status ringclaw cron show # full template ringclaw cron diagnose [] # audit + remediation hints ringclaw cron tick # dry-run scheduler pass ringclaw cron revoke # atomic purge ``` ### 触发时的关卡(按顺序,默认全部开启) 1. **签名验证** —— 模板主体哈希后与存储的 `approval_signature` 匹配。任何外部编辑都会被拒绝。 2. **过期检查** —— `template.expires_at` 必须是在未来时间。 3. **分类器波及上限检查** —— 在冻结的 distilled_task 上运行分类器,如果提议的最大波及范围超过 `--blast-ceiling` 则拒绝。在批准时也会运行。可通过 `--no-blast-check` 选择退出。 4. **协作中止检查(worker 运行前)** —— 重新读取注册表,以捕获在准备阶段发生的撤销操作。 5. Worker 在根据操作者配置全新构建的实例上运行冻结的任务。 6. **协作中止检查(worker 运行后)** —— 在交付前再次检查。如果在触发过程中被撤销,则结果为 `killed`:不交付,不进行 tier-2 写入。 7. 交付至 `template.deliver_channel`。 每次拒绝都会向网络发送相同的通用消息 `"Scheduled job didn't run today."`;具体原因只会出现在审计日志中。`ringclaw cron diagnose` 是面向操作者的详细视图。 ### 带有 Prompt 注入防御的先前触发上下文 每次触发都会在 worker 运行之前,将带有 `cron:` 键的 tier-2 状态读取到 `prior_turns` 列表中。前一次触发的 `final_text` 可能携带攻击者的 payload(前一次触发读取了攻击者内容并被越狱)。为了防止持久的入侵: * 先前内容进入**用户消息**,永远不进入系统提示词(信任降级)。 * 包裹在 `[BEGIN_UNTRUSTED nonce=] / [END_UNTRUSTED nonce=]` 标记中——攻击者内容无法知道每次触发的随机数,因此无法伪造关闭标记来逃逸出该框架。 * 攻击者内容中的字面标记前缀 token 在包含之前会被清除掉。 * 截断为最后 3 次触发 × 每次最多 200 个字符。 ## CLI 参考 ``` ringclaw run One-shot task from CLI ringclaw telegram Long-polling Telegram bot ringclaw setup Interactive operator-config wizard ringclaw login --profile NAME One-time headed-browser login ringclaw surface add --profile NAME Catalogue-driven headed login for a known surface (gmail, proton, amazon, etc.) ringclaw cron approve --task TEXT --cron EXPR | --at ISODT | --every SECONDS [--tier TIER] [--blast-ceiling LEVEL] [--deliver-channel CHAN] [--deliver-to TARGET] [--expires-in DAYS] [--no-blast-check] [--force] ringclaw cron list ringclaw cron show ringclaw cron revoke [--force] ringclaw cron diagnose [] ringclaw cron tick Dry-run scheduler pass ringclaw cron scheduler [--interval SECONDS] [--once | --max-iterations N] [--dry-run] [--stub-worker] [--no-blast-check] ``` 所有子命令都接受 `--config PATH` 来覆盖操作者配置的存放位置。 ## 配置 操作者配置是一个 TOML 文件,位于 `$XDG_CONFIG_HOME/ringclaw/config.toml`(`~/.config/ringclaw/`)。`ringclaw setup` 会写入一个可用的入门配置;其模式(schema)在 [`src/ringclaw/operator_config.py`](src/ringclaw/operator_config.py) 中有内联的文档说明。 关键部分: ``` [defaults] # runtime knobs the CLI inherits backend = "ollama" model = "gemma4-agent:default" think = true [telegram] # inbound channel token_env = "TELEGRAM_BOT_TOKEN" allow_from = ["123456789"] [hosts."mail.google.com"] # trust universe classifications = ["user-email"] credentials = ["gmail_login"] [friction.user-email] classification = "user-email" default_blast = "high" [tiers.public] tool_surface = "narrow" [tiers.sensitive] tool_surface = "SensitiveSurface" allowed_hosts = ["mail.google.com"] credentials_eligible = ["gmail_login"] [modes] # defense-in-depth knobs plan_approval = "manual" # manual | auto_if_blast_below auto_approve_threshold = "low" sanitiser = "passthrough" # passthrough | parse_strict trajectory_monitor = "disabled" # disabled | log | enforce action_monitor = "disabled" two_factor_threshold = "off" # off | low | medium | high [two_factor] secret_env = "RINGCLAW_TOTP_SECRET" ``` ## 存储布局 遵循 XDG 规范,且按用户隔离: ``` $XDG_CONFIG_HOME/ringclaw/ ├── config.toml # operator config ├── catalogue/*.toml # user-extended surface catalogue └── channels/*.toml # user-extended channel catalogue $XDG_DATA_HOME/ringclaw/ ├── profiles// # persistent browser profiles (0700) ├── sessions.json # surface-login manifest └── cron/ ├── jobs.toml # approved cron templates (0700/0600) └── audit.jsonl # cron lifecycle log $XDG_STATE_HOME/ringclaw/ ├── sessions/ # tier-2 per-(channel, sender) │ ├── telegram/.json │ └── cron/.json └── cron/locks/.lock # in-flight fire locks ``` ## 威胁模型 详细表格见 [`DESIGN.md` § Defenses by attack class](DESIGN.md)。 要点: * **通过页面内容对 worker 进行 Prompt 注入** —— 由每个任务的主机白名单(由环境强制执行)和 sanitiser 进行防御。被越狱的 worker 仍然无法脱离其作用域。 * **通过受攻击者影响的工具输出对主控者进行 Prompt 注入** —— 由在敏感调度时重新检查主机信任根来防御。即使主控者提出了带有攻击者提供的主机的敏感任务,该主机也无法通过信任根检查。 * **通过错误主机进行凭证窃取** —— URL 门控的凭证库释放;凭证永远不会进入 agent 上下文。 * **通过植入的链接/表单进行跨主机导航** —— 表面级别的动作后 URL 检查,拒绝超出范围的导航。 * **包含会使得主控者产生偏见的提示词的 worker 输出** —— 处于 `parse_strict` 模式下的 sanitiser 会丢弃自由格式的文本;轨迹监视器会捕捉偏离目标的行动计划。 * **计划任务的重放/篡改** —— `approval_signature` 提供完整性(对规范化后的主体进行 sha256 校验)。文件系统权限(目录 0700,文件 0600)是真实性防御。信任假设详见 cron 模块的 docstring。 **不在此范围内(Out of scope)**的威胁: * 合法主机的失陷(bank.example 本身就被攻击者控制)——与密码管理器一样;爱莫能助。 * 文件系统级别的攻击者(对 `$XDG_DATA_HOME` 有写入权限)——被视为“他们已经控制了该账户”。 * 侧信道(时间、下游效应)。 ## 开发 ``` uv sync --all-extras # install everything uv run playwright install chromium # for browser/payment tests uv run pytest -q # 2230 tests uv run pytest -q -k cron # cron subsystem only uv run ruff check src/ # lint ``` 项目布局: ``` src/ringclaw/ ├── cli.py # all ringclaw subcommands ├── orchestrator.py # RingClaw class, plan dispatch ├── master/ # classifier ├── public/ # PublicAgent, AgentLoop, envs, surfaces, LLMs ├── sensitive/ # SensitiveSurface ├── plan_approval.py # the trust-contract gate ├── sanitiser.py # parse_strict implementation ├── trajectory_monitor.py # post-turn judge ├── vault.py # credential store ├── host_trust.py # host trust root ├── recipient_trust.py # email recipient trust root ├── catalogue.py # surface catalogue (gmail, proton, …) ├── channel_catalogue.py # channel catalogue (telegram, …) ├── sessions.py # per-profile login manifest ├── cron.py # scheduled tasks: templates, registry, │ # audit, scheduler, executor ├── channels/ # CLI, Telegram channel adapters ├── tier2.py # per-session memory ├── operator_config.py # TOML schema └── configs/ # shipped default config ``` ## 指引 * [`DESIGN.md`](DESIGN.md) —— 设计理念 + 威胁模型 * [`ARCHITECTURE.md`](ARCHITECTURE.md) —— 各组件参考 * [`SECURITY.md`](SECURITY.md) —— 范围、假设、遗留发现,以及如何报告漏洞 ## 许可证 Apache License 2.0 —— 详见 [`LICENSE`](LICENSE)。版权所有 © 2026 Owain Squad Ltd.
标签:AI安全, AI风险缓解, Chat Copilot, LLM代理, 提示注入防御, 权限隔离, 沙箱架构, 源代码安全, 特征检测, 逆向工具