feat: platform browser multi-tab, navigation allowlist, close cleanup, egress filter, allowlist worker wiring
- Multi-tab: browser_new_page / browser_list_pages / browser_switch_page / browser_close_page in playwright_mcp.py; each new page gets a fresh Playwright context, so cookies and storage are isolated between pages. Single-page tools act on the current page; switching to a missing index is an error, not a crash; closing the last page is an error (use browser_close). - Navigation allowlist: BROWSER_ALLOWED_HOSTS (comma list, leading-dot suffix match, case/port-insensitive) checked in _check_allowed_url before launching Chromium; unset (or empty) means no check, the old behaviour. file:// always allowed; localhost/127.0.0.1 have no special treatment. - Close cleanup: browser_close closes all pages and best-effort removes cwd browser-live.png / browser-live.json; custom screenshot files are untouched. list_pages needs no launch. - Egress filter: 10 programming-source Tinyproxy BASIC rules (python.org, nodejs.org, go.dev, pkg.go.dev, rust-lang.org, developer.mozilla.org, crates.io, stackoverflow.com, cve.mitre.org, nvd.nist.gov); already-present github.com / pypi.org / npmjs.org not duplicated; searx.diante.me stays exact-only, no diante.me wildcard. - Allowlist worker wiring: _docker_task_context now passes a set, non-empty BROWSER_ALLOWED_HOSTS from the worker env into [mcp_servers.playwright.env] in the tenant config, so the MCP process actually sees it (container -e alone does not reach MCP subprocesses, 2026-10-30). Unset/empty writes nothing and never overwrites; the value never goes into logs; it is not added to the websearch env block or the container env. ensure_playwright_mcp_env now returns False without rewriting when the result would be identical (canonical join: one blank line before the block), so a second prep leaves an unchanged tenant config alone; other sections untouched. - Skill + docs: browser-automation SKILL.md multi-tab/allowlist section; docs/platform-browser.md comparison doc (new file). - Tests (mock/fake only, no network, no real Chromium, no real credentials): cloud.tests.test_playwright + cloud.tests.test_websearch_worker, 48 tests OK, including 6 new wiring tests (switch off passes no variable, switch on reaches MCP env verbatim, empty never written, old-tenant merge idempotent + no-rewrite, ensure no-op when identical, allowlist enforces only after wiring). - NOT done (see PR body): no persistent profile, no credential vault, no approval card, no CAPTCHA bypass, in-page link navigation does not pass the allowlist, no real-Chromium live verification, host tinyproxy filter not reloaded.
This commit is contained in:
1 parent
27f48fe1ed
commit
da7a9950fb
8 files changed
+1050
-12
No files matched your search
@@ -0,0 +1,101 @@
|
||||
# 平台浏览器:与 Muse browser 的对照
|
||||
|
||||
本轨目标:把编程任务平台的浏览器能力复刻到与 Muse browser
|
||||
可对照的程度——只服务编程任务(验证自己的 Web 应用、读官方
|
||||
文档、查包与 CVE),不复刻个人 agent 的通用上网能力。
|
||||
|
||||
范围边界先说清:
|
||||
|
||||
- 搜索(browser.search / browser.open / browser.find 那一路)
|
||||
由另一轨负责,本轨只消费、不重建。
|
||||
- 多标签页已由本轨另一路完成:`browser_new_page` / `browser_list_pages` / `browser_switch_page` / `browser_close_page`,导航白名单见下文补充。
|
||||
- 本轨不部署:filter 改动只落在仓库文件里,主机 tinyproxy
|
||||
未 reload;真实站点未 live 验证(见文末“未验证”一节)。
|
||||
|
||||
## 逐项对照
|
||||
|
||||
| 能力 | Muse browser | 本轨状态 |
|
||||
| --- | --- | --- |
|
||||
| Live Chromium(真浏览器,非文本抓取) | 有 | 已有:`cloud/playwright_browser.py`,单页 headless Chromium,1280x800 视口 |
|
||||
| 导航 | 有 | 已有:`browser_navigate`;代理显式传 launch,域名不在白名单时报代理 403,不编造页面 |
|
||||
| 快照(无障碍式清单) | 有 | 已有:`browser_snapshot`,及 `browser_snapshot_refs`(ref -> 元素清单,`browser_click_ref` 按 ref 点击) |
|
||||
| 点击 / 填写 | 有 | 已有:`browser_click`、`browser_click_at`(坐标,有视口边界检查)、`browser_fill`、`browser_type_text` |
|
||||
| 键盘 / 鼠标 | 有 | 已有:`browser_key_press`、`browser_mouse_move` |
|
||||
| 截图 / 实时画面 | 有 | 已有:`browser_screenshot`、`browser_live_shot`(固定 live 文件 + JSON sidecar,供 console 轮询);截图只写任务工作区(`cwd`,文件名 basename 校验),不写共享目录 |
|
||||
| JS 求值 | 有 | 已有:`browser_evaluate` |
|
||||
| MCP 工具面 | 有 | 已有:`cloud/playwright_mcp.py`,stdio MCP,14 个工具(navigate / snapshot / snapshot_refs / click / click_at / click_ref / fill / type_text / key_press / mouse_move / evaluate / screenshot / live_shot / close) |
|
||||
| 观看 + 接管 | 有(观看与接管) | 已有:`BrowserPanel.tsx` 是观看 + 接管面板,不是纯观看面板;`holder` 驱动——用户接管(holder=user)时 agent 工具报错、把控制权交还用户,交还后 agent 续跑前先快照确认用户改动 |
|
||||
| Worker 播种 | — | 已有:worker `--playwright-mcp` 把 MCP 文件与配置播种进租户 codex-home |
|
||||
| 租户工作区隔离 | — | 已有:浏览器跑在任务沙箱内,截图与下载落任务工作区 |
|
||||
| 代理 403 如实报错 | — | 已有:域名不在 Tinyproxy 白名单时工具 plain 报错 403,不静默失败、不编造内容 |
|
||||
| 持久 profile / 跨回合登录态 | 有 | 缺口(本轨未做):每次任务的浏览器是 fresh 会话,登录态不跨回合保留 |
|
||||
| 登录凭证保险库 | 有 | 缺口 + 本轨不做:不存任何已存凭证;见“本轨不做” |
|
||||
| 服务端审批卡片(表单提交 / 下载 / 上传的 Allow / Ask / Deny) | 有 | 缺口(本轨未做):本轨只有 holder 接管阻塞(用户接管时 agent 工具报错停下),没有提交/下载/上传的审批流 |
|
||||
| 验证码 / 机器人检查 | 有处理流程 | 缺口(本轨未做绕过):被挡就报 blocked 停下,不绕过、不伪装 |
|
||||
| 下载落盘清单 | 有 | 缺口(本轨未做):除截图落任务工作区外,无统一的下载物清单面 |
|
||||
| 多标签页 | 有 | 已有(本轨补):`browser_new_page` / `browser_list_pages` / `browser_switch_page` / `browser_close_page`;新页自带全新 Playwright context,cookie/存储隔离 |
|
||||
| 真实站点 live 验证 | 有 | 未做:本轨只改 egress 白名单与文档,未在主机 tinyproxy 上 reload、未对真实站点跑 live 验证 |
|
||||
|
||||
## 本轨不做(明确排除)
|
||||
|
||||
- 个人购物下单。
|
||||
- 个人邮箱 / 社交登录。
|
||||
- 验证码绕过、机器人检查绕过。
|
||||
- 已存凭证保险库(任何真实密码、cookie、token 绝不写进
|
||||
代码、测试、文档、回执)。
|
||||
|
||||
## 本轨改动:egress 白名单
|
||||
|
||||
文件:`deploy/egress/tinyproxy-filter`(Tinyproxy POSIX BASIC
|
||||
正则写法,一行一条,`^\(.*\.\)*域名$` 形式;不要用 extended
|
||||
锚定写法,2026-10-26 已实测 extended 写法会把允许域名也挡成
|
||||
403;文件保持 644 可读)。
|
||||
|
||||
本轨新增 10 条(编程来源限定):
|
||||
|
||||
- `^\(.*\.\)*python\.org$`(含 docs.python.org 子域名规则覆盖)
|
||||
- `^\(.*\.\)*nodejs\.org$`
|
||||
- `^\(.*\.\)*go\.dev$`
|
||||
- `^\(.*\.\)*pkg\.go\.dev$`
|
||||
- `^\(.*\.\)*rust-lang\.org$`(含 docs.rust-lang.org)
|
||||
- `^\(.*\.\)*developer\.mozilla\.org$`(只放行该 host,不放行
|
||||
mozilla.org 通配)
|
||||
- `^\(.*\.\)*crates\.io$`
|
||||
- `^\(.*\.\)*stackoverflow\.com$`
|
||||
- `^\(.*\.\)*cve\.mitre\.org$`
|
||||
- `^\(.*\.\)*nvd\.nist\.gov$`
|
||||
|
||||
已在白名单、无需重复加:`github.com`、`pypi.org`、`npmjs.org`
|
||||
(另有 `pythonhosted.org` 供 pip)。
|
||||
|
||||
刻意不动:`^searx\.diante\.me$` 精确行不动;未加任何
|
||||
`diante.me` 通配(那会放行 control plane api.diante.me);
|
||||
未加购物、社交、邮箱、通用新闻、视频站。
|
||||
|
||||
对应测试:`cloud/tests/test_websearch_worker.py` 的
|
||||
`EgressFilterTestCase` 新增 2 项(见“验证”一节)——断言新规则
|
||||
存在、已存在规则不重复、searx 精确行仍在且无 diante.me 通配、
|
||||
以及用 BASIC 转 extended 后的实际匹配语义(子域名放行、
|
||||
形似域名与后缀 trick 不放行、control plane 与购物/视频/邮箱
|
||||
域名不放行)。
|
||||
|
||||
## 本轨补充:多标签页、导航白名单、关闭清理
|
||||
|
||||
- 多标签页:`browser_new_page`(开新页并切为当前页)、`browser_list_pages`(未启动时不启动、返回空列表)、`browser_switch_page`(按 index 切页,不存在时报明确错误、当前页不变、旧页 refs 清空)、`browser_close_page`(缺省关当前页,关后自动切上一页;关最后一页报错“use browser_close”)。现有单页方法全部作用于当前页、行为不变。
|
||||
- 导航白名单:`BROWSER_ALLOWED_HOSTS`(逗号列表,前导点后缀匹配,大小写与端口不影响匹配)。未设置时行为不变;设置后在启动 Chromium 之前检查,未命中直接报错且不启动;`file://` 一律放行;`localhost/127.0.0.1` 无特殊待遇、须显式列出(本地开发服务器要自己加 localhost)。注意缺口:白名单只卡 `navigate` 入口,页内链接点击导致的跳转不经过该检查,只能靠 Tinyproxy 代理 403 兜底;接线:worker 在容器启动前把 worker 环境里的 `BROWSER_ALLOWED_HOSTS`(已设置且非空时)写进租户 config 的 `[mcp_servers.playwright.env]`,MCP 进程由此拿到它;未设置时不写该键、白名单不生效(见未验证一节)。
|
||||
- 关闭清理:`browser_close()` 关掉全部页并尽力删除 cwd 下的 `browser-live.png` 与 `browser-live.json`(删失败不报错);自定义文件名的截图文件不在清理范围内。
|
||||
|
||||
## 安全硬线
|
||||
|
||||
- 任何真实密码、cookie、token 绝不写进代码、测试、文档、回执。
|
||||
- 浏览器只在任务沙箱与 egress 白名单内跑;白名单外域名一律
|
||||
代理 403,如实报错。
|
||||
- 截图与下载落任务工作区,不写共享目录。
|
||||
|
||||
## 未验证(诚实边界)
|
||||
|
||||
- filter 未在主机 tinyproxy 上 reload(本轨不部署、不碰主机)。
|
||||
- 真实站点未 live 验证:上述 10 条域名在主机生效后的实际
|
||||
可达性、以及浏览器在真实站点上的端到端回合,仍待部署后
|
||||
在有 egress 的环境里实测;本轨的测试只验证 filter 文件本身
|
||||
的语法与匹配语义,不冒充生产可用。
|
||||
Reference in new issue
Block a user