Skip to content

security: harden pipe auth, CDP proxy, markdown XSS, and webview#45

Open
foschmitz wants to merge 1 commit into
amirlehmam:masterfrom
foschmitz:security/harden-ipc-cdp-markdown
Open

security: harden pipe auth, CDP proxy, markdown XSS, and webview#45
foschmitz wants to merge 1 commit into
amirlehmam:masterfrom
foschmitz:security/harden-ipc-cdp-markdown

Conversation

@foschmitz

Copy link
Copy Markdown

Summary

Addresses the critical/high findings from a security review of the IPC pipe, CDP proxy, markdown rendering, and webview surfaces. No behavior change for normal use; the only externally-visible effect is that privileged pipe (V2) calls and direct CDP WebSocket connections from web origins are now rejected.

Findings fixed

#1 — Unauthenticated local RCE via named pipe (Critical)

Any local process could call privileged V2 methods (agent.spawn, browser.eval, markdown.load_file, workspace/pane mutation). Now:

  • Per-instance token generated with crypto.randomBytes(32), persisted 0600 under the instance APPDATA dir, compared with crypto.timingSafeEqual.
  • Privileged V2 methods require the token; an allowlist of read-only/telemetry methods (system.identify, system.capabilities, hook.event, agent.activity) stays open so detection, shell hooks, and agent telemetry keep working — and any new method is locked by default.
  • Token injected into spawned shells as WMUX_PIPE_TOKEN; the wmux CLI and hook client read it from env or the token file.
  • V1 plaintext telemetry (shell integration) is unchanged (low-risk, no code-exec surface).

#2 — CDP proxy reachable from web origins (Critical)

The CDP proxy exposes Runtime.evaluate (arbitrary JS in the webview). Loopback binding alone is insufficient:

  • DNS-rebinding: HTTP endpoints + WS upgrade now require a loopback-literal Host (403 / verifyClient reject).
  • Direct WS from a browser page: WebSockets bypass CORS, so a malicious page could open ws://127.0.0.1:9222 (valid Host) and drive the debugger. We now also validate Origin on the WS upgrade and reject any web/file origin. Absent Origin is allowed so chrome-devtools-mcp/puppeteer-core/native ws clients still connect (mirrors Chrome's --remote-allow-origins).

#3 — Markdown XSS (High)

Markdown can arrive from untrusted CLI/pipe/agent/file sources and was injected via dangerouslySetInnerHTML in the privileged renderer. Now sanitized with DOMPurify (forbids javascript: URIs, event handlers, style/form controls).

markdown.load_file path abuse

Even with a valid token, the loader now enforces an extension allowlist (.md/.markdown/.mdx/.txt/.text/.rst), a 5MB cap, and an isFile check — so it can't be used to slurp secrets (id_rsa, .env, …) into the viewer.

#9 — Webview hardening (Low/defense-in-depth)

Central web-contents-created handler: strips preload/nodeIntegration from attached webviews (enforces contextIsolation), routes window.open to the OS browser, and blocks the main window from navigating off its own UI (localhost/file://).

Tests

  • tests/unit/pipe-server.test.ts: token enforcement (reject without/with wrong token, accept correct token, public methods open, V1 still works) — 9 tests.
  • tests/unit/cdp-proxy.test.ts (new): Host allowlist (loopback/IPv6/rebind/non-loopback) + Origin guard (absent/devtools allowed, web origins rejected) — 9 tests.
  • Full suite: 106 passing. (pty-manager.test.ts fails only because the node-pty native module isn't built on this Linux dev box — pre-existing/environmental, fails identically on master.)

Notes for reviewer

  • CDP uses Host+Origin validation rather than a token because the external chrome-devtools-mcp connects via --browserUrl=http://127.0.0.1:9222 (set in claude-context.ts) and can't send a custom token.
  • The pipe token raises the bar from "any local code can drive the pipe" to "only processes running as this user (who can already read the user's files) can" — an honest, meaningful reduction for a single-user desktop app.

Addresses the critical/high findings from the security review:

- Pipe auth (amirlehmam#1): privileged V2 methods now require a per-instance token
  (crypto.randomBytes, 0600 file in APPDATA, timing-safe compare). Public
  telemetry methods stay open via an allowlist so detection/hooks keep
  working. Token injected into spawned shells as WMUX_PIPE_TOKEN; CLI and
  hook clients read it from env or the token file.
- CDP proxy (amirlehmam#2): reject DNS-rebinding via a loopback-only Host allowlist
  (HTTP 403 + WS verifyClient) AND a browser-Origin guard on the WS upgrade
  (WebSockets bypass CORS, so a passing Host alone lets a malicious page
  open ws://127.0.0.1:9222 and reach Runtime.evaluate). Absent Origin is
  allowed so chrome-devtools-mcp/puppeteer/native ws clients still connect.
- Markdown XSS (amirlehmam#3): sanitize marked output with DOMPurify before
  dangerouslySetInnerHTML in the privileged renderer.
- markdown.load_file: extension allowlist + 5MB cap + isFile check so a
  pipe caller can't slurp arbitrary secrets into the viewer.
- Webview hardening (amirlehmam#9): central web-contents-created handler strips
  preload/nodeIntegration from webviews, routes window.open to the OS
  browser, and blocks the main window from navigating off its own UI.

Tests: pipe-server token auth (9) + new cdp-proxy host/origin guard (9).
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.

1 participant