- 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。本地提交,未推未合未部署。
15 KiB
对齐 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(0750chv-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. 诚实边界(本轮不宣称的东西)
- 分类器是确定性规则,不是模型分类器:注入启发式、表单风险、 下载规则都是字面规则,有漏报也有误报;它只做“标注 + 拦截高危”, 不做语义判断。Muse 的浏览器分类器家族是模型 + 规则,本轨只做了 规则那半。
- a11y-tree-only 的边界与 P2 相同:单元内进程仍可用 curl/Python 标准库直接发 HTTP 取页面原文——那不是“浏览器 DOM 访问”,但也不是 无障碍树;出口只走浏览器子代理是 P3 Sentinel 的事,本轨不冒充已封死。
- 暂停的触发源不完整:显式
pause操作与自动暂停(POST 观测、 下载命中)已实现并测试;“用户在浏览器里接管”与“凭证注入填表”这两 个真实触发源还没有对端——前者要等真人浏览器联动(控制面),后者是 P3 的 authd 注入点。接口与状态机已留好,联动列为真机验证。 - ask 队列的审批人不在本轨:队列是文件型的待审批记录(与 P3
ApprovalQueue同语义,见 §4),resolve_ask接受外部写回的allow/deny;但谁来审批(人、还是 Sentinel 策略)不在本轨决定, P5 做审批 UI 在对话外之后再接。 - 拦截名单是静态的:首版只有格式示例与自测条目,生产条目须由运维 在构建时填入;名单的覆盖率、误拦率本轮无数据,不宣称“已拦截恶意站”。
- 性能数字全是待验证目标:CDP broker 多一跳的延迟、快照标注后 的 token 占用、拦截名单加载耗时——本轮无真机验证,一律记入 §5 清单, 不在代码/文档里写成已测得。
- 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 双向同步。
- 浏览器对齐验收(新增):
- 客机内
chv-cdp-broker进程以chv-browser身份跑,Chromium 正常启动(--no-sandbox未加时仍能起);启动参数里无--remote-debugging-port。 - 经 broker
browser open https://example.com返回标题 Example Domain 与无障碍树,响应里无ws://、无 DevTools 地址、无 HTML。 - 单元内进程连不上 CDP 端点(broker 的 socket 只在单元外,单元内
无 Chromium 可连;
python3 -c "import playwright"在单元内仍失败)。 - 含密码字段的测试页快照带
form_risk: password;此时open导航 返回ask、页面不跳转;resolve_ask allow后重发导航成功。 - 注入文本测试页快照带
untrusted: true+injection_flags。 - 命中拦截名单的域名导航被拒(先用
.test自测条目,再换生产名单复测)。 - 触发下载
.exe的测试页:下载被取消、代理暂停、ask入队。 pause后open/snapshot被拒、ping仍通;resume后恢复。- 性能记录(只记录、不设门):broker 多一跳的快照往返、拦截名单加载耗时。
- 客机内
- 集成者须改的一处:P2 测试
test_open_returns_a11y_snapshot_with_fake_playwright在合入 P4 后失效(BrowserAgent不再直连 Playwright),改为走假 CDP broker(与 P4 测试的FakeCdpBroker同款),断言不变。