Skip to content

[Security follow-up C-1] Move MCP pairing token from chrome.storage.local to .sessionΒ #42

Description

@hoainho

🌟 Before claiming this issue

Two quick steps before you open a PR:

  1. ⭐ Star the repository β€” low-friction signal that you'll follow through
  2. πŸ’¬ Comment I'll take this (or similar) below β€” prevents two contributors racing on the same issue

Full policy: CONTRIBUTING.md β†’ How to claim. If a claim is older than 7 days with no PR, you can reclaim it politely.


Tracks follow-up #1 from PR #17 security review. Blocked on mcp-server-v1 M3 (~Jul 28, 2026).

What

MCPPairingPanel currently stores the pairing token in chrome.storage.local. This is too long-lived β€” tokens persist across browser restarts and can be exfiltrated via any extension with storage permission. Use chrome.storage.session instead (cleared on browser restart, isolated per-tab).

Why

chrome.storage.local is persistent + readable by any extension with the storage permission. The MCP pairing token grants control of the debugger β€” same trust level as a session cookie. It should:

  1. Survive panel navigation within the same browser session
  2. Be cleared on browser restart
  3. Not be readable by other extensions

chrome.storage.session satisfies all 3. It's been available since MV3 launched.

Acceptance criteria

  • All token reads in src/panel/components/MCPPairingPanel.tsx go through chrome.storage.session (not chrome.storage.local)
  • Migration path documented: existing chrome.storage.local entries for old keys are read once at panel load, copied to session, then deleted from local
  • Add a unit test verifying the token does not appear in chrome.storage.local after the migration runs
  • CHANGELOG entry noting the storage migration

Implementation hint

// In MCPPairingPanel.tsx
const TOKEN_KEY = 'mcp_pairing_token_v1';

async function saveToken(token: string) {
  await chrome.storage.session.set({ [TOKEN_KEY]: token });
}

async function readToken(): Promise<string | null> {
  const { [TOKEN_KEY]: token } = await chrome.storage.session.get(TOKEN_KEY);
  return token ?? null;
}

// One-time migration helper
async function migrateLegacyToken() {
  const { [TOKEN_KEY]: legacy } = await chrome.storage.local.get(TOKEN_KEY);
  if (legacy) {
    await chrome.storage.session.set({ [TOKEN_KEY]: legacy });
    await chrome.storage.local.remove(TOKEN_KEY);
  }
}

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededsecuritySecurity-related (see SECURITY.md for vulnerabilities)status:in-progressImplementation underway

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions