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

15 KiB
Raw Blame History

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