Skip to content

fix(profile-sync-controller): scope storage-key & snap-signature caches by entropySourceId#8948

Merged
mathieuartu merged 7 commits into
mainfrom
fix/profile-sync-srp-data-leak-pairing
Jun 3, 2026
Merged

fix(profile-sync-controller): scope storage-key & snap-signature caches by entropySourceId#8948
mathieuartu merged 7 commits into
mainfrom
fix/profile-sync-srp-data-leak-pairing

Conversation

@mathieuartu
Copy link
Copy Markdown
Contributor

@mathieuartu mathieuartu commented Jun 1, 2026

Explanation

References

Related to: https://consensyssoftware.atlassian.net/browse/MUL-1861
Test drive extension PR: MetaMask/metamask-extension#43110

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

High Risk
Fixes incorrect cross-SRP cache sharing that could decrypt or write another SRP's backup/sync data when profile IDs align; touches encryption key derivation and snap signing paths.

Overview
Fixes a multi-SRP / shared-profile cache collision where UserStorageController keyed storage-key and snap-signature caches only by metamask:${profileId}. When two SRPs shared the same profileId (e.g. after canonical profile pairing), one SRP could reuse the other’s cached derived key and read or write the wrong encrypted user storage (including symptoms like a second SRP showing the first’s account names).

UserStorageController now scopes both caches with #scopedCacheKey as `${entropySourceId}:${message}` (primary uses the first HD keyring metadata id, not a fixed "primary" string). Storage callbacks pass entropySourceId through; getStorageKey returns null while locked; snap signing uses the same scoped cache. user-storage SDK StorageOptions and getStorageKey propagate optional entropySourceId on get/set cache. Tests cover vault restore (new entropy id → new key), signature reuse after storage-key flush, and distinct API paths per entropy source with identical profileId. Changelog documents the fix; test fixtures include a default HD keyring for primary scope.

Reviewed by Cursor Bugbot for commit a1edb9a. Bugbot is set up for automated code reviews on this repo. Configure here.

