Files
skill-lib/docs/L1-系统架构与安全模型层.md
2026-03-06 03:36:12 +08:00

997 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# L1 — 系统架构与安全模型层
**文档版本**: 1.0
**适用项目**: sgClaw (业数融合一平台 AI Agent 底座)
**编制日期**: 2026-03-03
---
## 1. 全局架构拓扑
### 1.1 完整架构图
以下是 sgClaw 系统的完整进程与组件拓扑。图中标注了新增组件(New)与已有组件(Existing)
以便评估改造范围。
```
superrpa-chromium.sh (wrapper, 启动入口)
├─ watchdog (background) ─────────────────────────── [Existing, 不变]
│ └─ node local_service [Existing, 不变]
│ ├─ 本地 HTTP API (端口 9527)
│ └─ 文件上传 / 下载辅助
└─ chrome (main process, foreground) ─────────────── [Existing, 不变]
├─ SgClawProcessHost ────────────────────────── [New, Singleton in Browser Process]
│ │
│ ├── STDIO Pipe (stdin/stdout) ──────────→ sgclaw (Rust child process)
│ │ │
│ │ ├─ Agent Runtime ──────── ZeroClaw ReAct Loop
│ │ │ └─ think → act → observe → repeat
│ │ │
│ │ ├─ LLM Provider ──────── Claude / GPT / 本地模型
│ │ │ ├─ streaming 支持
│ │ │ └─ token 用量统计
│ │ │
│ │ ├─ BrowserPipeTool ──── 自定义 Tool trait 实现
│ │ │ └─ 将 Agent action 序列化为 pipe command
│ │ │
│ │ ├─ SkillLoader ──────── 加载 JS 业务技能脚本
│ │ │ ├─ 技能签名校验
│ │ │ └─ 沙箱执行环境
│ │ │
│ │ ├─ MAC Policy ──────── 域 / Action 白名单
│ │ │ └─ rules.json 配合浏览器侧双重校验
│ │ │
│ │ ├─ Critic ─────────── 输出质量评估
│ │ │ └─ Circuit Breaker (熔断器)
│ │ │
│ │ ├─ Memory ─────────── SQLite + Vector 向量存储
│ │ │ ├─ 短期对话记忆
│ │ │ └─ 长期任务知识库
│ │ │
│ │ └─ MCP Client ─────── rmcp (Rust MCP SDK)
│ │ └─ 连接外部 MCP Server 获取工具
│ │
│ ├─ PipeListener (async read loop)
│ │ └─ 解析 JSON Line分发到 MAC 校验
│ │
│ ├─ MAC Whitelist Check ──────────────────── [New, ~100 lines C++]
│ │ ├─ 校验 action 是否在 pipe 允许列表
│ │ └─ 校验 expected_domain 是否匹配当前页
│ │
│ └─→ CommandRouter ──────────────────────── [Existing, 40+ actions, 不变]
│ ├─ CdpCommandExecutor → 页面操作
│ ├─ ZombiePageManager → 后台会话池
│ └─ SessionManager → 会话状态机
├─ CdpBridgeManager ────────────────────────── [Existing, 页内 JS SDK, 不变]
│ └─ 页面 JS → CDP binding → CommandRouter
├─ ZombiePageManager ───────────────────────── [Existing, 后台会话池, 不变]
│ └─ 最多 5 个 zombie page自动回收
├─ SessionManager ──────────────────────────── [Existing, 会话状态机, 不变]
│ └─ login → active → expired → re-login
└─ RpaGlobalStorage ────────────────────────── [Existing, 跨页存储, 不变]
└─ key-value 存储,持久化到磁盘
```
### 1.2 简化四组件视图
从系统集成的角度sgClaw 的架构可简化为四个核心组件的交互:
```
┌─────────────────────┐ STDIO Pipe ┌─────────────────────┐
│ │ ◄──────────────────────────► │ │
│ SuperRPA Browser │ JSON Line Protocol │ sgClaw (Rust) │
│ (C++ Chromium) │ 单连接、进程私有 │ 基于 ZeroClaw │
│ │ │ │
│ • CommandRouter │ │ • Agent Runtime │
│ • CdpBridgeManager│ │ • BrowserPipeTool │
│ • ZombiePageMgr │ │ • SkillLoader │
│ • SessionManager │ │ • Memory │
│ • SgClawProcessHost│ │ • MAC Policy │
│ (New) │ │ • Critic │
└─────────┬───────────┘ └──────┬──┬──────────┘
│ │ │
│ 页面渲染 API 调用 │ │ 加载技能
│ 用户交互 │ │
▼ ▼ ▼
┌─────────────────────┐ ┌────────────────────────────┐
│ │ │ │
│ 用户 (前台浏览器) │ │ LLM Cloud / Local │
│ │ │ ├─ Claude API │
│ • Side Panel UI │ │ ├─ GPT API │
│ • 启动/停止按钮 │ │ └─ 本地模型 (Ollama 等) │
│ • 任务输入框 │ │ │
│ • 执行日志 │ ├────────────────────────────┤
│ │ │ │
└─────────────────────┘ │ Skill Repository │
│ ├─ 内置技能 (OA 审批等) │
│ ├─ 用户自定义技能 │
│ └─ 签名校验 + 版本管理 │
│ │
└────────────────────────────┘
```
---
## 2. 技术选型决策记录
### 2.1 基底框架选型: ZeroClaw
在确定 sgClaw 的 Agent Runtime 底座时,团队对 Rust 生态中主流 Agent 框架进行了系统评估。
评估维度包括:社区活跃度、内存占用、可嵌入性(是否支持替换浏览器控制层)、以及与我们
STDIO Pipe 架构的兼容性。
| Framework | Language | Stars | Runtime Memory | Embeddability | Browser Layer Replaceable | 备注 |
|------------|----------|-------|----------------|----------------------|---------------------------|---------------------------|
| **ZeroClaw** | Rust | 17K | ~5 MB | High (trait-driven) | **Best** | 活跃社区trait 抽象完善 |
| Rig | Rust | 6.2K | Minimal | Best (cargo add) | Full custom | 轻量但 Agent loop 需自建 |
| Moltis | Rust | - | ~40 MB | Medium | Via MCP only | 内存偏高,嵌入需改造 |
| OpenFang | Rust | New | ~40 MB | Low (API only) | Difficult | API Server 架构,不适合嵌入 |
| MicroClaw | Rust | 152 | - | Low (fork needed) | Via MCP only | 社区不活跃,需 fork 维护 |
**决策ZeroClaw**
核心理由:
1. **trait-driven 架构**ZeroClaw 将 Tool、Provider、Memory、Security 全部定义为 trait
允许我们用自定义的 `BrowserPipeTool` 替换默认的浏览器控制层(如 Playwright/Puppeteer
同时复用其 Agent Runtime、Provider 抽象、Memory 系统和安全模块。
2. **内存优势**~5 MB 的运行时内存占用,在 8 GB 总内存预算内几乎可以忽略不计。
相比 Moltis/OpenFang 的 ~40 MB差异显著。
3. **ReAct Loop 成熟**ZeroClaw 内置 think → act → observe 循环,支持 streaming、
multi-turn、tool-use无需从零构建。
4. **MCP 生态兼容**:内置 rmcp client可在需要时连接外部 MCP Server 扩展工具集。
### 2.2 通信协议选型: STDIO Pipe
sgClaw (Rust) 与 SuperRPA Browser (C++) 之间的 IPC 通道是整个架构的关键路径。
团队评估了四种 IPC 机制:
| 方式 | Linux 支持 | Windows 支持 | 安全性 | 多连接支持 | 延迟 |
|------------------|-------------|---------------------|---------------------------------|-----------|----------|
| **STDIO Pipe** | fd inherit | HANDLE inherit | **最高** (进程私有,无法外部连接) | 否 | ~0.1 ms |
| Unix Domain Socket| /tmp/sock | 不支持 | 高 (文件权限控制) | 是 | ~0.2 ms |
| Named Pipe | 不支持 | \\\\.\pipe\ | 高 (DACL) | 是 | ~0.2 ms |
| TCP localhost | 支持 | 支持 | **低** (任意进程可连) | 是 | ~0.5 ms |
**决策STDIO Pipe**
核心理由:
1. **安全性最高**STDIO Pipe 是进程私有的文件描述符Linux或句柄Windows
只有父子进程间可以访问。本机其他进程无法连接、监听或注入命令。
这从物理层面杜绝了 Pipe Hijack 攻击。
2. **跨平台统一**Chromium 的 `base::LaunchProcess` 已经抽象了跨平台的管道创建,
Linux 使用 `pipe()` + `fork()`/`exec()`Windows 使用 `CreatePipe()` + `CreateProcess()`
我们不需要编写任何平台特定代码。
3. **单连接足够**sgClaw 是 Browser 的唯一 Rust 子进程,一条 STDIO Pipe 即可满足
全部双向通信需求。不需要多客户端并发连接的能力。
4. **延迟最低**:内核态 pipe bufferLinux 默认 64 KB的读写延迟约 0.1 ms
远低于 socket 方案。
### 2.3 Why Rust
为什么 sgClaw 选择 Rust 而非 C++/Python/Node.js
1. **内存安全无 GC**:在 8 GB 总内存限制下GC 语言的内存开销和暂停不可接受。
Rust 的零成本抽象确保 ~5 MB 运行时内存,无 GC 停顿。
2. **跨平台编译**:目标平台包括 Linux 银河麒麟 V10 SP1 (x86_64) 和 Windows 10/11。
Rust 的 cross-compilation 工具链 (`cross`, `cargo-zigbuild`) 可直接生成两个平台的二进制。
3. **极小二进制体积**release 构建约 8.8 MB含 LLM provider、memory、MCP client 全部功能),
Windows 约 9 MB。无需额外运行时依赖。
4. **冷启动极快**< 10 ms 内完成进程初始化和 pipe handshake用户点击"启动"后几乎无感。
5. **ZeroClaw 生态**ZeroClaw 本身及其核心依赖rmcp、tokenizers 等)均为 Rust 实现,
选择 Rust 可直接复用,无需 FFI 桥接。
---
## 3. 进程模型与生命周期
### 3.1 进程层次结构
sgClaw 嵌入 SuperRPA 浏览器的既有进程树中,作为 Chrome 主进程的子进程存在。
```
superrpa-chromium.sh (wrapper, PID 1001)
├─ watchdog (PID 1002, background, 守护进程)
│ └─ node local_service (PID 1003, HTTP API)
│ ├─ 监听 127.0.0.1:9527
│ ├─ 文件上传/下载
│ └─ 与浏览器通过 HTTP 通信
└─ chrome (PID 1004, foreground, 用户交互主进程)
│ 当用户在 Side Panel 点击 [启动] 按钮时:
│ SgClawProcessHost::Start() 被调用
└─ sgclaw (PID 1005, child of chrome)
├─ stdin ← chrome stdout (接收命令响应/事件)
├─ stdout → chrome stdin (发送命令请求)
├─ Agent Runtime (ReAct loop)
├─ LLM Provider (网络出口)
└─ Memory (SQLite 文件 I/O)
```
关键特征:
- sgclaw 是 chrome 的**直接子进程**,继承 chrome 的 UID/GID 权限
- STDIO Pipe 的文件描述符在 `fork()`/`exec()` 时自动继承,无需额外传递
- 当 chrome 进程退出时sgclaw 会收到 SIGPIPE/EOF自行退出
- watchdog 和 local_service 与 sgclaw **无直接通信**,完全独立
### 3.2 生命周期状态机
```
┌──────────────────────────────────┐
│ Side Panel UI: [启动] 按钮 │
└──────────────┬───────────────────┘
│ 用户点击
┌──────────────────────────────────┐
│ SgClawProcessHost::Start() │
│ │
│ 1. Check: sgclaw 二进制存在? │
│ → 不存在: 报错,终止 │
│ │
│ 2. Check: 是否已有实例运行? │
│ → 已运行: 直接返回 │
│ │
│ 3. 创建 pipe pair (读端+写端) │
│ │
│ 4. base::LaunchProcess( │
│ "sgclaw", pipe_opts) │
│ │
│ 5. 启动 PipeReader async loop │
│ │
│ 6. 发送 handshake: │
│ {"type":"init","version":"1.0"}│
│ │
│ 7. 等待 sgClaw ack (超时 5s) │
│ → 超时: Kill + 报错 │
│ │
│ 8. Notify UI: Running │
└──────────────┬───────────────────┘
┌──────────────┴───────────────────┐
│ Running (正常运行) │
│ │
│ • PipeReader 持续监听响应 │
│ • Agent Runtime 接受用户任务 │
│ • 命令经 pipe 发往 Browser │
└──────┬──────────┬──────────┬─────┘
│ │ │
用户点击 浏览器 sgClaw
[停止] 正常退出 异常崩溃
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌──────────────────┐
│ Stop() │ │Shutdown()│ │ OnProcessCrash() │
│ │ │ │ │ │
│ 发送 │ │ 发送 │ │ 1. 记录崩溃日志 │
│ shutdown │ │ SIGTERM │ │ 2. 收集 stderr │
│ 命令 │ │ + 等待 │ │ 3. 通知 UI 异常 │
│ 等待退出 │ │ 2s 超时 │ │ 4. 关闭 pipe │
│ Kill │ │ SIGKILL │ │ │
└────┬─────┘ └────┬────┘ │ *** 不自动重启 ***│
│ │ └────────┬─────────┘
▼ ▼ ▼
┌──────────────────────────────────┐
│ Stopped (已停止) │
│ │
│ • Pipe 已关闭 │
│ • 子进程已退出 │
│ • UI 显示 [启动] 按钮 │
│ • 用户可再次点击启动 │
└──────────────────────────────────┘
```
**关键决策:崩溃不自动重启**
当 sgclaw 进程崩溃时(如 panic、OOM、非零退出码系统**不会**自动重启。
用户必须显式点击 [启动] 按钮才能再次启动。理由:
- 防止恶意 prompt injection 导致的无限崩溃重启循环
- 防止异常状态下的资源耗尽(内存泄漏累积、日志磁盘写满)
- 给用户明确的故障感知,而非静默恢复后行为异常
### 3.3 内存预算 (8 GB 约束)
目标部署环境为 8 GB 内存的银河麒麟工作站。以下是各组件的内存分配:
```
┌───────────────────────────────────┬────────────┬──────────┐
│ 组件 │ 内存占用 │ 备注 │
├───────────────────────────────────┼────────────┼──────────┤
│ OS + Desktop (银河麒麟 V10) │ ~2.0 GB │ 固定开销 │
│ Browser Process (主进程) │ ~0.2 GB │ 已有 │
│ Foreground Tabs (1-3 tabs) │ ~0.3-0.9 GB│ 用户页面 │
│ Side Panel (Agent UI) │ ~0.05 GB │ Vue SPA │
│ Zombie Page Pool (max 5) │ ~0.15-0.25 GB│ 后台会话 │
│ local_service (Node.js) │ ~0.1 GB │ 已有 │
│ sgClaw (Rust binary) │ ~0.005 GB │ 5 MB │
│ LLM context cache (对话历史) │ ~0.05 GB │ 内存缓存 │
│ SQLite + vector memory │ ~0.02 GB │ 磁盘为主 │
├───────────────────────────────────┼────────────┼──────────┤
│ **Total** │ **~3.0-3.6 GB** │ │
│ **Headroom** │ **~4.4-5.0 GB** │ >50% │
└───────────────────────────────────┴────────────┴──────────┘
```
sgClaw 的 ~5 MB 内存占用仅为总预算的 0.06%,对系统几乎零负担。
即使在极端场景下3 个前台标签 + 5 个 zombie page + LLM 长对话),
总内存使用也不超过 4 GB保留超过 50% 的余量供操作系统和突发需求使用。
---
## 4. 安全架构
### 4.1 三层纵深防御模型
sgClaw 的安全架构采用纵深防御Defense in Depth策略从内到外设置三道独立的安全层。
任何单一层被突破时,上层仍能有效阻止攻击。
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ Layer 3: 浏览器内核层 (C++ Browser Process) │
│ 最后一道防线 —— 不信任外部一切输入 │
│ │
│ ┌─ MAC 强制访问控制 ───────────────────────────────────────────┐ │
│ │ │ │
│ │ • 域白名单: 仅 rules.json 中配置的域允许特权操作 │ │
│ │ → 未知域的 click/type 等操作直接拒绝 │ │
│ │ │ │
│ │ • Action 白名单: pipe 来源命令受限于安全子集 │ │
│ │ → eval、executeJsInPage 等危险操作禁止通过 pipe 调用 │ │
│ │ │ │
│ │ • 凭证隔离: credential_store 数据永不通过 pipe 暴露 │ │
│ │ → 登录操作由 SessionManager 内部完成 │ │
│ │ │ │
│ │ • 速率限制: 单域每秒最多 N 次特权操作 (可配置,默认 10) │ │
│ │ → 超限后暂停该域操作 30 秒 │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Layer 2: Rust 中枢层 (sgClaw Agent) │
│ 核心原则 —— 不信任 LLM 输出 │
│ │
│ ┌─ 命令校验沙箱 ──────────────────────────────────────────────┐ │
│ │ │ │
│ │ • JSON Schema 严格校验: LLM 输出必须符合预定义的命令格式 │ │
│ │ → 非法字段、类型错误、缺失必要参数均被拒绝 │ │
│ │ │ │
│ │ • 禁止危险命令: LLM 不可生成 eval / executeJsInPage 类命令 │ │
│ │ → BrowserPipeTool 的 action 枚举中不包含这些操作 │ │
│ │ │ │
│ │ • Human-in-the-loop: 敏感操作弹窗确认 │ │
│ │ → sessionLogin / sessionLogout / 大批量操作 │ │
│ │ │ │
│ │ • 序列号 + HMAC: 每条 pipe 消息携带递增 sequence_id │ │
│ │ → 防止消息重放和篡改 │ │
│ │ │ │
│ │ • 熔断器 (Circuit Breaker): 连续失败 N 次自动停止 Agent │ │
│ │ → 默认阈值 10 次,指数退避冷却 │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: 管道传输层 (STDIO Pipe) │
│ 物理隔离 —— 传输通道本身不可被第三方访问 │
│ │
│ ┌─ 安全通道 ──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • STDIO Pipe: 进程私有 fd/HANDLE无法被外部进程连接 │ │
│ │ → 不经过文件系统、不绑定端口、不暴露地址 │ │
│ │ │ │
│ │ • Handshake 校验: 启动时双向版本握手 │ │
│ │ → 版本不匹配则立即断开 │ │
│ │ │ │
│ │ • 递增 sequence_id: 所有消息附带全局递增序列号 │ │
│ │ → 检测到乱序或重复即报警并断开 │ │
│ │ │ │
│ │ • 格式约束: JSON Line 格式,单消息最大 1 MB │ │
│ │ → 超大消息直接丢弃,防止内存 DoS │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.2 各层防御详解
**Layer 1 — 管道传输层**
本层的设计目标是确保 sgClaw 与 Browser 之间的通信通道从物理层面不可被第三方窃听或注入。
防御对象:
- 本机恶意进程尝试连接或嗅探 IPC 通道
- 中间人攻击(消息篡改、注入伪造命令)
- 消息重放(录制合法命令序列后重新发送)
实现机制:
- STDIO Pipe 使用操作系统内核的匿名管道,文件描述符仅在父子进程间继承,
`/proc/[pid]/fd/` 对其他用户不可见(取决于 procfs 挂载选项,银河麒麟默认安全)
- 启动时的 handshake 消息包含协议版本号,防止二进制版本不匹配导致的解析错误
- 每条消息的 `sequence_id` 全局递增,接收方校验连续性,检测到跳号或重复立即断开 pipe
**Layer 2 — Rust 中枢层**
本层的核心假设是 **LLM 的输出不可信**。无论是 prompt injection 还是 hallucination
LLM 可能生成任何格式、任何内容的输出。sgClaw 必须在将命令发往 Browser 之前进行严格校验。
防御对象:
- Prompt injection恶意网页内容被 LLM 读取后,诱导其生成危险操作
- HallucinationLLM 生成不存在的 action 或格式错误的参数
- 无限循环LLM 陷入 retry 死循环,持续消耗资源
实现机制:
- `BrowserPipeTool` 定义了一个封闭的 action 枚举enumLLM 只能选择预定义的安全操作
- 每个 action 都有对应的 JSON Schema参数类型、范围、必填项均有严格约束
- Critic 模块在 Agent 每步 observe 后评估输出质量,异常时触发终止
- Circuit Breaker 在连续 10 次操作失败后自动停止 Agent loop需用户手动恢复
- 敏感操作(涉及登录、批量删除等)通过 Side Panel UI 弹窗请求用户确认
**Layer 3 — 浏览器内核层**
本层是安全架构的最后防线。即使 sgClaw 的 Rust 层被完全绕过(理论上几乎不可能),
Browser 的 C++ 代码仍会独立执行 MAC 检查。
防御对象:
- 已突破 Layer 2 的命令(假设 sgClaw 被完全控制)
- 未授权域上的操作sgClaw 试图操作不在白名单中的网站)
- 凭证窃取(任何试图通过 pipe 读取存储密码的操作)
实现机制:
- `rules.json` 白名单由管理员配置,列出允许 Agent 操作的域名(如 `oa.example.com`
- 每条 pipe 命令到达 Browser 后,先校验 `expected_domain` 是否与当前页面的实际域名一致
- `credential_store`Linux Keyring / Windows Credential Manager的 API 在 pipe handler 中
没有任何暴露点——登录凭证只能由 SessionManager 在 Browser 内部使用
- 速率限制器独立于 sgClaw 的 Circuit Breaker即使后者被禁用Browser 侧的限速仍然生效
---
## 5. 威胁模型与对抗策略
### 5.1 威胁分类总表
| # | 威胁名称 | 描述 | 影响 | 防御层 | 对抗策略 |
|----|------------------------|------------------------------------------------|----------------------------|-----------------|---------------------------------------------------------|
| T1 | Pipe Hijack | 本机攻击者劫持 STDIO Pipe | 完全控制浏览器操作 | Layer 1 | STDIO 进程私有fd 不经过文件系统procfs 受限 |
| T2 | LLM Prompt Injection | 恶意页面内容注入 LLM prompt | 执行未授权操作 | Layer 2 | JSON Schema 校验Action 枚举白名单;禁止 eval |
| T3 | Skill Poisoning | 注入恶意 Skill 脚本 | 持久化后门 | Layer 2 + 3 | Skill 签名校验沙箱执行MAC 域名限制 |
| T4 | Credential Leakage | 通过 pipe 读取浏览器存储的密码 | 账户泄露 | Layer 3 | credential_store API 不暴露给 pipe凭证仅内部使用 |
| T5 | Replay Attack | 录制合法命令后重放 | 重复执行敏感操作 | Layer 2 | sequence_id 递增校验 + HMAC 签名 |
| T6 | Retry Storm | LLM 无限重试导致资源耗尽 | CPU/内存/网络耗尽 | Layer 2 | Circuit Breaker (阈值 10);指数退避;最大重试限制 |
### 5.2 各威胁详细分析
**T1: Pipe Hijack管道劫持**
攻击路径:同一台机器上的恶意进程尝试读取/写入 sgClaw 与 Browser 之间的 STDIO Pipe。
分析STDIO Pipe 基于操作系统匿名管道实现,没有文件系统路径,没有网络端口。
攻击者需要获取目标进程的 fdLinux或 HANDLEWindows这在标准安全配置下
需要 root 权限或 `ptrace` 能力。银河麒麟 V10 默认启用 `kernel.yama.ptrace_scope=1`
非父进程无法 ptrace。
残余风险root 用户可以访问任何进程的 fd。但在本部署场景中root 被视为可信实体。
**T2: LLM Prompt Injection提示注入**
攻击路径:用户访问的恶意网页中包含精心构造的文本,当 sgClaw 读取页面内容并发送给 LLM 时,
这些文本被 LLM 误解为指令,导致 Agent 执行非预期操作。
分析:这是 AI Agent 面临的最常见威胁。sgClaw 的防御不依赖于 LLM 本身的抗注入能力
(这是不可靠的),而是在 Rust 层进行硬编码限制:
- LLM 只能输出预定义的 action 枚举值,任何不在枚举中的操作被直接拒绝
- 最危险的 `eval``executeJsInPage` 不在枚举中,从根本上不可能被 LLM 触发
- `expected_domain` 字段要求 LLM 明确声明目标域名Browser 侧校验实际域名是否匹配
**T3: Skill Poisoning技能投毒**
攻击路径:攻击者将恶意 JavaScript Skill 注入到 Skill Repository 中,
当 sgClaw 加载该 Skill 时执行恶意代码。
分析Skill 文件在加载前经过以下校验链:
1. 签名校验:每个 Skill 文件必须附带由构建系统生成的数字签名
2. 沙箱执行Skill 代码在受限的 JavaScript 沙箱中运行,无法访问文件系统或网络
3. MAC 限制:即使 Skill 生成了恶意命令Browser 侧的域白名单仍会阻止未授权操作
**T4: Credential Leakage凭证泄漏**
攻击路径:通过 pipe 发送特制命令,尝试读取 Browser 中存储的登录密码。
分析:`credential_store` 使用 Linux Keyring银河麒麟或 Windows Credential Manager
存储加密凭证。其 C++ API 仅在 `SessionManager::DoLogin()` 内部调用,
没有任何 CommandRouter action 会暴露凭证数据。pipe handler 的代码路径中
完全不存在读取凭证的逻辑。
**T5: Replay Attack重放攻击**
攻击路径:攻击者(假设已突破 Layer 1录制合法的 pipe 命令序列,稍后重放。
分析:每条消息携带递增的 `sequence_id` 和基于共享密钥的 HMAC。
接收方维护已处理的最大 sequence_id拒绝任何小于或等于该值的消息。
HMAC 密钥在每次 sgclaw 启动时通过 handshake 动态协商,重启后旧命令的 HMAC 无效。
**T6: Retry Storm重试风暴**
攻击路径LLM 因 hallucination 或 prompt injection 陷入无限循环,
持续生成失败命令并重试。
分析sgClaw 内置两级防护:
1. Agent Runtime 层:单次任务最大步数限制(默认 50 步),超出自动终止
2. Circuit Breaker连续 10 次操作失败后,断路器打开,停止所有 pipe 通信,
需要用户手动恢复。冷却期间采用指数退避1s → 2s → 4s → ... → 30s max
---
## 6. 通信架构
### 6.1 Pipe 协议概览
sgClaw 与 Browser 之间使用 JSON Line 格式通过 STDIO Pipe 通信。
每条消息占一行(以 `\n` 结尾),单消息最大 1 MB。
**消息类型一览:**
```
sgClaw → Browser:
├─ command (请求执行浏览器操作)
└─ ack (确认收到事件)
Browser → sgClaw:
├─ response (命令执行结果)
├─ event (主动推送的页面事件)
└─ init_ack (handshake 响应)
```
**Request 格式sgClaw → Browser**
```json
{
"seq": 1,
"type": "command",
"action": "click",
"params": {
"selector": "#submit-btn",
"wait_after": 1000
},
"security": {
"expected_domain": "oa.example.com",
"hmac": "a3f8c2..."
}
}
```
字段说明:
- `seq`: 递增序列号,全局唯一,用于匹配 response 和防重放
- `type`: 消息类型,`command` 表示操作请求
- `action`: 操作名称,必须在允许列表中
- `params`: 操作参数,每个 action 有独立的 JSON Schema
- `security.expected_domain`: 期望的目标域名Browser 侧校验
- `security.hmac`: 消息完整性签名
**Response 格式Browser → sgClaw**
```json
{
"seq": 1,
"type": "response",
"success": true,
"data": {
"clicked": true,
"element_text": "提交"
},
"aom_snapshot": [
{"role": "button", "name": "提交", "bounds": [100, 200, 80, 30]}
],
"timing": {
"queue_ms": 2,
"exec_ms": 45
}
}
```
字段说明:
- `seq`: 与请求对应的序列号
- `success`: 操作是否成功
- `data`: 操作结果数据,格式因 action 而异
- `aom_snapshot`: 操作后的 AOMAccessibility Object Model快照
供 Agent 的 observe 阶段使用,理解页面当前状态
- `timing`: 性能计时信息
**Event 格式Browser → sgClaw主动推送**
```json
{
"type": "event",
"event": "page_navigated",
"data": {
"url": "https://oa.example.com/approval/list",
"title": "审批列表 - OA系统",
"domain": "oa.example.com"
},
"timestamp": 1709452800000
}
```
支持的事件类型:
- `page_navigated`: 页面导航完成
- `page_loaded`: 页面 DOMContentLoaded
- `dialog_appeared`: 浏览器对话框弹出alert/confirm/prompt
- `session_expired`: SessionManager 检测到会话过期
- `zombie_ready`: Zombie page 初始化完成
### 6.2 与 CommandRouter 的对接
sgClaw 的命令最终汇入 SuperRPA 已有的 CommandRouter这是一个统一的命令分发器
支持 40+ 种浏览器操作。sgClaw 作为新增的命令来源,与已有的 JS SDK 并行存在。
**sgClaw Pipe 路径(新增):**
```
sgClaw (Rust)
│ JSON Line over STDIO Pipe
SgClawProcessHost (C++, Browser Process)
PipeListener (async read, parse JSON)
├─── MAC Whitelist Check ─────────────────────────────┐
│ │ │
│ ├─ action in allowed_pipe_actions? │
│ │ → No: reject with error response │
│ │ │
│ ├─ expected_domain matches current page? │
│ │ → No: reject with domain_mismatch error │
│ │ │
│ └─ rate limit not exceeded? │
│ → Exceeded: reject with rate_limit error │
│ │
▼ │
CommandRouter (Existing, 40+ actions) ◄──────────────────┘
├───────────┬──────────────┬──────────────┐
▼ ▼ ▼ ▼
CdpCommand ZombiePage Session RpaGlobal
Executor Manager Manager Storage
Page DOM Operations (click, type, getText, etc.)
```
**已有 JS SDK 路径(不变):**
```
页面内 JavaScript
│ CDP binding (chrome.runtime.sendMessage)
CdpBridgeManager (C++, Renderer → Browser IPC)
CommandRouter (Same instance!) ◄── 同一个 CommandRouter
├───────────┬──────────────┬──────────────┐
▼ ▼ ▼ ▼
CdpCommand ZombiePage Session RpaGlobal
Executor Manager Manager Storage
```
两条路径汇聚于同一个 CommandRouter 实例。sgClaw 只是新增了一个命令来源,
不改变 CommandRouter 的任何现有逻辑。区别仅在于:
- JS SDK 路径:无 MAC whitelist check页面 JS 已通过 CSP 和 rules.json 限制)
- Pipe 路径:增加 MAC whitelist check因为 sgClaw 被视为外部进程,需额外校验)
### 6.3 Pipe 允许的 Action 子集
并非所有 CommandRouter 支持的 40+ 个 action 都允许通过 pipe 调用。
出于安全考虑pipe 来源的命令被限制在以下安全子集中:
**允许通过 Pipe 调用(无需确认):**
| Action | 描述 | 备注 |
|------------------|--------------------|---------------------------------|
| click | 点击元素 | 需 expected_domain 匹配 |
| type | 输入文本 | 需 expected_domain 匹配 |
| navigate | 导航到 URL | URL 必须在域白名单内 |
| getText | 获取元素文本 | 只读操作,安全 |
| getHtml | 获取元素 HTML | 只读操作,安全 |
| waitForSelector | 等待元素出现 | 只读操作,安全 |
| pageScreenshot | 页面截图 | 返回 base64用于 Agent observe |
| storageSet | 写 RpaGlobalStorage | key 前缀限制为 sgclaw.* |
| storageGet | 读 RpaGlobalStorage | key 前缀限制为 sgclaw.* |
| zombieSpawn | 创建 zombie page | 受池大小限制max 5 |
| zombieKill | 销毁 zombie page | 只能销毁自己创建的 zombie |
| select | 下拉框选择 | 需 expected_domain 匹配 |
| scrollTo | 滚动页面 | 需 expected_domain 匹配 |
| getAomSnapshot | 获取 AOM 快照 | 只读操作,核心 observe 能力 |
**禁止通过 Pipe 调用(硬编码拒绝):**
| Action | 描述 | 禁止原因 |
|--------------------|--------------------|--------------------------------------------|
| eval | 执行任意 JS | 最高危操作,可绕过一切安全限制 |
| executeJsInPage | 在页面执行 JS | 等同于 evalprompt injection 核心攻击面 |
| registerJsFunction | 注册 JS 回调 | 可植入持久化代码 |
| setRequestInterceptor | 拦截网络请求 | 可窃取请求中的 token/cookie |
| exportCookies | 导出所有 cookie | 可窃取会话凭证 |
**需要用户确认的操作Human-in-the-loop**
| Action | 描述 | 确认原因 |
|------------------|--------------------|---------------------------------|
| sessionLogin | 执行登录流程 | 涉及凭证使用,需用户知情 |
| sessionLogout | 登出会话 | 可能中断用户正在进行的工作 |
| clearStorage | 清空存储 | 不可逆操作 |
---
## 7. 跨平台策略
### 7.1 STDIO Pipe 跨平台实现
STDIO Pipe 的跨平台实现依赖 Chromium 已有的 `base::LaunchProcess` 抽象层,
sgClaw 团队无需编写平台特定的管道代码。
**Linux 实现路径:**
```
Browser Process (C++) sgClaw (Rust)
───────────────────── ─────────────
1. pipe(fds) → fds[0](读), fds[1](写)
2. pipe(fds2) → fds2[0](读), fds2[1](写)
3. fork()
4. 子进程:
dup2(fds[0], STDIN_FILENO)
dup2(fds2[1], STDOUT_FILENO)
exec("sgclaw") ──────────────────→ main()
5. 父进程: std::io::stdin() → 读取命令
write(fds[1], ...) ──────────────→ std::io::stdout() → 写回响应
read(fds2[0], ...) ◄──────────── BufReader + BufWriter
```
Chromium 的 `base::LaunchProcess` 封装了上述 `pipe()` + `fork()` + `exec()` 流程,
通过 `LaunchOptions::fds_to_remap` 配置 fd 映射。
**Windows 实现路径:**
```
Browser Process (C++) sgClaw (Rust)
───────────────────── ─────────────
1. CreatePipe(&hRead1, &hWrite1, ...)
2. CreatePipe(&hRead2, &hWrite2, ...)
3. SetHandleInformation(hRead1, INHERIT)
4. SetHandleInformation(hWrite2, INHERIT)
5. CreateProcess("sgclaw.exe",
STARTUPINFO {
hStdInput = hRead1, main()
hStdOutput = hWrite2 std::io::stdin() → ReadFile
}) std::io::stdout() → WriteFile
6. WriteFile(hWrite1, ...) ──────────→
ReadFile(hRead2, ...) ◄────────
```
Chromium 的 `base::LaunchProcess` 在 Windows 下使用 `CreateProcess` + `STARTUPINFO`
实现句柄继承,与 Linux 的 fd 继承语义一致。
**Rust 侧统一代码:**
无论 Linux 还是 Windowssgclaw 的 Rust 代码完全一致:
```rust
// sgclaw 端无需区分平台
let reader = BufReader::new(std::io::stdin());
let writer = BufWriter::new(std::io::stdout());
for line in reader.lines() {
let msg: PipeMessage = serde_json::from_str(&line?)?;
let response = handle_message(msg).await?;
writeln!(writer, "{}", serde_json::to_string(&response)?)?;
writer.flush()?;
}
```
### 7.2 目标平台
| 平台 | 操作系统 | CPU 架构 | 状态 | 备注 |
|-----------|--------------------------|----------|------------------|-----------------------------|
| Primary | 银河麒麟 V10 SP1 | x86_64 | 目标部署环境 | 政企客户主要使用平台 |
| Secondary | Windows 10 / 11 | x86_64 | 开发 + 部分部署 | 开发团队日常使用 + 部分客户 |
| Future | 银河麒麟 V10 SP1 | aarch64 | 规划中 | 国产 ARM 服务器/终端 |
**银河麒麟 V10 SP1 特殊注意事项:**
- 基于 Ubuntu 内核但定制了安全策略SELinux/AppArmor 变体)
- `procfs` 默认配置限制跨进程 fd 访问,有利于 Pipe Hijack 防御
- `kernel.yama.ptrace_scope=1`,非父进程无法 ptrace增强安全性
- glibc 版本需确认兼容(通常 >= 2.31Rust musl 静态链接可规避
### 7.3 sgClaw 二进制分发
sgClaw 编译为单一静态链接的 Rust 二进制,无外部运行时依赖。
```
sgclaw 二进制产物
├─ Linux: sgclaw (~8.8 MB, musl 静态链接, strip)
│ target: x86_64-unknown-linux-musl
│ 无 glibc 依赖,银河麒麟直接运行
└─ Windows: sgclaw.exe (~9.0 MB, MSVC 链接, strip)
target: x86_64-pc-windows-msvc
无额外 DLL 依赖
```
构建命令:
```bash
# Linux (musl 静态链接,确保银河麒麟兼容)
cargo build --release --target x86_64-unknown-linux-musl
strip target/x86_64-unknown-linux-musl/release/sgclaw
# Windows (交叉编译)
cargo build --release --target x86_64-pc-windows-msvc
```
部署方式:将 `sgclaw` / `sgclaw.exe` 放置在 SuperRPA 浏览器安装目录下:
```
superrpa-browser/
├─ chrome (浏览器主程序)
├─ superrpa-chromium.sh (启动脚本)
├─ sgclaw (AI Agent, 新增)
├─ sgclaw-skills/ (技能目录, 新增)
│ ├─ oa-approval.js
│ └─ ...
└─ resources/
└─ rules.json (域白名单, 已有)
```
---
## 8. 与 SuperRPA 已有子系统的关系
### 8.1 复用与新增总览
sgClaw 的核心设计原则之一是**最小侵入**——最大限度复用 SuperRPA 浏览器的已有基础设施,
将新增代码量控制在最低水平。
```
┌─────────────────────────────────────────────────────────────────────┐
│ SuperRPA Browser 已有子系统 │
│ (全部保持不变,零修改) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Command │ │ CdpBridge │ │ ZombiePage │ │
│ │ Router │ │ Manager │ │ Manager │ │
│ │ (40+ acts) │ │ (JS SDK) │ │ (5 pools) │ │
│ └──────┬──────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ┌──────┴──────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CdpCommand │ │ Session │ │ RpaGlobal │ │
│ │ Executor │ │ Manager │ │ Storage │ │
│ │ │ │ (状态机) │ │ (KV store) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ credential │ │ rules.json │ │ BrowserAction│ │
│ │ _store │ │ (URL白名单) │ │ API │ │
│ │ (Keyring) │ │ │ │ (JS 调度) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ sgClaw 新增组件 │
│ (~500 lines C++ in Browser + Rust binary) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SgClawProcessHost (C++, ~200-300 lines) │ │
│ │ ├─ 进程生命周期管理 (Start / Stop / OnCrash) │ │
│ │ ├─ PipeListener (~150 lines, async read loop) │ │
│ │ ├─ MAC Whitelist Check (~100 lines) │ │
│ │ └─ FunctionsUI handlers for start/stop (~50 lines) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Side Panel UI Controls (Vue, ~100 lines) │ │
│ │ ├─ [启动] / [停止] 按钮 │ │
│ │ ├─ 状态指示 (Running / Stopped / Error) │ │
│ │ ├─ 任务输入框 │ │
│ │ └─ 执行日志 (streaming output) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ sgclaw Binary (Rust, based on ZeroClaw) │ │
│ │ ├─ Agent Runtime (ReAct loop) │ │
│ │ ├─ BrowserPipeTool (custom Tool trait impl) │ │
│ │ ├─ SkillLoader (JS business skills) │ │
│ │ ├─ LLM Provider (Claude / GPT / local) │ │
│ │ ├─ Memory (SQLite + vector) │ │
│ │ ├─ MAC Policy (domain / action whitelist) │ │
│ │ ├─ Critic + Circuit Breaker │ │
│ │ └─ MCP Client (rmcp) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
### 8.2 复用子系统详细说明
| 子系统 | 代码位置 | sgClaw 如何复用 | 是否修改 |
|---------------------|----------------------|---------------------------------------------------|---------|
| CommandRouter | browser/rpa/command/ | sgClaw 通过 pipe → PipeListener → CommandRouter | 不修改 |
| CdpBridgeManager | browser/rpa/cdp/ | 独立运行JS SDK 路径不受影响 | 不修改 |
| ZombiePageManager | browser/rpa/zombie/ | sgClaw 通过 zombieSpawn/zombieKill action 使用 | 不修改 |
| SessionManager | browser/rpa/session/ | sgClaw 可触发 sessionLogin (需确认) | 不修改 |
| RpaGlobalStorage | browser/rpa/storage/ | sgClaw 通过 storageSet/storageGet 读写 | 不修改 |
| BrowserAction API | browser/rpa/action/ | 已有 JS SDK 继续使用,与 sgClaw 独立 | 不修改 |
| credential_store | browser/rpa/cred/ | 仅 SessionManager 内部使用sgClaw 无法访问 | 不修改 |
| rules.json | resources/ | sgClaw 的 MAC Policy 也读取此文件进行域校验 | 不修改 |
### 8.3 新增代码量估算
| 组件 | 语言 | 估计行数 | 位置 |
|-----------------------------|--------|-------------|-------------------------------|
| SgClawProcessHost | C++ | ~200-300 | browser/rpa/sgclaw/ |
| PipeListener | C++ | ~150 | browser/rpa/sgclaw/ |
| MAC Whitelist Check (Pipe) | C++ | ~100 | browser/rpa/sgclaw/ |
| FunctionsUI handlers | C++ | ~50 | browser/rpa/functions_ui/ |
| Side Panel UI controls | Vue | ~100 | resources/side_panel/ |
| **Browser 侧合计** | | **~500-600**| |
| sgclaw binary | Rust | ~3000-5000 | 独立仓库 sgclaw/ |
### 8.4 影响评估
**对已有功能的影响:零。**
具体论证:
1. **CommandRouter 不变**sgClaw 只是新增了一个命令来源pipeCommandRouter 本身的
dispatch 逻辑、action 处理函数、错误处理均不修改。
2. **JS SDK 不变**CdpBridgeManager 独立于 SgClawProcessHost两者通过不同的 IPC 路径
到达同一个 CommandRouter。JS SDK 的用户不会感知到 sgClaw 的存在。
3. **会话管理不变**SessionManager 的状态机login → active → expired → re-login
完全不变。sgClaw 只能通过 CommandRouter 触发 sessionLogin action需用户确认
不直接操作 SessionManager 内部状态。
4. **存储隔离**sgClaw 通过 RpaGlobalStorage 的 key 被限制在 `sgclaw.*` 命名空间下,
不会与已有的 JS SDK 存储的 key 冲突。
5. **二进制独立**sgclaw 是独立二进制,不链接 Chromium 的任何库。
即使 sgclaw 构建失败或缺失,浏览器仍然正常工作——只是 Side Panel 的 [启动] 按钮
会报错"sgclaw binary not found"。
---
*文档结束。本文档为 sgClaw L1 层架构设计参考,后续 L2详细设计、L3实现规范
将在此基础上展开。*