molecule-core/plugins
Hongming Wang 8cc325eb3b fix(security): token-auth on cdp-proxy to prevent LAN exposure (#293)
HIGH finding from security-auditor on PR #291 (merged tick-37). The
cdp-proxy bound to 0.0.0.0:9223 with no authentication, exposing
Chrome DevTools Protocol — full remote control of any tab, including
cookie/localStorage exfiltration — to anyone on the same WiFi/LAN.

Root cause: Docker Desktop on macOS routes host.docker.internal
through the VM network interface, not loopback. Binding to 127.0.0.1
would break the primary use case (containers reaching the host
Chrome). The design trade was "bind wide for reachability, accept LAN
exposure" — #293 makes that trade unacceptable.

Fix: bearer token auth on every HTTP + WebSocket request. The proxy
REFUSES TO START without a token — no unauth mode.

Three-file change:

1. cdp-proxy.cjs
   - Read token from CDP_PROXY_TOKEN env OR ~/.molecule-cdp-proxy-token
   - Fail loudly if neither is set (exit 1 with install-host-bridge.sh
     pointer)
   - Validate X-CDP-Proxy-Token header via crypto.timingSafeEqual on
     every HTTP request AND every WS upgrade
   - Strip the header before forwarding to Chrome (defense in depth —
     token never leaks into Chrome's request log)

2. install-host-bridge.sh
   - New ensure_token() function generates a 64-char hex token via
     openssl rand -hex 32 (fallback to /dev/urandom). Written to
     ~/.molecule-cdp-proxy-token with chmod 600.
   - macOS: token injected into launchd plist EnvironmentVariables
   - Linux: written to ~/.molecule-cdp-proxy.env (chmod 600) and
     referenced via systemd EnvironmentFile — avoids embedding the
     token in the often world-readable unit file
   - Install reuses existing token if present (16+ chars); uninstall
     preserves token file so a reinstall keeps the same token
   - Verify command now includes the token header
   - Documents container-side bind-mount pattern
     (-v ~/.molecule-cdp-proxy-token:/run/secrets/cdp-proxy-token:ro)

3. lib/connect.js
   - New loadProxyToken() with precedence: env var >
     /run/secrets/cdp-proxy-token > ~/.molecule-cdp-proxy-token
   - Attaches X-CDP-Proxy-Token header on both /json/version probe +
     final puppeteer.connect() call via headers: {} option
     (puppeteer-core v21+ supports this natively)
   - Host-direct fallback (CDP port 9222 on loopback) unchanged —
     Chrome's own port is loopback-only so it doesn't need the token

Attack surface now:
  - LAN attacker must also steal the token file from the user's home
    directory (requires shell access) OR the env var (requires
    launchd/systemd process inspection as the same user) — reduces to
    local-privilege-escalation territory
  - Containers on the same Docker network still have access (they
    mount the token by design) — intentional, any workspace-template
    install already runs inside the platform's trust boundary

Not fixing in this PR:
  - Rate limiting on /json/version (low priority — probe-and-mine is
    expensive even without)
  - IP allowlist on top of token auth (diminishing returns)
  - Rotating the token periodically (user can rm ~/.molecule-cdp-proxy-token
    and reinstall)

Closes #293.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:00:02 -07:00
..
browser-automation fix(security): token-auth on cdp-proxy to prevent LAN exposure (#293) 2026-04-15 18:00:02 -07:00
ecc initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
molecule-audit feat(plugin): split compliance-posture into 3 plugins (#256) 2026-04-15 14:15:25 -07:00
molecule-audit-trail feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-careful-bash feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-compliance feat(plugin): split compliance-posture into 3 plugins (#256) 2026-04-15 14:15:25 -07:00
molecule-dev initial commit — Molecule AI platform 2026-04-13 11:55:37 -07:00
molecule-freeze-scope feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-hitl feat(plugin): molecule-hitl — opt-in HITL gates (#257) 2026-04-15 14:03:19 -07:00
molecule-prompt-watchdog feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-security-scan feat(plugin): split compliance-posture into 3 plugins (#256) 2026-04-15 14:15:25 -07:00
molecule-session-context feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-skill-code-review feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-skill-cron-learnings feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-skill-cross-vendor-review feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-skill-llm-judge feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-skill-update-docs feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-workflow-retro feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
molecule-workflow-triage feat(plugins): split guardrails into 12 modular plugins 2026-04-14 12:20:04 -07:00
superpowers chore: structural cleanup — dead dirs, moves, gitignore 2026-04-13 14:06:52 -07:00