@mathieuartu mathieuartu self-assigned this Jun 1, 2026
@mathieuartu mathieuartu requested review from a team as code owners June 1, 2026 13:21
@mathieuartu
Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.0-preview-1e1b476aa
@metamask-previews/accounts-controller@38.1.2-preview-1e1b476aa
@metamask-previews/address-book-controller@7.1.2-preview-1e1b476aa
@metamask-previews/ai-controllers@0.7.0-preview-1e1b476aa
@metamask-previews/analytics-controller@1.1.0-preview-1e1b476aa
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-1e1b476aa
@metamask-previews/announcement-controller@8.1.0-preview-1e1b476aa
@metamask-previews/app-metadata-controller@2.0.1-preview-1e1b476aa
@metamask-previews/approval-controller@9.0.1-preview-1e1b476aa
@metamask-previews/assets-controller@8.2.0-preview-1e1b476aa
@metamask-previews/assets-controllers@108.3.0-preview-1e1b476aa
@metamask-previews/authenticated-user-storage@2.0.0-preview-1e1b476aa
@metamask-previews/base-controller@9.1.0-preview-1e1b476aa
@metamask-previews/base-data-service@0.1.3-preview-1e1b476aa
@metamask-previews/bridge-controller@73.2.0-preview-1e1b476aa
@metamask-previews/bridge-status-controller@72.0.0-preview-1e1b476aa
@metamask-previews/build-utils@3.0.4-preview-1e1b476aa
@metamask-previews/chain-agnostic-permission@1.6.1-preview-1e1b476aa
@metamask-previews/chomp-api-service@3.1.0-preview-1e1b476aa
@metamask-previews/claims-controller@0.5.2-preview-1e1b476aa
@metamask-previews/client-controller@1.0.1-preview-1e1b476aa
@metamask-previews/compliance-controller@2.0.1-preview-1e1b476aa
@metamask-previews/composable-controller@12.0.1-preview-1e1b476aa
@metamask-previews/config-registry-controller@0.3.2-preview-1e1b476aa
@metamask-previews/connectivity-controller@0.2.0-preview-1e1b476aa
@metamask-previews/controller-utils@12.1.0-preview-1e1b476aa
@metamask-previews/core-backend@6.3.1-preview-1e1b476aa
@metamask-previews/delegation-controller@3.0.1-preview-1e1b476aa
@metamask-previews/earn-controller@12.2.0-preview-1e1b476aa
@metamask-previews/eip-5792-middleware@3.0.4-preview-1e1b476aa
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-1e1b476aa
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-1e1b476aa
@metamask-previews/ens-controller@19.1.3-preview-1e1b476aa
@metamask-previews/eth-block-tracker@15.0.1-preview-1e1b476aa
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-1e1b476aa
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-1e1b476aa
@metamask-previews/foundryup@1.0.1-preview-1e1b476aa
@metamask-previews/gas-fee-controller@26.2.2-preview-1e1b476aa
@metamask-previews/gator-permissions-controller@4.2.0-preview-1e1b476aa
@metamask-previews/geolocation-controller@0.1.3-preview-1e1b476aa
@metamask-previews/json-rpc-engine@10.5.0-preview-1e1b476aa
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-1e1b476aa
@metamask-previews/keyring-controller@26.0.0-preview-1e1b476aa
@metamask-previews/logging-controller@8.0.2-preview-1e1b476aa
@metamask-previews/message-manager@14.1.2-preview-1e1b476aa
@metamask-previews/messenger@1.2.0-preview-1e1b476aa
@metamask-previews/messenger-cli@0.2.0-preview-1e1b476aa
@metamask-previews/money-account-balance-service@1.0.2-preview-1e1b476aa
@metamask-previews/money-account-controller@0.3.1-preview-1e1b476aa
@metamask-previews/money-account-upgrade-controller@2.0.3-preview-1e1b476aa
@metamask-previews/multichain-account-service@10.0.1-preview-1e1b476aa
@metamask-previews/multichain-api-middleware@3.1.2-preview-1e1b476aa
@metamask-previews/multichain-network-controller@3.1.2-preview-1e1b476aa
@metamask-previews/multichain-transactions-controller@7.1.0-preview-1e1b476aa
@metamask-previews/name-controller@9.1.2-preview-1e1b476aa
@metamask-previews/network-controller@32.0.0-preview-1e1b476aa
@metamask-previews/network-enablement-controller@5.2.0-preview-1e1b476aa
@metamask-previews/notification-services-controller@24.1.2-preview-1e1b476aa
@metamask-previews/passkey-controller@2.0.1-preview-1e1b476aa
@metamask-previews/permission-controller@13.1.1-preview-1e1b476aa
@metamask-previews/permission-log-controller@5.1.0-preview-1e1b476aa
@metamask-previews/perps-controller@7.0.0-preview-1e1b476aa
@metamask-previews/phishing-controller@17.2.0-preview-1e1b476aa
@metamask-previews/polling-controller@16.0.6-preview-1e1b476aa
@metamask-previews/preferences-controller@23.1.0-preview-1e1b476aa
@metamask-previews/profile-metrics-controller@3.1.5-preview-1e1b476aa
@metamask-previews/profile-sync-controller@28.1.1-preview-1e1b476aa
@metamask-previews/ramps-controller@14.0.0-preview-1e1b476aa
@metamask-previews/rate-limit-controller@7.0.1-preview-1e1b476aa
@metamask-previews/react-data-query@0.2.1-preview-1e1b476aa
@metamask-previews/remote-feature-flag-controller@4.2.1-preview-1e1b476aa
@metamask-previews/sample-controllers@5.0.1-preview-1e1b476aa
@metamask-previews/seedless-onboarding-controller@10.0.0-preview-1e1b476aa
@metamask-previews/selected-network-controller@26.1.3-preview-1e1b476aa
@metamask-previews/shield-controller@5.1.2-preview-1e1b476aa
@metamask-previews/signature-controller@39.2.3-preview-1e1b476aa
@metamask-previews/snap-account-service@0.2.1-preview-1e1b476aa
@metamask-previews/social-controllers@2.2.1-preview-1e1b476aa
@metamask-previews/storage-service@1.0.1-preview-1e1b476aa
@metamask-previews/subscription-controller@6.1.3-preview-1e1b476aa
@metamask-previews/transaction-controller@66.0.0-preview-1e1b476aa
@metamask-previews/transaction-pay-controller@23.0.0-preview-1e1b476aa
@metamask-previews/user-operation-controller@41.2.3-preview-1e1b476aa
@metamask-previews/wallet@1.0.1-preview-1e1b476aa

message: `metamask:${string}`,
entropySourceId?: string,
): string {
return `${entropySourceId ?? 'primary'}:${message}`;
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.

Just wondering, if we somehow start using the primary keyring with its explicit entropySourceId, then it would be both under primary:... and <primary-entropy-source-id>:... scoped keys?

That's probably ok?

Otherwise, we could just fallback to the real primary entropy source ID too?

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.

Another question that came to mind is, during a "reset wallet" flow, are we clearing this cache?

If during that flow we start using a new SRP, this would become the new primary, but I think the Snap message we put in cache here, would change with a different SRP, so we need to invalidate this cache somehow.

(I haven't checked thoroughly this controller, just pointing this out so we can double-check we're not invalid cached value 😅)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

since this was mentioned, can we double check if #_snapSignMessageCache needs to be cleaned/flushed at some point?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Proposition here: cbcef3d

Copy link
Copy Markdown
Contributor

@ccharly ccharly left a comment

Choose a reason for hiding this comment

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

Logic LGTM now, thanks for addressing that!

The only remark I might have is that we have no way to flush the #snapSignMessageCache cache. Though, given that we now use truly random ID (entropy source ID) even for the primary, we should never ever have have conflicting keys (+ this will get flushed upon client restart anyway).

@mathieuartu mathieuartu added this pull request to the merge queue Jun 3, 2026
Merged via the queue into main with commit c209a74 Jun 3, 2026
370 checks passed
@mathieuartu mathieuartu deleted the fix/profile-sync-srp-data-leak-pairing branch June 3, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants