Secrets

Prototype Pollution in JavaScript: How It Works and How to Prevent It

Understanding the JavaScript-specific vulnerability that can lead to RCE

What Is Prototype Pollution

Prototype pollution is a JavaScript vulnerability where an attacker modifies the prototype of a base object (Object.prototype), injecting properties that are inherited by all objects in the application. Because JavaScript uses prototypal inheritance, any property added to Object.prototype becomes accessible on every object unless explicitly overridden.

Mapped to CWE-1321 and classified under OWASP A03:2021 – Injection, prototype pollution can lead to denial of service, authentication bypass, or remote code execution depending on how the polluted properties are used downstream in the application.

The vulnerability arises from JavaScript's dynamic property assignment and prototype chain lookup. When code recursively merges objects or sets properties using attacker-controlled keys, the special keys __proto__, constructor, and prototype can modify the prototype chain rather than the target object.

How the Prototype Chain Is Exploited

Prototype pollution typically occurs through unsafe object merging or property assignment functions:

// Vulnerable deep merge function
function merge(target, source) {
  for (const key in source) {
    if (typeof source[key] === 'object') {
      if (!target[key]) target[key] = {};
      merge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
}

// Attacker-controlled input
const malicious = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, malicious);

// Now EVERY object has isAdmin = true
const user = {};
console.log(user.isAdmin); // true

The attacker sets __proto__ as a key, which the merge function follows into Object.prototype, adding isAdmin to the prototype of all objects.

Exploitation Scenarios

  • Authentication bypass: If the application checks user.isAdmin without hasOwnProperty, polluting the prototype with isAdmin: true grants admin access to all users
  • Denial of service: Polluting toString or valueOf on the prototype crashes the application when objects are coerced to strings
  • Remote code execution: In server-side JavaScript, polluting properties consumed by template engines (e.g., Handlebars, Pug) or child process spawning can lead to RCE

Real-World Prototype Pollution CVEs

  • Lodash (CVE-2020-8203): The zipObjectDeep function allowed prototype pollution through crafted property paths. Lodash has over 25 million weekly npm downloads, making this one of the most impactful JavaScript CVEs.
  • jQuery (CVE-2019-11358): The $.extend(true, {}, malicious) deep merge function was vulnerable to prototype pollution in jQuery versions before 3.4.0.
  • Handlebars (CVE-2021-23369): Prototype pollution in the template compiler led to remote code execution when processing attacker-controlled templates.
  • minimist (CVE-2020-7598): The popular argument parser (used by thousands of CLI tools) was vulnerable to prototype pollution through crafted command-line arguments.

These CVEs demonstrate that prototype pollution affects foundational JavaScript libraries used across millions of projects.

How CodeSlick Detects Prototype Pollution

CodeSlick identifies prototype pollution patterns in JavaScript and TypeScript codebases:

  • Recursive object merge functions that do not filter __proto__, constructor, and prototype keys
  • Dynamic property assignment using bracket notation with user-controlled keys (obj[userInput] = value)
  • Path-based property setting functions (e.g., set(obj, path, value)) without path sanitization

Findings are mapped to CWE-1321 with CVSS severity scoring. CodeSlick's AI-powered fixes suggest safe alternatives including Object.create(null), key allowlisting, and property descriptor freezing.

Detect prototype pollution vulnerabilities in your JavaScript and TypeScript code automatically.

Frequently Asked Questions

Related Guides

Prototype Pollution in JavaScript: How It Works and How to Prevent It | CodeSlick Security Scanner