# 对齐 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` 同款),断言不变。