{"version":3,"file":"validation.mjs","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { providerErrors, rpcErrors } from '@metamask/rpc-errors';\nimport type { Struct, StructError } from '@metamask/superstruct';\nimport { validate } from '@metamask/superstruct';\nimport type { Hex } from '@metamask/utils';\n\nimport { parseTypedMessage } from './normalize';\nimport type { WalletMiddlewareContext } from '../wallet';\n\n/**\n * Validates and normalizes a keyholder address for transaction- and\n * signature-related operations.\n *\n * @param address - The Ethereum address to validate and normalize.\n * @param context - The context of the request.\n * @param options - The options for the validation.\n * @param options.getAccounts - The function to get the accounts for the origin.\n * @returns The normalized address, if valid. Otherwise, throws\n * an error\n */\nexport async function validateAndNormalizeKeyholder(\n  address: Hex,\n  context: WalletMiddlewareContext,\n  { getAccounts }: { getAccounts: (origin: string) => Promise<string[]> },\n): Promise<Hex> {\n  if (\n    typeof address === 'string' &&\n    address.length > 0 &&\n    resemblesAddress(address)\n  ) {\n    // Ensure that an \"unauthorized\" error is thrown if the requester\n    // does not have the `eth_accounts` permission.\n    const accounts = await getAccounts(context.assertGet('origin'));\n\n    const normalizedAccounts: string[] = accounts.map((_address) =>\n      _address.toLowerCase(),\n    );\n\n    const normalizedAddress = address.toLowerCase() as Hex;\n\n    if (normalizedAccounts.includes(normalizedAddress)) {\n      return normalizedAddress;\n    }\n\n    throw providerErrors.unauthorized();\n  }\n\n  throw rpcErrors.invalidParams({\n    message: `Invalid parameters: must provide an Ethereum address.`,\n  });\n}\n\n/**\n * Validates the parameters of a request against a Superstruct schema.\n * Throws a JSON-RPC error if the parameters are invalid.\n *\n * @param value - The value to validate.\n * @param struct - The Superstruct schema to validate against.\n * @throws An error if the parameters are invalid.\n */\nexport function validateParams<ParamsType>(\n  value: unknown | ParamsType,\n  struct: Struct<ParamsType>,\n): asserts value is ParamsType {\n  const [error] = validate(value, struct);\n\n  if (error) {\n    throw rpcErrors.invalidParams(\n      formatValidationError(error, `Invalid params`),\n    );\n  }\n}\n\n/**\n * Checks if a string resembles an Ethereum address.\n *\n * @param str - The string to check.\n * @returns True if the string resembles an Ethereum address, false otherwise.\n */\nexport function resemblesAddress(str: string): boolean {\n  // hex prefix 2 + 20 bytes\n  return str.length === 2 + 20 * 2;\n}\n\n/**\n * Formats a Superstruct validation error into a human-readable string.\n *\n * @param error - The Superstruct validation error.\n * @param message - The base error message to prepend to the formatted details.\n * @returns The formatted error.\n */\nfunction formatValidationError(error: StructError, message: string): string {\n  return `${message}\\n\\n${error\n    .failures()\n    .map(\n      (failure) =>\n        `${failure.path.join(' > ')}${failure.path.length ? ' - ' : ''}${failure.message}`,\n    )\n    .join('\\n')}`;\n}\n\nexport const DANGEROUS_PROTOTYPE_PROPERTIES = [\n  '__proto__',\n  'constructor',\n  'prototype',\n  '__defineGetter__',\n  '__defineSetter__',\n  '__lookupGetter__',\n  '__lookupSetter__',\n] as const;\n\n/**\n * Checks if a property name is dangerous for prototype pollution.\n *\n * @param key - The property name to check\n * @returns True if the property name is dangerous\n */\nfunction isDangerousProperty(key: string): boolean {\n  return (DANGEROUS_PROTOTYPE_PROPERTIES as readonly string[]).includes(key);\n}\n\n/**\n * Recursively checks an object for dangerous prototype pollution properties.\n *\n * @param obj - The object to check\n * @throws rpcErrors.invalidInput() if a dangerous property is found\n */\nfunction checkObjectForPrototypePollution(obj: unknown): void {\n  if (obj === null || obj === undefined) {\n    return;\n  }\n\n  if (Array.isArray(obj)) {\n    for (const item of obj) {\n      checkObjectForPrototypePollution(item);\n    }\n    return;\n  }\n\n  if (typeof obj === 'object') {\n    for (const key of Object.getOwnPropertyNames(\n      obj as Record<string, unknown>,\n    )) {\n      if (isDangerousProperty(key)) {\n        throw rpcErrors.invalidInput();\n      }\n      checkObjectForPrototypePollution((obj as Record<string, unknown>)[key]);\n    }\n  }\n}\n\n/**\n * Validates V1 typed data (array format) for prototype pollution attacks.\n * V1 format: [{ type: 'string', name: 'fieldName', value: 'data' }, ...]\n *\n * @param data - The V1 typed data array to validate\n * @throws rpcErrors.invalidInput() if prototype pollution is detected\n */\nexport function validateTypedDataV1ForPrototypePollution(\n  data: Record<string, unknown>[],\n): void {\n  if (!data || !Array.isArray(data)) {\n    return;\n  }\n\n  for (const item of data) {\n    if (item && typeof item === 'object') {\n      // Only check the 'value' field (the message data) for dangerous properties\n      if (item.value !== null && typeof item.value === 'object') {\n        checkObjectForPrototypePollution(item.value);\n      }\n    }\n  }\n}\n\n/**\n * Validates V3/V4 typed data (EIP-712 format) for prototype pollution attacks.\n * Only checks the message field for dangerous properties.\n *\n * @param data - The stringified typed data to validate\n * @throws rpcErrors.invalidInput() if prototype pollution is detected\n */\nexport function validateTypedDataForPrototypePollution(data: string): void {\n  const { message } = parseTypedMessage(data);\n\n  // Check message recursively for dangerous properties\n  if (message !== undefined) {\n    checkObjectForPrototypePollution(message);\n  }\n}\n"]}