- 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.
68 lines
3.0 KiB
Plaintext
68 lines
3.0 KiB
Plaintext
# Tinyproxy Filter: ONE regex per line, matched against the bare
|
|
# domain for CONNECT requests (the log shows e.g. filtered domain
|
|
# "xcmapi.org" -- no scheme, no :port suffix).
|
|
#
|
|
# CRITICAL: Tinyproxy compiles these as POSIX BASIC regex, not
|
|
# extended regex. Extended syntax (+ ? ( ) |) does NOT work here:
|
|
# an extended-style anchored pattern (^(.+\.)?xcmapi\.org$) was
|
|
# verified on 2026-10-26 to deny even xcmapi.org itself, while the
|
|
# basic-syntax equivalent below allows it. In basic regex, groups
|
|
# are \(...\), repetition of a group is \(...\)*, and + ? ( ) are
|
|
# literal characters. Do NOT "modernize" these patterns.
|
|
#
|
|
# Each entry is anchored (^...$) to the domain and its subdomains
|
|
# only: a bare substring "github.com" would also match
|
|
# "evilgithub.com". The \(.*\.\)* prefix matches zero or more
|
|
# "label." segments, so xcmapi.org and api.xcmapi.org match, while
|
|
# notxcmapi.org and xcmapi.org.evil.com do not.
|
|
^\(.*\.\)*xcmapi\.org$
|
|
^\(.*\.\)*github\.com$
|
|
^\(.*\.\)*githubusercontent\.com$
|
|
^\(.*\.\)*pypi\.org$
|
|
^\(.*\.\)*pythonhosted\.org$
|
|
^\(.*\.\)*npmjs\.org$
|
|
# Web search MCP v1 (see docs/web-search-mcp.md). Each line is the
|
|
# API host's domain, subdomains included, in the same BASIC form.
|
|
# searx.diante.me is exact-only on purpose: a \(.*\.\)*diante.me$
|
|
# line would also let tasks reach api.diante.me (the control plane).
|
|
^\(.*\.\)*perplexity\.ai$
|
|
^\(.*\.\)*exa\.ai$
|
|
^\(.*\.\)*tavily\.com$
|
|
^\(.*\.\)*search\.brave\.com$
|
|
^\(.*\.\)*duckduckgo\.com$
|
|
^searx\.diante\.me$
|
|
# live-info primary sources (2026-10-30, user-approved): time
|
|
# APIs + weather primary sources only, so web_fetch can verify
|
|
# numbers instead of failing 403 Filtered. Deliberately NOT
|
|
# included: time.is / time.org.cn / time.ms (commercial or
|
|
# secondary time sites; worldtimeapi + timeapi cover time),
|
|
# and no general news/social domains -- the whitelist stays
|
|
# fail-closed, these are the only fetch destinations live-info
|
|
# needs. Each line is domain + subdomains, same BASIC form.
|
|
^\(.*\.\)*worldtimeapi\.org$
|
|
^\(.*\.\)*timeapi\.io$
|
|
^\(.*\.\)*open-meteo\.com$
|
|
^\(.*\.\)*weather\.com\.cn$
|
|
^\(.*\.\)*meteoblue\.com$
|
|
^\(.*\.\)*accuweather\.com$
|
|
# Platform browser programming sources (2026-11-01, feat-platform-browser).
|
|
# Only programming sources: official docs, GitHub, package registries,
|
|
# Stack Overflow, CVE/NVD. Deliberately NOT included: shopping, social,
|
|
# mail, general news, video sites. Same BASIC form as above.
|
|
# Already present above, not duplicated here: github.com, pypi.org,
|
|
# npmjs.org (plus pythonhosted.org for pip). Notes on coverage:
|
|
# docs.python.org is covered by the python.org line (subdomain rule);
|
|
# docs.rust-lang.org by rust-lang.org; nodejs.org docs live on
|
|
# nodejs.org itself. developer.mozilla.org is scoped to that host
|
|
# only, NOT a mozilla.org wildcard, so no other Mozilla host opens.
|
|
^\(.*\.\)*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$
|