Skip to content

[P1][security] live-server /live.js leaks bearer token via unauthenticated CORS-readable endpoint #304

Description

@claudio-arklay

Version: impeccable@3.1.0 (also on HEAD)

File: .claude/skills/impeccable/scripts/live-server.mjs lines 388-414

return (req, res) => {
  const url = new URL(req.url, `http://localhost:${state.port}`);
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
  const p = url.pathname;
  if (p === '/live.js') {
    // ...
    const body = assembleLiveBrowserScript({
      token: state.token,
      port: state.port,
      // ...
    });

Issue. Every other route under the live server (/source, /events, manual-edit endpoints) checks token === state.token and returns 401 on mismatch. But /live.js returns a JavaScript bundle with state.token embedded inline, without any auth check, and the handler sets Access-Control-Allow-Origin: * for all responses.

Impact. While the live server is running on the user's machine, any web page (or local process — a browser tab visiting an attacker-controlled site, a stray service worker, an iframe, etc.) that knows or probes the default localhost port can fetch /live.js, regex out the embedded token, and then make authenticated requests against /source, /events, and the manual-edit routes against the user's project. The token-gated authorization on the rest of the server is undermined.

The default port + the * CORS policy + the unauthenticated token-bearing script combine to make this a real local-network / local-browser attack surface, not a theoretical one. Mitigations like "localhost-only listen" don't help: the browser running on the same machine that hosts the live server is exactly the attack vector.

Fix options:

  1. Token-gate /live.js too: require ?token=... on the script URL itself (the live mode's bootstrap can include the token in the script-tag src). Cleanest mitigation.
  2. Drop the token from the served script: serve a static-ish bootstrap that POSTs to a server-side handshake to acquire a per-session token. Larger refactor.
  3. Tighten CORS: replace Access-Control-Allow-Origin: * with the live mode's known origin (the page being driven). Doesn't fix the same-origin case but reduces blast radius.

Surfaced by codex review against a real install. Wanted to file it so future installs aren't silently exposed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions