Skip to content

feat: add superstruct.sensitive#577

Open
ccharly wants to merge 10 commits into
mainfrom
cc/feat/superstruct-sensitive
Open

feat: add superstruct.sensitive#577
ccharly wants to merge 10 commits into
mainfrom
cc/feat/superstruct-sensitive

Conversation

@ccharly

@ccharly ccharly commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Adding a new superstruct "decorator" to mark a field as "sensitive" this way, it never gets logged by mistake in superstruct error messages.

Also, redacting the value whenever we try to use it using struct internal/validations.

Lastly, to enable the last point (nested sensitive fields), we have to use our custom object, so we now have an eslint rule to force us to use this object (at least in the keyring-api for now).


Note

Medium Risk
Changes validation error shaping for schemas that carry private keys; behavior is security-hardening but touches secret-handling paths and many keyring-api structs.

Overview
Adds sensitive() in @metamask/keyring-utils so superstruct validation failures replace secret field values with *** in error message, StructError.value, and branch metadata. The package object() is extended to scrub sensitive() sibling keys from parent objects in failure branches (including nested siblings), and type() casting is tightened without changing behavior.

@metamask/keyring-api now imports object() / exactOptional() from keyring-utils instead of @metamask/superstruct, and PrivateKeyExportedAccountStruct.privateKey uses sensitive(string()). New tests cover export-account redaction; ESLint no-restricted-imports blocks raw superstruct object / exactOptional under packages/keyring-api.

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

@ccharly ccharly marked this pull request as ready for review June 25, 2026 13:19
@ccharly ccharly requested a review from a team as a code owner June 25, 2026 13:19
Comment thread eslint.config.mjs
Comment on lines +153 to +158
{
name: '@metamask/superstruct',
importNames: ['object'],
message:
"Import object() from '@metamask/keyring-utils' instead — it supports exactOptional() and sensitive() field redaction.",
},

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.

This forces us to use our own object type that supports our exactOptional AND also allows us to use the new sensitive decorator.

Comment thread eslint.config.mjs
Comment on lines +159 to +164
{
name: '@metamask/superstruct',
importNames: ['exactOptional'],
message:
"Import exactOptional() from '@metamask/keyring-utils' instead — mixing superstruct's exactOptional with keyring-utils' object() breaks ExactOptionalize and makes optional fields appear required.",
},

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.

Same here, we cannot use the exactOptional from the main repo (that works slightly differently from ours)

* The private key of the exported account.
*/
privateKey: string(),
privateKey: sensitive(string()),

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.

Since it's marked as sensitive AND that we use our object struct, this field will never be display in validation/error messages from superstruct.

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