Files
codex-app-server/docs/chv-muse-p4-gap.md
T
host 1c8fc3f7c8 对齐 Muse P4:浏览器完全对齐(CDP broker 单元外 + a11y-tree-only + 确定性分类器)
- chv-guest/chv-cdp-broker.py(新):单元外 CDP broker,用户 chv-browser,
  独家 chromium.launch(headless=True, args=LAUNCH_ARGS);启动参数白名单
  无 --remote-debugging-port、无 devtools;导航前 URL 校验 + 拦截名单;
  快照恒带 untrusted_input 与 injection_flags;高风险表单在页时 open
  即 ask 入队;观测到导航 POST 自动暂停;命中拦截规则的下载取消 + 暂停
  + ask;全部回包与错误经 sanitize_error + response_leaks 检查。
- chv-guest/chv-browser.py(P2 版重构):改为 broker 客户端,不再直连
  Playwright(本文件无 playwright import),保留 URL 校验 + 快照截断
  作为第二层防线;暂停状态与 broker 同步。
- chv-guest/chv_browser_guard.py(新):纯 stdlib 共享规则——validate_url、
  拦截名单加载/匹配、快照截断、错误消毒(去 HTML、截断 200 字符、凭据形
  key=value 与 sk- 形 token 打码)、注入启发式(中英文各 6 条)、表单风险
  扫描、下载扫描(可执行扩展名)、文件型 ask 队列(pending/allowed/denied,
  与 P3 ApprovalQueue 同语义)、启动参数与回包泄露检查、脚本注入面
  禁止清单(FORBIDDEN_PAGE_ATTRS/FORBIDDEN_OPS)。
- chv-guest/browser-blocklist.txt(新):静态拦截名单;首版只有 RFC 2606
  .test 自测条目,生产条目由运维构建时填入,无在线更新。
- chv-guest/build.sh/buildinfo.py:P2 三脚本 + P4 三文件纳入镜像与
  buildinfo;新增 chv-cdp-broker.service 与 /run/chv/ask-queue tmpfiles。
- docs/chv-muse-p4-gap.md(新):现状 vs Muse 官方要求差距表、改动、
  诚实边界、P2 合入约定、真机待验证清单(round 12)。
- docs/chv-production-readiness.md:补 §15(P4 一节;编号合入时若与
  P2 的 §15 撞,顺延)。
- cloud/tests/test_chv_muse_p4.py(新,36 项):guard 单元、AST 钉住
  禁止属性、假 Playwright broker 测试、假 broker agent 测试、真 Unix
  socket 联调、buildinfo/build 干跑断言;含否定测试(evaluate 被拒、
  DOM 外泄被拒、DevTools 不可达、私有地址被拒、高风险表单被拦、
  注入文本被标注)。

测试:P4 新测试 36/36;全量 cloud 套件 1903 项,两次连续全过
(skipped=8)。附记:第一次全量跑出 1 个失败但输出未保留、后两次
均未复现,疑为时序抖动,未追认。
真机验证未做:排队等 round-11/第三棒释放 100.65.51.33,清单见 gap
表 §5。本地提交,未推未合未部署。
2026-11-06 22:36:56 +00:00

