From a8140cd8c238a3de0b3ba8775cf34402b54121d9 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 20 Apr 2026 21:34:52 +0530 Subject: [PATCH 1/4] feat: adding more validations for types sign v4 messages --- .../src/utils/validation.test.ts | 36 +++++++++++++++++++ .../src/utils/validation.ts | 21 +++++++++++ .../src/wallet.test.ts | 23 ++++++++++++ .../eth-json-rpc-middleware/src/wallet.ts | 2 ++ 4 files changed, 82 insertions(+) diff --git a/packages/eth-json-rpc-middleware/src/utils/validation.test.ts b/packages/eth-json-rpc-middleware/src/utils/validation.test.ts index 8191436037b..0b572b9a31d 100644 --- a/packages/eth-json-rpc-middleware/src/utils/validation.test.ts +++ b/packages/eth-json-rpc-middleware/src/utils/validation.test.ts @@ -8,6 +8,7 @@ import { resemblesAddress, validateAndNormalizeKeyholder, validateParams, + validateTypedMessageKeys, } from './validation'; jest.mock('@metamask/superstruct', () => ({ @@ -125,4 +126,39 @@ describe('Validation Utils', () => { `); }); }); + + describe('validateTypedMessageKeys', () => { + it('does not throw for data with only schema-defined keys', () => { + const data = JSON.stringify({ + types: { + EIP712Domain: [{ name: 'name', type: 'string' }], + }, + }); + + expect(() => validateTypedMessageKeys(data)).not.toThrow(); + }); + + it('throws for data with extraneous keys', () => { + const data = JSON.stringify({ + types: { + EIP712Domain: [{ name: 'name', type: 'string' }], + }, + primaryType: 'EIP712Domain', + domain: {}, + message: {}, + extraKey: 'unexpected', + }); + + expect(() => validateTypedMessageKeys(data)).toThrow('Invalid input.'); + }); + + it('throws when data contains only extraneous keys', () => { + const data = JSON.stringify({ + foo: 'bar', + baz: 123, + }); + + expect(() => validateTypedMessageKeys(data)).toThrow('Invalid input.'); + }); + }); }); diff --git a/packages/eth-json-rpc-middleware/src/utils/validation.ts b/packages/eth-json-rpc-middleware/src/utils/validation.ts index 7d291aec56c..547a35b0363 100644 --- a/packages/eth-json-rpc-middleware/src/utils/validation.ts +++ b/packages/eth-json-rpc-middleware/src/utils/validation.ts @@ -1,3 +1,4 @@ +import { TYPED_MESSAGE_SCHEMA } from '@metamask/eth-sig-util'; import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; import type { Struct, StructError } from '@metamask/superstruct'; import { validate } from '@metamask/superstruct'; @@ -187,3 +188,23 @@ export function validateTypedDataForPrototypePollution(data: string): void { checkObjectForPrototypePollution(message); } } + +/** + * Validates that EIP-712 typed message data contains only keys defined in + * the TYPED_MESSAGE_SCHEMA from `@metamask/eth-sig-util`. Rejects messages + * with extraneous top-level keys. + * + * @param data - The stringified typed data to validate. + * @throws rpcErrors.invalidInput() if extraneous keys are detected. + */ +export function validateTypedMessageKeys(data: string): void { + const parsedData = parseTypedMessage(data); + const allowedKeys = new Set(Object.keys(TYPED_MESSAGE_SCHEMA.properties)); + const extraneousKeys = Object.keys(parsedData).filter( + (key) => !allowedKeys.has(key), + ); + + if (extraneousKeys.length > 0) { + throw rpcErrors.invalidInput(); + } +} diff --git a/packages/eth-json-rpc-middleware/src/wallet.test.ts b/packages/eth-json-rpc-middleware/src/wallet.test.ts index 3c8d69fd36e..a9964c0d3a1 100644 --- a/packages/eth-json-rpc-middleware/src/wallet.test.ts +++ b/packages/eth-json-rpc-middleware/src/wallet.test.ts @@ -757,6 +757,29 @@ describe('wallet', () => { engine.handle(...createHandleParams(payload)), ).rejects.toThrow('Invalid input.'); }); + + it('should throw if message data contains extraneous keys', async () => { + const getAccounts = async (): Promise => testAddresses.slice(); + const processTypedMessageV4 = async (): Promise => testMsgSig; + const engine = JsonRpcEngineV2.create({ + middleware: [ + createWalletMiddleware({ getAccounts, processTypedMessageV4 }), + ], + }); + + const messageParams = getMsgParams(); + const payload = { + method: 'eth_signTypedData_v4', + params: [ + testAddresses[0], + JSON.stringify({ ...messageParams, extraKey: 'unexpected' }), + ], + }; + + await expect( + engine.handle(...createHandleParams(payload)), + ).rejects.toThrow('Invalid input.'); + }); }); describe('sign', () => { diff --git a/packages/eth-json-rpc-middleware/src/wallet.ts b/packages/eth-json-rpc-middleware/src/wallet.ts index 24394190640..b220be16e09 100644 --- a/packages/eth-json-rpc-middleware/src/wallet.ts +++ b/packages/eth-json-rpc-middleware/src/wallet.ts @@ -25,6 +25,7 @@ import { validateAndNormalizeKeyholder as validateKeyholder, validateTypedDataForPrototypePollution, validateTypedDataV1ForPrototypePollution, + validateTypedMessageKeys, } from './utils/validation'; export type TransactionParams = { @@ -408,6 +409,7 @@ export function createWalletMiddleware({ const address = await validateAndNormalizeKeyholder(params[0], context); const message = normalizeTypedMessage(params[1]); + validateTypedMessageKeys(message); validatePrimaryType(message); validateVerifyingContract(message); validateTypedDataForPrototypePollution(message); From 467a4bd51c6fb10a2120188cde840ca2ae3c0fcd Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 21 Apr 2026 14:04:52 +0530 Subject: [PATCH 2/4] changelog --- packages/eth-json-rpc-middleware/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/eth-json-rpc-middleware/CHANGELOG.md b/packages/eth-json-rpc-middleware/CHANGELOG.md index 86c71ec6dce..6d3bf772d6e 100644 --- a/packages/eth-json-rpc-middleware/CHANGELOG.md +++ b/packages/eth-json-rpc-middleware/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add more strict validation for signTypedData V4 requests ([#8526](https://github.com/MetaMask/core/pull/8526)) + ## [23.1.1] ### Changed From 11a16b702430dd0e70bee8609fe0dbb274bcb908 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 22 Apr 2026 08:58:33 +0530 Subject: [PATCH 3/4] Update packages/eth-json-rpc-middleware/CHANGELOG.md Co-authored-by: Elliot Winkler --- packages/eth-json-rpc-middleware/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eth-json-rpc-middleware/CHANGELOG.md b/packages/eth-json-rpc-middleware/CHANGELOG.md index 6d3bf772d6e..62d436f4187 100644 --- a/packages/eth-json-rpc-middleware/CHANGELOG.md +++ b/packages/eth-json-rpc-middleware/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added +### Changed - Add more strict validation for signTypedData V4 requests ([#8526](https://github.com/MetaMask/core/pull/8526)) From 3c2ae43efc033a93c32c2ea0baa57856f8be285e Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 22 Apr 2026 08:58:58 +0530 Subject: [PATCH 4/4] Update packages/eth-json-rpc-middleware/src/utils/validation.ts Co-authored-by: jiexi --- packages/eth-json-rpc-middleware/src/utils/validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eth-json-rpc-middleware/src/utils/validation.ts b/packages/eth-json-rpc-middleware/src/utils/validation.ts index 547a35b0363..f65a6bc41ba 100644 --- a/packages/eth-json-rpc-middleware/src/utils/validation.ts +++ b/packages/eth-json-rpc-middleware/src/utils/validation.ts @@ -200,11 +200,11 @@ export function validateTypedDataForPrototypePollution(data: string): void { export function validateTypedMessageKeys(data: string): void { const parsedData = parseTypedMessage(data); const allowedKeys = new Set(Object.keys(TYPED_MESSAGE_SCHEMA.properties)); - const extraneousKeys = Object.keys(parsedData).filter( + const hasExtraneousKey = Object.keys(parsedData).some( (key) => !allowedKeys.has(key), ); - if (extraneousKeys.length > 0) { + if (hasExtraneousKey) { throw rpcErrors.invalidInput(); } }