Skip to content

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

Merged
aaspinwall merged 1 commit into
mainfrom
fix/token-bridge-passphrase-hardening
Jul 14, 2026
Merged

fix(addon): harden token bridge and deliver the passphrase to the add-on (#953)#975
aaspinwall merged 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.

  • This is pre-existing on main (open alert Validate that send was migrated correctly #2, created 2025-10-10); it is not introduced by this PR. It surfaces as "new" only because this PR's pullBridgedPassphrase → storePassPhrase adds another dataflow into the same, unchanged cleartext sink.
  • The actual fix is fix(send): encrypt the Send passphrase at rest (#549) #982 (encrypt the passphrase at rest), which removes the cleartext sink at storePassPhrase. fix(send): encrypt the Send passphrase at rest (#549) #982 is drafted but blocked on a dev-e2e rework (Playwright storageState can't carry the IndexedDB key), so the encryption was descoped from this PR to keep the reviewed bridge hardening + delivery mergeable with a green e2e.
  • All e2e (end-to-end-tests, end-to-end-bucket-tests), frontend, integration, and the CodeQL Analyze jobs pass on this PR.

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

@radishmouse radishmouse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed that new code paths are used in system add-on

@aaspinwall
aaspinwall merged commit 6b8fd85 into main Jul 14, 2026
19 of 20 checks passed
@aaspinwall
aaspinwall deleted the fix/token-bridge-passphrase-hardening branch July 14, 2026 00: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

2 participants