Skip to content

fix(settings): validate uid before using it as an accounts key - #21084

Open
vbudhram wants to merge 1 commit into
mainfrom
fxa-12450
Open

fix(settings): validate uid before using it as an accounts key#21084
vbudhram wants to merge 1 commit into
mainfrom
fxa-12450

Conversation

@vbudhram

Copy link
Copy Markdown
Contributor

Because

  • A code scan raised a prototype pollution warning on legacyLocalStorageAccount.email = email in Account.ts. That line is not the sink. Its key is a literal, so it can only ever write a property called email. It is where the tainted value enters the object.
  • The writes that matter are one call deeper, in currentAccount() in cache.ts. all is the accounts object parsed from localStorage. uid is only asserted to be a hexstring (as hexstring), never checked at run time.
  • The URL supplies one of those uids. With ?uid=constructor, all['constructor'] is truthy through the prototype chain, so constructor becomes the stored account uid. currentAccount() then returns the global Object, and the next call to sessionToken(newToken) writes the session token onto it.

This pull request

  • Adds an isValidUid guard to cache.ts. It tests a uid against /^[0-9a-f]{32}$/.
  • Applies the guard to the uid query parameter that currentAccount() reads from the URL.
  • Makes currentAccount() return undefined and write nothing when the uid of the given account fails the check. That keeps the existing StoredAccountData | undefined return type, so no caller changes.
  • Adds tests to cache.test.ts for the rejected uids, for the round trip of a valid uid, and for the ?uid=constructor chain above.

Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-12450

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: currentAccount() in packages/fxa-settings/src/lib/cache.ts.
  • Suggested review order: the guard in cache.ts, then cache.test.ts.
  • Risky or complex parts: the guard drops an account whose uid is not 32 lowercase hex characters. Server uids always have that shape, so a real account is not affected. The rejection is silent, because no caller reads the return value of currentAccount(account).

Screenshots (Optional)

No user interface change.

Other information (Optional)

  • The broader alternative is to build all with Object.create(null). That makes the whole class of key impossible instead of rejecting one shape. Ask for it if you prefer that trade.
  • Out of scope, and still unguarded: the read paths (getAccountByUid, the final return all[uid]), and the sibling writers of the same accounts key in storage-utils.ts and account-storage.ts. Say the word and I will file a follow up ticket.
  • Ran npx jest src/lib/cache.test.ts (43 passed, 0 failed) and npx nx lint fxa-settings (0 errors).

@vbudhram
vbudhram requested a review from a team as a code owner August 20, 2026 16:59
Copilot AI balanced review requested due to automatic review settings August 20, 2026 16:59
@vbudhram vbudhram added the auto label Aug 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds runtime UID validation to protect settings account-cache keys from prototype-chain access.

Changes:

  • Validates account and URL UIDs as 32-character lowercase hex strings.
  • Adds regression coverage for malicious UIDs and session-token pollution.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/fxa-settings/src/lib/cache.ts Adds UID validation around account-cache access.
packages/fxa-settings/src/lib/cache.test.ts Tests valid, invalid, and malicious UID behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +92 to +94
if (!isValidUid(account.uid)) {
return undefined;
}
## Because

- A uid is typed `hexstring`. That is a TypeScript assertion, not a runtime check. The `?uid=` query parameter and the stored `currentAccountUid` both reach `cache.ts` unvalidated.
- `cache.ts` uses the uid as a key into the accounts object, a plain object parsed from localStorage. `?uid=constructor` passes the old `all[forceUid]` truthiness test, because `all['constructor']` resolves to `Object`. The value gets persisted. A later `sessionToken(newToken)` call then writes `Object.prototype.sessionToken`.
- The scan reported this against `Account.ts`, on `legacyLocalStorageAccount.email = email`. That is where the tainted value enters the object, but the key there is a literal, so it cannot pollute anything. The sink is one call deeper.

## This pull request

- Adds `isValidUid()` to `cache.ts`. It checks for 32 hex characters, matching `DEVICE_ID_REGEX` in `fxa-shared`.
- Guards the writes: the `?uid=` query parameter, and `account.uid` before it becomes a key.
- Guards the reads: `currentAccount()`, `getAccountByUid()`, and the delete in `clearSignedInAccountUid()`.
- Adds `currentAccount` and `getAccountByUid` tests to `cache.test.ts`.

## Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-12450
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants