Skip to content

fix(mcp): validate Host and Origin on CDP relay WebSocket upgrades - #42103

Merged
Yury Semikhatsky (yury-s) merged 7 commits into
microsoft:mainfrom
yury-s:fix-mcp-1694
Aug 4, 2026
Merged

fix(mcp): validate Host and Origin on CDP relay WebSocket upgrades#42103
Yury Semikhatsky (yury-s) merged 7 commits into
microsoft:mainfrom
yury-s:fix-mcp-1694

Conversation

@yury-s

Copy link
Copy Markdown
Member

Summary

  • Reject CDP relay WebSocket upgrades with a non-loopback Host header or an http(s) Origin. Legitimate clients are unaffected: the local Playwright client sends no Origin, the extension's Origin is chrome-extension://.
  • Defense-in-depth hardening only, not an exploitable vulnerability: the relay always binds to localhost and its endpoints use unguessable UUID paths.

Fixes microsoft/playwright-mcp#1694

The relay HTTP server only binds to localhost, but the WebSocket upgrade
accepted any Host header (DNS rebinding) and any Origin. Reject upgrades
with a non-loopback Host or a web page Origin; the legitimate clients are
the local Playwright client (no Origin) and the extension
(chrome-extension:// Origin).

Fixes: microsoft/playwright-mcp#1694
Comment thread packages/playwright-core/src/tools/mcp/cdpRelay.ts Outdated
Extract the Host allowlist check from HttpServer._onRequest into
isAllowedHost(), enforce it on createWebSocket() upgrades, and reuse it
together with the shared loopback host set in the CDP relay.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Replace the raw ws server and custom verifyClient in CDPRelayServer with
WSServer, which now validates the Host allowlist on upgrades in addition
to Origin. Restrict the WSServer origin check to http(s) origins so that
extension connections are allowed.
Rename isValidPathname to isAllowedPathname, require it in the delegate
and define it at all callsites. Drop the unused path parameter of the
PlaywrightWebSocketServer constructor.
Also pass the ws path to the PlaywrightWebSocketServer constructor
instead of listen().
Allow WSServerDelegate.onConnection to return undefined instead of a
no-op connection, align the upgrade 403 status line in HttpServer with
WSServer.
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test results for "tests 1"

10 flaky ⚠️ [chromium-library] › library/video.spec.ts:699 › screencast › should capture full viewport on hidpi `@frozen-time-library-chromium-linux`
⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@realtime-time-library-chromium-linux`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:177 › serviceWorker(), and fromServiceWorker() work `@chromium-ubuntu-22.04-node24`
⚠️ [chromium-library] › library/video.spec.ts:495 › screencast › should capture static page in persistent context Radoslav Kirilov (@smoke) `@chromium-ubuntu-22.04-node24`
⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@chromium-ubuntu-22.04-node20`
⚠️ [chromium-library] › library/video.spec.ts:356 › screencast › should work for popups `@chromium-ubuntu-22.04-node22`
⚠️ [firefox-page] › page/page-emulate-media.spec.ts:144 › should keep reduced motion and color emulation after reload `@firefox-ubuntu-22.04-node20`
⚠️ [firefox-page] › page/page-event-request.spec.ts:181 › should return response body when Cross-Origin-Opener-Policy is set `@firefox-ubuntu-22.04-node20`
⚠️ [playwright-test] › ui-mode-trace.spec.ts:827 › should update state on subsequent run `@macos-latest-node22`
⚠️ [playwright-test] › ui-mode-test-output.spec.ts:118 › should collapse repeated console messages for test `@ubuntu-latest-node20`

51189 passed, 1188 skipped


Merge workflow run.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test results for "MCP"

1 failed
❌ [firefox] › mcp/annotate.spec.ts:446 › should switch screencast to -s session on show --annotate @mcp-windows-latest-firefox

7971 passed, 1284 skipped


Merge workflow run.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Hi, I'm the Playwright bot and I took a first look at the CI failures here.

🟢 The one failure is a known flake — this PR is clear

[firefox] › mcp/annotate.spec.ts:446 › should switch screencast to -s session on show --annotate (mcp-windows-latest-firefox) is a pre-existing flake, not something this PR broke. This PR only hardens Host/Origin validation on the CDP relay WebSocket (CDPRelayServer, wsServer/httpServer), and the plain MCP annotate flow never touches the extension relay.

Details

Overall: the diff is confined to the extension CDP relay and the shared WS/HTTP host-origin check. The failing test exercises the ordinary MCP annotate screencast path, which doesn't instantiate the relay, so there's no path from the change to this failure. The tests 1 report on the same commit had only flaky tests (no failures), so nothing to triage there.

Pre-existing flake / infra

Triaged by the Playwright bot - agent run

Triaged by the Playwright bot - agent run

@yury-s
Yury Semikhatsky (yury-s) merged commit 40d39ac into microsoft:main Aug 4, 2026
50 of 51 checks passed
@yury-s
Yury Semikhatsky (yury-s) deleted the fix-mcp-1694 branch August 4, 2026 16:37
Yury Semikhatsky (yury-s) added a commit to microsoft/playwright-mcp that referenced this pull request Aug 6, 2026
## What's New

### Tool Improvements

- **`browser_take_screenshot` — WebP format** — New `type` option to
pick the image format (`png`, `jpeg` or `webp`); when unset, the format
is inferred from the filename extension
([#41152](microsoft/playwright#41152))
- **`--codegen` — more languages** — Code generation now supports
`python`, `java` and `csharp` in addition to `typescript`
([#42106](microsoft/playwright#42106))
- **Configurable settle delay** — New `--timeout-settle` flag (and
`timeouts.settle` config option) to control how long to wait after each
action for triggered work (navigations, requests) to settle, defaults to
500ms ([#41924](microsoft/playwright#41924))

## Fixes

- Aria snapshot capture is skipped when the response discards it
([#41923](microsoft/playwright#41923))
- New `--snapshot-boxes` flag (and `snapshot.boxes` config option) to
enable bounding boxes in snapshots globally — previously this was only
available per command
([#42102](microsoft/playwright#42102))
- Reconnect to the browser after a disconnect
([#41966](microsoft/playwright#41966))
- Report malformed JSON config instead of silently falling back to INI
parsing ([#41909](microsoft/playwright#41909))
- Clear the dialog modal state when a dialog is closed out of band
([#42032](microsoft/playwright#42032))
- Detect downloads via the download event instead of guessing from
`net::ERR_ABORTED` navigation errors
([#41933](microsoft/playwright#41933))
- Surface the missing executable path when a browser isn't installed
([#41941](microsoft/playwright#41941))
- Escape user input in codegen output
([#41962](microsoft/playwright#41962))
- Removed the stale `--output-mode` CLI option
([#41833](microsoft/playwright#41833))

### Browser Extension

- Validate `Host` and `Origin` headers on CDP relay WebSocket upgrades
([#42103](microsoft/playwright#42103))
- Launch the Chrome profile that has the extension installed
([#41939](microsoft/playwright#41939))
- Pass `noDefaults` for extension CDP connections
([#42119](microsoft/playwright#42119))
- Mention `PLAYWRIGHT_MCP_EXECUTABLE_PATH` in the error shown when the
browser executable is not found
([#42122](microsoft/playwright#42122))
- Removed support for extension protocol v1
([#41857](microsoft/playwright#41857))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security Architecture Review] Playwright MCP — Defense Analysis & Improvement Suggestions

2 participants