Skip to content
Injection Reviewed 2026-09-12

Prototype pollution

Prototype pollution occurs when untrusted property names reach operations that modify an object's prototype or a shared prototype. Other code may then read an inherited value as if the application had supplied it. Impact depends on that later use: changed options, incorrect authorization decisions, or access to a dangerous execution path.

Trust boundary: JSON, query parameters, or imported configuration must not choose arbitrary object paths. Parsing JSON alone does not pollute a prototype; a later assignment or recursive merge can introduce the weakness. OWASP's prevention guidance recommends structures such as Map and objects without prototypes where suitable.

Unsafe example: copying arbitrary preferences

This JavaScript example accepts parsed JSON. Assignment can invoke special setters, and it also accepts settings the caller should not control.

function preferences(input) {
  const result = {};
  for (const key of Object.keys(input)) result[key] = input[key];
  return result;
}

Safer example: construct the supported shape

function preferences(input) {
  if (input === null || typeof input !== 'object' || Array.isArray(input)) {
    throw new TypeError('Expected a preferences object');
  }
  const allowed = new Set(['theme', 'compact']);
  if (Object.keys(input).some(key => !allowed.has(key))) {
    throw new TypeError('Unsupported preference');
  }
  const theme = Object.hasOwn(input, 'theme') ? input.theme : 'light';
  const compact = Object.hasOwn(input, 'compact') ? input.compact : false;
  if (!['light', 'dark'].includes(theme) || typeof compact !== 'boolean') {
    throw new TypeError('Invalid preference');
  }
  return Object.assign(Object.create(null), { theme, compact });
}

Only two known properties reach a fresh result, and defaults do not read inherited values. The schema rejects nested objects instead of recursively merging them. Keep this validation at runtime in TypeScript: a type assertion does not validate an HTTP body.

For genuine dictionaries, consider Map. For third-party merge utilities, keep dependencies patched and review how nested keys are handled. Filtering only __proto__ is insufficient for every merge algorithm; paths through constructor and prototype also matter. A Node.js prototype-disabling flag is additional protection, not a substitute for an input contract.

Regression test

In a local unit test, accept {theme: 'dark', compact: true} and default an empty object. Reject an array, an unknown key, a nested theme, and parsed JSON containing a reserved property name. Assert Object.getPrototypeOf(result) === null and that Object.prototype remains unchanged. These tests cover this preferences function, not all object operations in the application.

Related: mass assignment, dependency security, and JavaScript and other language guides.

Reference: CWE-1321 — improperly controlled modification of object prototype attributes.