Why crossmem bridge does not use Chrome DevTools Protocol
The incident
On a developer workstation, a suspicious process (PID 73079) spawned from a Claude shell snapshot executed the following sequence:
sleep 2400(wait for Chrome to settle)- Connect to
ws://localhost:9222(Chrome DevTools Protocol) Runtime.evaluate→Clerk.session.getToken()to steal the active session tokenPOSTthe stolen token to an external API (teaching.monster)
Root cause: a dev tool had launched Chrome with --remote-debugging-port=9222.
This single flag exposes every open tab, every origin, every cookie on a
localhost WebSocket with zero authentication. Any local process—malicious or
not—can connect and run arbitrary JavaScript in the context of any page the user
has open. CDP is a debugger; it trusts the caller completely.
What crossmem bridge does differently
crossmem bridge is a Manifest V3 Chrome extension that communicates with local
agents over a WebSocket on localhost:7600. The design differs from CDP in
several concrete ways:
- No
--remote-debugging-port. The user’s Chrome launches normally. There is no app-wide debug backdoor to connect to. - User-installed extension with Chrome’s permission UI. The user explicitly grants the extension host permissions. CDP requires no user consent at all; whatever launched Chrome with the flag decides.
- Whitelisted action set. The bridge accepts a fixed set of named actions:
navigate,click,type,extract,screenshot,summarize,tab_info,wait,ping. There is no generic “evaluate arbitrary JS” verb. An attacker who connects to:7600can click buttons and read extracted text, but cannot callClerk.session.getToken()orNetwork.getAllCookies. - Real Chrome profile, no spoofing. The extension runs inside the user’s
actual Chrome profile—no
--user-data-dirto a throwaway directory, no Chrome for Testing with broken Keychain integration.
Threat model comparison
| Attack surface | CDP (:9222) | crossmem bridge (:7600) |
|---|---|---|
| Arbitrary JS on any origin | Runtime.evaluate — yes | No eval verb — no |
| Dump all cookies | Network.getAllCookies — yes | No such action — no |
| Read/modify DOM | Full DOM access | Only via named actions (click, extract) |
| Authentication | None | None (same weakness — see below) |
| User consent | None; whoever launched Chrome decides | Chrome extension install prompt |
The PID 73079 attack required exactly two CDP primitives: Runtime.evaluate and
network access. Neither exists in the crossmem bridge action vocabulary.
What this design does NOT protect against
Honesty matters more than marketing. crossmem bridge has real limitations:
localhost:7600is unauthenticated, same as CDP on:9222. Any local process can connect. The attack surface is smaller (no eval, no cookie dump), but the network posture is identical.chrome.scripting.executeScriptis arbitrary JS under the hood. The bridge currently uses it to implement actions likeextractandclick. If a future action handler passes attacker-controlled input (selectors, payloads) intoexecuteScriptwithout sanitization, the constrained action set becomes a confused deputy.- Supply-chain attack on the extension itself. A malicious MV3 update pushed to the Chrome Web Store bypasses every architectural constraint. The extension IS the trust boundary.
- Planned hardening (not yet implemented):
- Per-request auth token (shared secret between agent and extension)
- Unix domain socket instead of TCP (removes network-reachable surface)
- Strict input validation on action parameters
Takeaway
The lesson from PID 73079 is not “use crossmem bridge instead of CDP.” It is: dev automation tooling should not default to opening an app-wide debug backdoor.
CDP is a debugger protocol. It was designed for DevTools, not for agent orchestration. When you expose it on localhost, you hand every local process— including ones you didn’t launch—full control over every tab in the browser.
crossmem bridge chose a constrained, consent-gated channel: a user-installed extension exposing a fixed action vocabulary over a local WebSocket. This is a design choice that reduces the blast radius of local-process compromise. It is not magic, and it is not complete. But it means PID 73079’s exact attack vector—connect, eval, exfiltrate—does not work.