Skip to content

fix(addon): harden token bridge and deliver the passphrase to the add-on (#953)#975

Open
aaspinwall wants to merge 1 commit into
mainfrom
fix/token-bridge-passphrase-hardening
Open

fix(addon): harden token bridge and deliver the passphrase to the add-on (#953)#975
aaspinwall wants to merge 1 commit into
mainfrom
fix/token-bridge-passphrase-hardening

Conversation

@aaspinwall

@aaspinwall aaspinwall commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What changed?

Hardening of the add-on "token bridge" — the content script that relays messages from the Send web app to the privileged background (OIDC tokens, the encryption passphrase, sign-in/out).

1. Bridge security (closes #953):

  • token-bridge.js forwarded window.postMessages straight to the background. It ran on <all_urls> with its sender checks commented out. It now rejects messages unless e.source === window and e.origin === window.location.origin.
  • manifest.json content_scripts.matches scoped to the app origins (send.tb.pro, send-stage.tb.pro, localhost); build.sh strips http://localhost/* from production builds so a released add-on never trusts a localhost page. Removed a dead debug block.

2. Passphrase delivery to popup/background (see #712):

  • Only the management page pulled the bridged passphrase into the keychain, so an upload from the popup/background could find none. Extracted pullBridgedPassphrase() and call it inside restoreKeysUsingLocalStorage (which the popup and background already call), so neither depends on the management page having run the transfer.

AI disclosure: implemented with Claude Code (an AI agent) under my direction and review; reviewed by independent agent passes.

Why?

The bridge relayed secrets to the privileged background but trusted any page/frame and injected everywhere (#953), and the shared passphrase didn't reach the upload popup (#712).

Scope note

Passphrase encryption at rest was originally bundled here but has been split into its own PR — it's incompatible with the dev e2e's cross-session storageState reuse (which can't carry the IndexedDB key) and needs a dedicated e2e rework. This PR contains only the bridge hardening + delivery, which don't change the passphrase storage format.

Verification

Frontend unit suite green; tsc clean for changed files; bridge origin-scoping + pullBridgedPassphrase covered by unit tests and real-browser checks. e2e is unaffected by these changes (no storage-format change).

Applicable Issues

Closes #953
Related: #712

@aaspinwall aaspinwall changed the title WIP: fix(addon): harden token bridge + reliably share passphrase with add-on (#953) WIP: fix(addon): harden token bridge, deliver + encrypt the passphrase (#953) Jul 8, 2026
…d-on

Security: the token-bridge content script forwards window.postMessages straight
to the privileged background (OIDC tokens, encryption passphrase, sign-in/out).
It ran on <all_urls> with its sender checks commented out. It now validates
e.source === window and e.origin === window.location.origin, and
content_scripts.matches is scoped to the Send app origins. build.sh strips the
http://localhost/* match for production builds so a released add-on never
trusts a localhost page. Also removes a dead debug "visual cue" block.

Functional: only the management page (useSendConfig) pulled the bridged
passphrase from browser.storage.local into the keychain, so a key restore
driven from the popup or background could find no passphrase. Extract
pullBridgedPassphrase() and call it inside restoreKeysUsingLocalStorage — which
both the popup (PopupView) and background (initCloudFile) already call — so
neither depends on the management page having run the transfer first.
useSendConfig now delegates to the same helper. Adds a unit test for it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aaspinwall aaspinwall force-pushed the fix/token-bridge-passphrase-hardening branch from b4b38fa to 763f831 Compare July 9, 2026 17:13
@aaspinwall aaspinwall marked this pull request as ready for review July 9, 2026 17:29
@aaspinwall aaspinwall changed the title WIP: fix(addon): harden token bridge, deliver + encrypt the passphrase (#953) fix(addon): harden token bridge, deliver + encrypt the passphrase (#953) Jul 9, 2026
@aaspinwall aaspinwall force-pushed the fix/token-bridge-passphrase-hardening branch from f100b75 to fe60f63 Compare July 9, 2026 19:08
@aaspinwall aaspinwall changed the title fix(addon): harden token bridge, deliver + encrypt the passphrase (#953) fix(addon): harden token bridge and deliver the passphrase to the add-on (#953) Jul 9, 2026
@aaspinwall

Copy link
Copy Markdown
Collaborator Author

CodeQL note (for reviewers): the one red CodeQL check flags js/clear-text-storage-of-sensitive-data (high) at packages/send/frontend/src/lib/storage/LocalStorage.ts:24 — the passphrase stored in localStorage as clear text.

Suggest merging on the green functional checks and tracking the cleartext alert via #982 rather than blocking here.

@aaspinwall aaspinwall requested a review from radishmouse July 9, 2026 19:26
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.

Harden token-bridge: validate postMessage origin/source before relaying to background

1 participant