Skip to content

Stock CDP frameworks (Puppeteer, Playwright, browser-use) cannot attach through the gate #2

Description

@redbotster

Summary

The README says to point a framework's cdp_url at the URL the bridge prints. The popular Node clients cannot complete that connection today: the gate refuses the methods they issue during the attach handshake. This affects Puppeteer, Playwright, and the Playwright-based agents (browser-use, Stagehand).

Not a security bug (the gate is doing its job) — a usability gap between what the README invites and what the allowlist permits.

Repro

Start any bridge (the demo.mjs setup is enough), then point a stock client at bridge.url:

// Puppeteer
await puppeteer.connect({ browserWSEndpoint: bridge.url });
// Playwright
await chromium.connectOverCDP(bridge.url);

Both fail immediately:

Protocol error (Browser.getVersion): method_not_allowed:
  Browser.getVersion is not on the bridge allowlist

Verified with puppeteer-core@23 and playwright-core@1.48 against a real Chromium.

Root cause

Two layers, and the second is the hard one:

  1. Browser.getVersion is not in ALLOWED_METHODS (cdp-policy.ts). Both clients call it first thing on connect.
  2. Even with that added, both drive target discovery with Target.setAutoAttach and Target.setDiscoverTargets, which are in NEVER_ALLOWED by design — the module doc is explicit that re-attaching to Chromium outside the gate makes the no-plaintext invariant moot.

So this is not a one-line allowlist change. Supporting a stock framework client means the proxy answering the discovery handshake itself (synthesizing Browser.getVersion, and presenting only the client's own gated targets in response to auto-attach/discovery) rather than forwarding those commands to Chromium.

Options

  • Document only (done in examples: full register → login → act flow, and a framework-compat note #1's README): tell integrators to speak gated CDP directly, the way examples/agent.mjs does, and use the MCP tools for anything touching a credential. Cheapest, and honest about the current surface.
  • Proxy the handshake: make the proxy respond to Browser.getVersion and the target-discovery commands locally, scoped to the client's assigned context, so Puppeteer/Playwright believe they attached to a normal browser while still only ever seeing gated targets. This is what unlocks browser-use and Stagehand, which are the frameworks people will actually bring. Bigger change, needs its own adversarial coverage (a synthesized discovery reply must not become a way back to an ungated target).

Context

Found while writing the examples in #1. The examples/README.md there documents the current state so integrators do not lose time to it; this issue is to track the underlying decision (document vs. proxy the handshake).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions