Skip to content

Comments

feat!: add serialized keyring data to state#8009

Open
mikesposito wants to merge 3 commits intomainfrom
mikesposito/feat/add-serialized-keyrings-data-to-state
Open

feat!: add serialized keyring data to state#8009
mikesposito wants to merge 3 commits intomainfrom
mikesposito/feat/add-serialized-keyrings-data-to-state

Conversation

@mikesposito
Copy link
Member

@mikesposito mikesposito commented Feb 20, 2026

Explanation

A draft proposal to provide a way to sychronously access read-only serialized keyrings via KeyringController state, with no mutex needed

References

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

Medium Risk
Touches keyring state shape and logging behavior; consumers may break if they assume KeyringObject has only type/accounts/metadata, and serialized data in memory increases sensitivity if mishandled.

Overview
BREAKING: KeyringObject (and thus KeyringControllerState.keyrings) now includes a data: Json field containing a deep-cloned result of keyring.serialize(), so serialized keyring data is synchronously available in controller state.

To reduce sensitive logging, keyrings is no longer included in state logs metadata. Tests and changelog are updated to assert the new data field and to use a mock keyring builder in the “existing metadata” unlock test; unlock state restoration now assigns updated keyrings with a TS workaround for deep Json instantiation.

Written by Cursor Bugbot for commit 3d4d0d0. This will update automatically on new commits. Configure here.

},
keyrings: {
includeInStateLogs: true,
includeInStateLogs: false,
Copy link
Member Author

@mikesposito mikesposito Feb 20, 2026

Choose a reason for hiding this comment

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

The keyrings array has historically provided useful insights into state logs. If we want to keep this array exposed, we can add an separate serializedKeyrings state property where we can store serialized keyring objects safely, instead of having a data property in the objects included in keyrings

@mikesposito mikesposito marked this pull request as ready for review February 20, 2026 17:47
@mikesposito mikesposito requested review from a team as code owners February 20, 2026 17:47
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

// Cast to `string[]` here is safe here because `accounts` has no nullish
// values, and `normalize` returns `string` unless given a nullish value
accounts: accounts.map(normalize) as string[],
data: cloneDeep(await keyring.serialize()),
Copy link

Choose a reason for hiding this comment

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

Serialized keyring secrets exposed via controller state

High Severity

displayForKeyring now puts await keyring.serialize() into state.keyrings[*].data, and keyrings is marked usedInUi: true. Since keyring serialize() commonly includes sensitive material (e.g., mnemonics/private keys), this exposes secrets to any consumer of KeyringController:getState / KeyringController:stateChange (including UI code).

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think we should worry about KeyringController consumers being able to read secrets, because that's already possible via withKeyring. But perhaps we should consider restricting this property in the UI, to avoid accidental leaks.

// Cast to `string[]` here is safe here because `accounts` has no nullish
// values, and `normalize` returns `string` unless given a nullish value
accounts: accounts.map(normalize) as string[],
data: cloneDeep(await keyring.serialize()),
Copy link

Choose a reason for hiding this comment

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

State updates now repeatedly serialize keyrings

Medium Severity

displayForKeyring now calls keyring.serialize() (and cloneDeep) when building state.keyrings. This makes common operations that refresh state (e.g., unlock, vault updates) perform full keyring serialization even when only accounts/type/metadata are needed, which can significantly increase latency for large keyrings or expensive serializers.

Fix in Cursor Fix in Web

},
keyrings: {
includeInStateLogs: true,
includeInStateLogs: false,
Copy link

Choose a reason for hiding this comment

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

Keyrings removed from state logs unexpectedly

Medium Severity

Setting keyrings.includeInStateLogs to false removes keyrings from deriveStateFromMetadata(..., 'includeInStateLogs'), reducing observability in state logs. This can break existing diagnostics/support workflows that relied on seeing the (non-sensitive) keyrings array shape/types in logs, especially since the PR discussion notes historical reliance on it.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

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.

1 participant