Skip to content

security(browser): the Playwright container follows redirects and re-resolves DNS after the one-time SSRF check #16562

Description

@mrveiss

Problem

Every caller-URL entry point into the Playwright browser container checks the URL once, in the Python process (is_public_url_async: send_to_browser_vm, and capture_screenshot after #16544). The navigation itself runs in autobot-browser-worker/playwright-server.js (page.goto(...)), which resolves DNS again and follows redirects with no re-check. A public URL that 302s to an internal address, or a hostname that rebinds between check and fetch, reaches internal services from the container.

The other fetch paths already close this: research_browser_manager.py re-checks after navigation (#13018), and content_reach/web_fetch pin the resolved IP through redirects (#13017, #13019). The container path (services/playwright_service.py, browser_backends/container.py, autobot_shared/browser/registry.py::_guard_url) has neither. Found in #16544's review. The separately unguarded POST /playwright/navigate is tracked in #16552.

Acceptance criteria

  • The browser worker refuses to navigate, or to follow a redirect, to a non-public address: request interception re-checks every navigation and redirect hop against the same policy as url_safety.
  • A test covers a public URL that redirects to a private, loopback or metadata address and is refused.

Activity

  1. added this to the v0.10.0 milestone on Sep 12, 2026
  2. self-assigned this
    on Sep 12, 2026
  3. mrveiss commented on Sep 30, 2026

    @mrveiss
    OwnerAuthor

    Defect class, two known instances — cross-referencing so the shape is visible from both sides. #17827 is the same defect in a different subsystem: the LLM's HTTP tool checks a domain allowlist on the initial URL, then issues the request on a raw tracked_session() with allow_redirects unset, so a 302 from an allowlisted host escapes the check. Same shape as this issue's check once, then re-resolve/redirect past it.

    Remedies differ (container network policy here, routing a call site onto the shared client there), so they stay separate issues — but a third sighting of this shape should join the class rather than open a new thread. autobot_shared/security/ssrf_guard.py:230 pinned_request_with_redirects is the in-repo primitive that already handles it correctly. Enforcement of the class as a whole is #17576.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions