{"title":"Prototype Pollution via Deep Merge of Untrusted JSON","language":"Javascript","severity":"Critical","cwe":"CWE-1321","source_lines":[1],"flow_lines":[1,3,14],"sink_lines":[14],"vulnerable_code":"async function syncIoTDeviceConfig(deviceId, cloudPayload) {\n  const localConfig = await db.getDeviceSettings(deviceId);\n  function applyCloudSettings(target, source) {\n    for (let prop in source) {\n      if (source[prop] && typeof source[prop] === 'object' && !Array.isArray(source[prop])) {\n        target[prop] = target[prop] || {};\n        applyCloudSettings(target[prop], source[prop]);\n      } else {\n        target[prop] = source[prop];\n      }\n    }\n    return target;\n  }\n  const mergedSettings = applyCloudSettings(localConfig, cloudPayload);\n  await device.updateFirmwareConfig(deviceId, mergedSettings);\n  return mergedSettings;\n}","explanation":"The cloudPayload parameter is untrusted input from the cloud backend that is directly merged into the localConfig object without sanitization. The recursive applyCloudSettings function does not filter dangerous properties like __proto__, constructor, or prototype, allowing an attacker to pollute Object.prototype and inject properties that affect all objects in the JavaScript runtime.","remediation":"The fix prevents prototype pollution by skipping dangerous property keys (__proto__, constructor, prototype) during the recursive merge and by using hasOwnProperty to only iterate over the source object's own properties, preventing inherited properties from being traversed.","secure_code":"async function syncIoTDeviceConfig(deviceId, cloudPayload) {\n  const localConfig = await db.getDeviceSettings(deviceId);\n  const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n  function applyCloudSettings(target, source) {\n    for (let prop in source) {\n      if (!Object.prototype.hasOwnProperty.call(source, prop)) continue;\n      if (DANGEROUS_KEYS.has(prop)) continue;\n      if (source[prop] && typeof source[prop] === 'object' && !Array.isArray(source[prop])) {\n        target[prop] = target[prop] || {};\n        applyCloudSettings(target[prop], source[prop]);\n      } else {\n        target[prop] = source[prop];\n      }\n    }\n    return target;\n  }\n  const mergedSettings = applyCloudSettings(localConfig, cloudPayload);\n  await device.updateFirmwareConfig(deviceId, mergedSettings);\n  return mergedSettings;\n}"}