-
-
Notifications
You must be signed in to change notification settings - Fork 290
feat: validate eth_sendTransaction / eth_signTransaction params
#9482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e4570f5
8a62471
c138500
42e4bee
186f262
4f96cf5
a5db667
fa3e763
77fd64e
d6b900c
2cdbbcc
57e5c1c
30283ac
0fb9263
97c8b4b
42f0c85
ced4991
2b91e13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,13 @@ | ||
| 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'; | ||
| import { | ||
| array, | ||
| object, | ||
| optional, | ||
| string, | ||
| validate, | ||
| } from '@metamask/superstruct'; | ||
| import type { Hex } from '@metamask/utils'; | ||
|
|
||
| import type { WalletMiddlewareContext } from '../wallet'; | ||
|
|
@@ -234,3 +240,72 @@ export function validateTypedMessageKeys(data: string): void { | |
| } | ||
| } | ||
| } | ||
|
|
||
| export const TransactionParamsStruct = object({ | ||
| accessList: optional( | ||
| array(object({ address: string(), storageKeys: array(string()) })), | ||
| ), | ||
| authorizationList: optional( | ||
| array( | ||
| object({ | ||
| address: string(), | ||
| chainId: string(), | ||
| nonce: string(), | ||
| r: optional(string()), | ||
| s: optional(string()), | ||
| yParity: optional(string()), | ||
| }), | ||
| ), | ||
| ), | ||
| chainId: optional(string()), | ||
| data: optional(string()), | ||
| from: string(), | ||
| gas: optional(string()), | ||
| gasLimit: optional(string()), | ||
| gasPrice: optional(string()), | ||
| maxFeePerGas: optional(string()), | ||
| maxPriorityFeePerGas: optional(string()), | ||
| nonce: optional(string()), | ||
| to: optional(string()), | ||
| type: optional(string()), | ||
| value: optional(string()), | ||
| }); | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| // Upper bound derived from the largest valid eth_sendTransaction payload: | ||
| // EIP-3860 caps initcode at 49,152 bytes → hex-encoded in 'data' field ≈ 98 KB of JSON. | ||
| // 200 KB is ~2× that ceiling, giving clear headroom above any protocol-legal | ||
| // transaction while blocking the padding attacks this cap defends against. | ||
| // TODO(CONF-1662): tighten once P99 production data is available. | ||
| export const MAX_TRANSACTION_PARAMS_SIZE_BYTES = 200 * 1024; | ||
|
|
||
| /** | ||
| * Validates `eth_sendTransaction` / `eth_signTransaction` params against the | ||
| * standard transaction schema and rejects payloads whose serialized size | ||
| * exceeds `MAX_TRANSACTION_PARAMS_SIZE_BYTES`. | ||
| * | ||
| * Guards against two attack shapes: | ||
| * - Structural: extraneous top-level keys or ill-typed fields (e.g. | ||
| * `{ from, to, test: { b: { b: ... × 1200 } } }`) that would crash | ||
| * downstream normalization / PPOM WASM with `RangeError: Maximum call | ||
| * stack size exceeded`, silently bypassing security checks. Superstruct's | ||
| * `object()` rejects unknown keys by name without accessing their values, | ||
| * so hostile nested subtrees are never traversed. | ||
| * - Size: valid-shaped but oversized payloads (e.g. `data` padded with | ||
| * millions of hex zeros) that exhaust memory in downstream code. | ||
| * | ||
| * @param params - The transaction params object supplied by the dapp. | ||
| * @throws rpcErrors.invalidParams() if params is an array or exceeds the | ||
| * serialized size limit. | ||
| * @throws rpcErrors.invalidInput() if params fails schema validation | ||
| * (wrong type, extraneous top-level key, or malformed nested field). | ||
| */ | ||
| export function validateTransactionParams(params: unknown): void { | ||
| validateParams(params, TransactionParamsStruct); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Struct validation before size capMedium Severity
Reviewed by Cursor Bugbot for commit 3009741. Configure here.
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| if ( | ||
| new TextEncoder().encode(JSON.stringify(params)).byteLength > | ||
| MAX_TRANSACTION_PARAMS_SIZE_BYTES | ||
| ) { | ||
| throw rpcErrors.invalidInput(); | ||
| } | ||
| } | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension/mobile PPOM middleware can gate params before their own
normalizeTransactionParamscall (which is where the WASM crash actually originates in clients).