175 lines
15 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.
# 对齐 Muse P4 差距表:浏览器完全对齐
依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
(官方以 Meta security.muse.ai 2026-09-08 为准;第三方拆解只作参考,
逆向细节不得写成官方架构)。Muse 浏览器安全要求一句话:真实最新的
Chromium、位于虚拟化层之后;浏览器子代理只看到页面无障碍树快照、
不是原始 DOM;它不能在页面上下文中运行 JavaScript、无脚本动词、
无浏览器进程内 exec、DevTools 被禁用;CDP 连接由单元外 broker 管理;
用户接管浏览器或凭证存储正在填表时,代理被暂停、完全不能行动;
另有浏览器分类器家族与恶意网站拦截。
本轨范围只限 P4:浏览器完全对齐。不碰 P1(常驻 VM/气球/vsock)、
不碰 P3(authd/Sentinel 宿主侧)、不碰 P5(Sentinel 纵深 eBPF)、
不碰 P6(privsep worker/确定性安全检查层)。分支
`feat-chv-muse-p4`(从 `06c3e17` 分出)。
## 0. 与 P2 的关系(先说清,免得合入时糊涂)
P2(提交 `c61484a`)已把浏览器子代理独立出来:`chv-guest/chv-browser.py`
(只返无障碍树快照,`evaluate`/`content`/DevTools 一律拒绝)、
`chv-guest/chv-broker.py`(单元外转发)、`chv-guest/chv-cell.py`、
`build.sh`/`buildinfo.py` 的安装步骤。P4 的基线 `06c3e17` 里没有这些
文件——P4 要改的正是它们,所以本轨先把 P2 的五个文件按原样拷进来
(`cmp` 逐字节核对过,与 `c61484a` 完全一致),再在上面做 P4 的收紧。
文件头与本表注明来源。
合入时的处理(给集成者):
- `chv-guest/chv-cell.py`、`chv-guest/chv-broker.py`:P4 未改内容,
与 P2 版逐字节一致,合入无冲突。
- `chv-guest/chv-browser.py`:P4 在 P2 版基础上重构(不再直连
Playwright,改走 CDP broker;见 §2),合入时直接取 P4 版。
- `chv-guest/build.sh`、`chv-guest/buildinfo.py`:P4 版 = P2 版的步骤
+ P4 新增步骤(CDP broker 服务、guard 模块、拦截名单、ask 队列目录),
合入时取并集。
- `cloud/tests/test_chv_muse_p2.py`:P2 的测试文件本轨不拷、不改;
P4 的测试是独立的 `cloud/tests/test_chv_muse_p4.py`。P2 测试里
有一处与 P4 行为有关:`test_open_returns_a11y_snapshot_with_fake_playwright`
假定 `BrowserAgent` 直连 Playwright——合入后该测试须按 P4 的新结构改
(走假 CDP broker),见 §5;本轨不改它,留给集成者按本表执行。
## 1. 现状 vs Muse
| 项 | 现状(P2 已交付,代码为准) | Muse 目标 | 本轨做法 |
|---|---|---|---|
| 子代理看到什么 | `accessibility.snapshot()` 无障碍树(截断 200 节点/64KB)+ 标题/URL;`evaluate`/`content` 已拒 | 只见无障碍树快照,不见原始 DOM | 收紧到底:快照路径上所有回包与错误信息过 `sanitize_error`(去 HTML 标签、截断 200 字符、含尖括号即丢弃原文),任何响应字段不得含 `ws://`、DevTools 地址、原始 HTML;AST 测试钉住两个客机脚本里不存在脚本注入面 |
| 页面内 JS | socket 层拒 `evaluate` 等操作,但 Playwright 直连在 `chv-browser.py` 进程内——代码里今天没调,不等于调不了 | 不能在页面上下文运行 JS,无脚本动词 | Chromium 的所有权移到单元外 CDP broker;broker 只调白名单调用(`goto`/`title`/`url`/`accessibility.snapshot`/`screenshot`/`close` + 只读监听 `on(request/download)`);`evaluate`/`add_script_tag`/`add_init_script`/`expose_function`/`set_content`/`eval` 等一律不在代码里出现,AST 测试扫两个脚本的全部属性名 |
| 浏览器进程内 exec | 无此操作 | 无浏览器进程内 exec | 同上:broker 与子代理的代码里不存在任何经浏览器执行命令的调用;`page.keyboard`/`mouse` 等交互调用也不在白名单内(本轨无点击/填表操作) |
| DevTools | 未显式开 `--remote-debugging-port`,但 Playwright 直连默认持有 CDP 端点,且 `chv-browser.py` 进程内可及 | DevTools 被禁用;CDP 由单元外 broker 管理 | launch 参数白名单(见 §2),无 `--remote-debugging-port`、无 `devtools=True`;CDP 端点(ws 地址)绝不进任何响应;Chromium 由 `chv-cdp-broker`(单元外、用户 `chv-browser`)独家启动,子代理只经 broker socket 说话 |
| CDP broker | 无(P2 是子代理直连 Playwright) | CDP 连接由单元外 broker 管理 | 新 `chv-guest/chv-cdp-broker.py`:单元外进程,Unix socket `/run/chv/cdp-broker.sock`,独家持有 Playwright/Chromium;`chv-browser.py` 改为 broker 的客户端 |
| 接管/填表暂停 | 无此语义 | 用户接管或凭证注入填表时代理被暂停、完全不能行动 | 状态机 `active/paused` 落在 CDP broker(`pause`/`resume`/`status` 操作);暂停时除 `ping`/`resume`/`status` 外一切操作拒绝;触发源:显式 `pause` 操作(供将来用户接管通道调用)、POST 表单提交被观测到时自动暂停、高风险下载命中时自动暂停;凭证注入填表联动是 P3 的 authd 注入点,本轨只留 `pause` 操作接口与接线设计 |
| 高风险表单提交 | 无(P2 的 API 里根本没有点击/填表操作) | 支付/密码类表单提交前停下等审批 | 确定性规则(不冒充模型分类器):`detect_form_risk` 扫无障碍树,密码/支付字段即标 `form_risk`;高风险表单在页时 `open` 导航即返回 `ask`(入待审批队列、执行停止);观测到 POST 请求即自动暂停 + 入队。审批队列与 P3 的 `ApprovalQueue` 同语义(见 §4) |
| DOM 内提示注入 | 无标注 | 外部数据进模型上下文时标注“不受信任输入” | `detect_injection` 启发式扫描快照文本(中英文注入模式,见 §2),命中即回包带 `untrusted: true` + `injection_flags`;快照本身恒带 `untrusted_input: true`(外部页面文本永远是数据、不是指令) |
| 下载文件 | 无处理 | 下载文件中的提示注入要检测/阻断 | `scan_download` 钩子:一条可测规则(可执行扩展名与双扩展名即拦:`.exe/.msi/.bat/.cmd/.ps1/.sh/.dmg/.pkg/.apk/.scr/.vbs` 及 `*.pdf.exe` 形),命中即取消下载 + 暂停 + 入队;接口留好,后续规则只加条目 |
| 恶意网站拦截 | 只有 localhost/私有地址字面量拒绝 | 恶意网站拦截(Meta 现有系统名单) | 静态名单 `chv-guest/browser-blocklist.txt`(一行一个域名/后缀),导航前检查;名单来源与更新机制见 §2;本轮只做静态名单 + 测试,不冒充实时情报 |
## 2. 改了什么(本轨交付物)
- `chv-guest/chv_browser_guard.py`(新,纯标准库,无 Playwright):
共享纯函数,两脚本共用:`validate_url`(P2 规则:只 http/https、
拒 localhost/私有地址字面量)、`check_blocklist`(读名单文件、
后缀匹配)、`truncate_a11y`(P2 的截断逻辑原样搬入)、
`sanitize_error`(错误信息去 HTML、截断 200 字符、凭据形
key=value 与 sk- 形 token 打码、含尖括号即丢弃原文)、
`detect_injection`(启发式模式表,中英文各 6 条,命中返回标签)、
`detect_form_risk`(无障碍树扫密码/支付字段)、`scan_download`
(一条可执行扩展名规则)、`enqueue_ask`/`list_asks`/`resolve_ask`
(文件型待审批队列,`/run/chv/ask-queue/`,一条 ask 一个 JSON 文件,
`pending/allowed/denied` 三态)、`LAUNCH_ARGS`(Chromium 启动参数
白名单)、`FORBIDDEN_PAGE_ATTRS`(禁用的 Playwright 调用名表)。
- `chv-guest/chv-cdp-broker.py`(新,标准库 + playwright 懒加载):
单元外 CDP broker,用户 `chv-browser`,Unix socket
`/run/chv/cdp-broker.sock`。独家 `chromium.launch(headless=True,
args=LAUNCH_ARGS)`;操作 `open/snapshot/screenshot/close/ping/
pause/resume/status/list_asks/resolve_ask`;导航前 `validate_url` +
`check_blocklist`;快照恒带 `untrusted_input: true`,命中注入模式带
`injection_flags`;表单风险与 POST 观测、下载命中走 ask 队列 +
自动暂停;所有回包与日志过 `sanitize_error`,绝不返回 ws 端点。
- `chv-guest/chv-browser.py`(P2 版基础上改):不再直连 Playwright,
改为 CDP broker 的客户端(每请求一连接,Unix socket JSON 行);
保留 `validate_url`/`truncate_a11y` 二次校验(纵深防御)、
`FORBIDDEN_OPS`(扩到 `click/fill/type/press/hover/select/check/
evaluate/content/devtools/js/route/expose/add_init_script/
add_script_tag/set_content/eval`)、暂停镜像状态(broker 暂停时
本层同样拒绝);`playwright_factory` 参数删除(不再需要)。
- `chv-guest/browser-blocklist.txt`(新):静态拦截名单。格式一行一个
条目(域名或 `.后缀`);来源:随镜像发布的静态文件,首版只含 RFC 2606
保留 `.test` 域的格式示例与自测条目(`malware.test`、
`.phishing.test`),**不编造真实恶意域名**;生产名单由运维在构建时
用公开情报源(如 URLhaus / Google Safe Browsing 导出)的域名替换
本文件后重建镜像;更新机制 = 换文件 + 重建镜像,无在线更新(在线
更新列为后续,不冒充已做)。
- `chv-guest/build.sh`:P2 的步骤原样保留,加 P4 步骤:拷入
`chv-cdp-broker.py`、`chv_browser_guard.py`、`browser-blocklist.txt`
(缺一即失败);装 `chv-cdp-broker.service`(用户 `chv-browser`)
并 enable;tmpfiles.d 加 `/run/chv/ask-queue`(0750
`chv-browser:chv-browser`)。
- `chv-guest/buildinfo.py`:P2 的内容项原样保留,加 `chv-cdp-broker`、
`chv-browser-guard`、`browser-blocklist`。
- `cloud/tests/test_chv_muse_p4.py`(新):见 §5。
- `docs/chv-production-readiness.md`:补 P4 一节,旧定案不删、只追加。
## 3. 诚实边界(本轮不宣称的东西)
1. **分类器是确定性规则,不是模型分类器**:注入启发式、表单风险、
下载规则都是字面规则,有漏报也有误报;它只做“标注 + 拦截高危”,
不做语义判断。Muse 的浏览器分类器家族是模型 + 规则,本轨只做了
规则那半。
2. **a11y-tree-only 的边界与 P2 相同**:单元内进程仍可用 curl/Python
标准库直接发 HTTP 取页面原文——那不是“浏览器 DOM 访问”,但也不是
无障碍树;出口只走浏览器子代理是 P3 Sentinel 的事,本轨不冒充已封死。
3. **暂停的触发源不完整**:显式 `pause` 操作与自动暂停(POST 观测、
下载命中)已实现并测试;“用户在浏览器里接管”与“凭证注入填表”这两
个真实触发源还没有对端——前者要等真人浏览器联动(控制面),后者是
P3 的 authd 注入点。接口与状态机已留好,联动列为真机验证。
4. **ask 队列的审批人不在本轨**:队列是文件型的待审批记录(与 P3
`ApprovalQueue` 同语义,见 §4),`resolve_ask` 接受外部写回的
`allow/deny`;但谁来审批(人、还是 Sentinel 策略)不在本轨决定,
P5 做审批 UI 在对话外之后再接。
5. **拦截名单是静态的**:首版只有格式示例与自测条目,生产条目须由运维
在构建时填入;名单的覆盖率、误拦率本轮无数据,不宣称“已拦截恶意站”。
6. **性能数字全是待验证目标**:CDP broker 多一跳的延迟、快照标注后
的 token 占用、拦截名单加载耗时——本轮无真机验证,一律记入 §5 清单,
不在代码/文档里写成已测得。
7. **Chromium 启动参数以 Playwright 1.65 + headless-shell 为准**:
白名单参数均为真实 Chromium 开关(`--disable-gpu`、`--no-first-run`、
`--disable-dev-shm-usage`、`--disable-extensions`、
`--disable-background-networking`、`--disable-sync`、
`--disable-translate`、`--mute-audio`);`--no-sandbox` 刻意不在
白名单内(浏览器以非 root 的 `chv-browser` 身份跑,不应再降级沙箱;
若客机内启动失败,属真机验证项,不在本轮改参数凑合)。
## 4. 与 P3/P5/P6 的接口(不碰对方文件,只定契约)
- **P4 → P3(ask 队列)**:ask 记录格式与 P3 `ApprovalQueue` 同语义:
`{ask_id, kind, url, detail, status: pending/allowed/denied,
created_at}`;`kind` 本轨有三类:`form_submit`(高风险表单时导航)、
`form_submit_observed`(观测到 POST)、`download_blocked`(下载命中)。
宿主侧 Sentinel 读到 `pending` 的 ask 后按 P3 的 allow/deny/ask 决策,
经 `resolve_ask` 写回;`allowed` 后 broker 解除暂停(`form_submit`
类还需调用方重发原操作,本轨不自动续跑)。队列的宿主侧读取路径是
`host.py` 经 SSH 读 `/run/chv/ask-queue/`(接线设计,P6 或控制面实现,
本轨只留 `list_asks` 操作与文件格式)。
- **P4 → P5(审批 UI)**:`resolve_ask` 的调用方是 P5 的对话外审批 UI;
在此之前 ask 只记录、不自动放行(`allowed` 缺席即一直停)。
- **P4 → P6(确定性安全检查层)**:`detect_injection` 的
`untrusted_input: true` 标注是 P6 的外部数据标注的浏览器侧来源;
P6 复用本轨的模式表时以本文件为准,不另起一份。
- **P2 → P4(已兑现)**:P2 的 `BrowserAgent` 直连 Playwright 的结构
已被本轨改成 broker 客户端;P2 测试里假定直连的那一处(见 §0)由
集成者按本表改,不在本轨动 P2 的测试文件。
## 5. 真机待验证清单(round 12,测试机空闲后)
- lite 验收照旧:qcow2 ≤1GB、冷启动到 SSH、codex 版本、headless-shell
例、Gitea 双向同步。
- 浏览器对齐验收(新增):
1. 客机内 `chv-cdp-broker` 进程以 `chv-browser` 身份跑,Chromium
正常启动(`--no-sandbox` 未加时仍能起);启动参数里无
`--remote-debugging-port`。
2. 经 broker `browser open https://example.com` 返回标题 Example
Domain 与无障碍树,响应里无 `ws://`、无 DevTools 地址、无 HTML。
3. 单元内进程连不上 CDP 端点(broker 的 socket 只在单元外,单元内
无 Chromium 可连;`python3 -c "import playwright"` 在单元内仍失败)。
4. 含密码字段的测试页快照带 `form_risk: password`;此时 `open` 导航
返回 `ask`、页面不跳转;`resolve_ask allow` 后重发导航成功。
5. 注入文本测试页快照带 `untrusted: true` + `injection_flags`。
6. 命中拦截名单的域名导航被拒(先用 `.test` 自测条目,再换生产名单复测)。
7. 触发下载 `.exe` 的测试页:下载被取消、代理暂停、`ask` 入队。
8. `pause` 后 `open/snapshot` 被拒、`ping` 仍通;`resume` 后恢复。
9. 性能记录(只记录、不设门):broker 多一跳的快照往返、拦截名单加载耗时。
- 集成者须改的一处:P2 测试 `test_open_returns_a11y_snapshot_with_fake_playwright`
在合入 P4 后失效(`BrowserAgent` 不再直连 Playwright),改为走假
CDP broker(与 P4 测试的 `FakeCdpBroker` 同款),断言不变。