{"record":{"id":"800ab04c8c4fcd97","repo":"n8n-io/n8n","slug":"dangerous-key-dangerouskey-found-in-json-data","errorCode":null,"errorMessage":"Dangerous key \"${dangerousKey}\" found in JSON data","messagePattern":"Dangerous key \"(.+?)\" found in JSON data","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/src/parsers/structured-file-parser.ts","lineNumber":340,"sourceCode":"\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('Expected a JSON array of objects, got a single value or object');\n\t}\n\n\tif (parsed.length === 0) {\n\t\treturn { rawHeaders: [], allRows: [] };\n\t}\n\n\t// Collect all keys across all objects (preserving order of first appearance)\n\tconst keySet = new Set<string>();\n\tfor (const item of parsed) {\n\t\tif (typeof item !== 'object' || item === null || Array.isArray(item)) {\n\t\t\tthrow new Error('JSON array items must be flat objects (not arrays, nulls, or primitives)');\n\t\t}\n\t\tconst dangerousKey = hasDangerousKey(item as Record<string, unknown>);\n\t\tif (dangerousKey) {\n\t\t\tthrow new Error(`Dangerous key \"${dangerousKey}\" found in JSON data`);\n\t\t}\n\t\tfor (const key of Object.keys(item as Record<string, unknown>)) {\n\t\t\tkeySet.add(key);\n\t\t}\n\t}\n\n\tconst rawHeaders = [...keySet];\n\n\t// Validate all values are flat\n\tfor (const item of parsed) {\n\t\tconst obj = item as Record<string, unknown>;\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tif (value !== null && typeof value === 'object') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Nested ${Array.isArray(value) ? 'array' : 'object'} found at key \"${key}\". Only flat objects are supported.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/src/parsers/structured-file-parser.ts#L322-L358","documentation":"Thrown by parseJson() inside parseStructuredFile when a JSON array object contains a key named __proto__, constructor, or prototype (see the DANGEROUS_KEYS set at structured-file-parser.ts:49). The guard runs before keys are collected as column headers, so prototype-polluting payloads can never reach downstream row/column processing. It is a hard security refusal, not a recoverable shape problem.","triggerScenarios":"An attachment with mimeType application/json (or an .xlsx round-tripped through JSON at xlsx-parser.ts:56) whose decoded content is a JSON array containing an object like {\"__proto__\": {...}} or {\"constructor\": \"x\"}. The check fires for every item in the array during header collection.","commonSituations":"Data exported from a tool that serializes class metadata; a user sanitizing/renaming columns to JS reserved names; an LLM-generated eval-data payload that includes prototype chains; a malicious or fuzzed attachment probing for prototype-pollution sinks.","solutions":["Inspect the JSON for the keys __proto__, constructor, or prototype and rename or strip them before parsing.","If the source is xlsx, open the sheet and rename the offending column header.","Pre-validate the parsed JSON with your own hasDangerousKey check (structured-file-parser.ts:261) to give a clearer upstream error.","Reject the attachment at the MIME-validation layer if it cannot be trusted."],"exampleFix":"// before: [{ \"__proto__\": { \"admin\": true }, \"name\": \"x\" }]\n// after:  [{ \"name\": \"x\" }]","handlingStrategy":"validation","validationCode":"const DANGEROUS = new Set(['__proto__','constructor','prototype']);\nfunction hasDangerousKey(obj: Record<string, unknown>): string | undefined {\n  for (const key of Object.keys(obj)) if (DANGEROUS.has(key)) return key;\n  return undefined;\n}\n// run before parseStructuredFile:\nconst parsed = JSON.parse(content);\nfor (const item of parsed) {\n  const bad = hasDangerousKey(item);\n  if (bad) throw new Error(`Refusing to attach: dangerous key \"${bad}\"`);\n}","typeGuard":"function isSafeFlatObject(v: unknown): v is Record<string, string | number | boolean | null> {\n  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n  for (const key of Object.keys(v)) {\n    if (DANGEROUS.has(key)) return false;\n    const val = (v as Record<string, unknown>)[key];\n    if (val !== null && typeof val === 'object') return false;\n  }\n  return true;\n}","tryCatchPattern":"try { return parseStructuredFile(attachment, idx, input); }\ncatch (e) {\n  if (e instanceof Error && e.message.startsWith('Dangerous key')) {\n    // surface a sanitize prompt to the caller / LLM\n  }\n  throw e;\n}","preventionTips":["Sanitize exported data with a key blocklist before attaching.","Never trust LLM- or user-generated JSON to be prototype-safe; validate first.","Treat __proto__/constructor/prototype as reserved names in any tool that emits tabular data."],"tags":["security","prototype-pollution","json","attachment-parsing"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}