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:
host committed 2026-11-01 08:34:13 +00:00
1 parent 27f48fe1ed
commit da7a9950fb
8 files changed
+1050 -12

No files matched your search

+35
View File
@@ -60,3 +60,38 @@ snapshot_refs 再点);`browser_mouse_move` / `browser_key_press` /
5. 不要用浏览器做静态抓取能做的事;不要用 evaluate 绕过
click / fill 的正常路径去改页面状态,除非 snapshot 已证明
正常路径走不通。
## 多页与域名白名单(本期新增)
多页工具:`browser_new_page`(开新页并切到它,每次
new_page 都是全新 Playwright context,cookie 与存储天然
隔离,适合同时登录两个账号或对照两个页面)、
`browser_list_pages`(列出全部页:index、URL、标题、
哪一页是当前页;浏览器未启动时不启动,只返回空列表)、
`browser_switch_page`(按 index 切到指定页,之后
navigate / snapshot / click / fill / screenshot 都针对该页;
切到不存在的 index 会明确报错,不会崩掉 MCP,当前页不变;
切换后旧页的 snapshot_refs 全部失效,要重新 snapshot_refs)、
`browser_close_page`(关一页,缺省关当前页;关当前页后
自动切到上一页;关最后一页会明确报错,改用
`browser_close` 关整个浏览器)。
域名白名单:环境变量 `BROWSER_ALLOWED_HOSTS`(逗号列表,
如 `example.com,.python.org`)。未设置时行为完全不变,
只靠 egress 代理白名单;设置后 navigate 在启动浏览器之前
就检查:`file://` 一律放行,`http(s)` 的 host 必须命中列表
(前导点条目做后缀匹配:`.python.org` 同时放行
`python.org` 与 `docs.python.org`,不放行
`notpython.org`),大小写与端口不影响匹配,未命中直接
报错且不启动 Chromium。`localhost` / `127.0.0.1` 没有
特殊待遇:本地开发服务器要自己把它列进变量,否则会被挡。
清理:`browser_close` 会关掉全部页,并尽力删除工作区里的
`browser-live.png` 与 `browser-live.json`(删不掉也不报错),
所以 close 之后不要再拿旧 live 文件当“当前画面”汇报;
单个截图(`browser_screenshot` 的 `shot-*.png`)不受影响。
不在本轨范围:下载没有落盘清单(下载仍只落工作区文件,
无统一清单可查);没有持久 profile(每次 MCP 进程结束
cookie 即丢);没有登录保险库(账号密码仍不进浏览器工具)。
需要这些时不要假装已有,先如实说明缺口。