# Prototype Pollution via Deep Merge of Untrusted JSON

Language: Javascript
Severity: Critical
CWE: CWE-1321

## Source
1

## Flow
1-3-14

## Sink
14

## Vulnerable Code
```javascript
async function syncIoTDeviceConfig(deviceId, cloudPayload) {
  const localConfig = await db.getDeviceSettings(deviceId);
  function applyCloudSettings(target, source) {
    for (let prop in source) {
      if (source[prop] && typeof source[prop] === 'object' && !Array.isArray(source[prop])) {
        target[prop] = target[prop] || {};
        applyCloudSettings(target[prop], source[prop]);
      } else {
        target[prop] = source[prop];
      }
    }
    return target;
  }
  const mergedSettings = applyCloudSettings(localConfig, cloudPayload);
  await device.updateFirmwareConfig(deviceId, mergedSettings);
  return mergedSettings;
}
```

## 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
```javascript
async function syncIoTDeviceConfig(deviceId, cloudPayload) {
  const localConfig = await db.getDeviceSettings(deviceId);
  const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
  function applyCloudSettings(target, source) {
    for (let prop in source) {
      if (!Object.prototype.hasOwnProperty.call(source, prop)) continue;
      if (DANGEROUS_KEYS.has(prop)) continue;
      if (source[prop] && typeof source[prop] === 'object' && !Array.isArray(source[prop])) {
        target[prop] = target[prop] || {};
        applyCloudSettings(target[prop], source[prop]);
      } else {
        target[prop] = source[prop];
      }
    }
    return target;
  }
  const mergedSettings = applyCloudSettings(localConfig, cloudPayload);
  await device.updateFirmwareConfig(deviceId, mergedSettings);
  return mergedSettings;
}
```
