Safeguard
Security

CVE-2021-3918: Prototype Pollution in json-schema Explained

CVE-2021-3918 is a prototype pollution vulnerability in the json-schema npm package that can let crafted input tamper with JavaScript object prototypes. Here is who is affected and how to fix it.

Aisha Rahman
Security Analyst
5 min read

CVE-2021-3918 is a prototype pollution vulnerability in the json-schema npm package below version 0.4.0, where crafted schema input can modify the Object prototype and affect every object in the running application. It is rated critical, with a CVSS 3.1 base score of 9.8, largely because prototype pollution can escalate from data tampering to denial of service or, in the worst case, remote code execution depending on how the polluted prototype is later used. The catch is that almost nobody installs json-schema directly; it arrives deep in the dependency tree.

What prototype pollution is

In JavaScript, nearly every object inherits from Object.prototype. If an attacker can set a property on that shared prototype, that property suddenly appears on every object in the program that does not explicitly override it. The usual vehicle is a special key like __proto__ or constructor.prototype that some code path treats as an ordinary property name during a merge, copy, or assignment.

// Illustration of the concept
const payload = JSON.parse('{"__proto__": {"polluted": true}}');
mergeDeep({}, payload);       // vulnerable merge writes to the prototype
console.log(({}).polluted);   // true — every object now carries it

Once the prototype is polluted, downstream code that checks for a property, if (options.isAdmin), for example, can be tricked into seeing an attacker-controlled value it never intended to set. That is why prototype pollution scores so high: its impact depends entirely on what the polluted property later influences, and in complex applications that surface is large.

What CVE-2021-3918 does specifically

The json-schema package validates data against JSON Schema definitions and can apply schema defaults and type coercion. In versions before 0.4.0, the property-iteration logic in the validation routine processed __proto__ as a regular property while handling schema defaults and coercion. A specially crafted schema could therefore set values on the object prototype during validation, which is textbook prototype pollution. Depending on how the host application consumed the results, that could lead to denial of service through triggered exceptions or, in some configurations, tampering that redirects code execution.

Affected and fixed versions

The vulnerability affects json-schema versions before 0.4.0. Version 0.4.0 contains the fix, which stops the validation logic from treating __proto__ as an assignable property.

The remediation is a straightforward upgrade to 0.4.0 or later. The complication is not the fix; it is finding where the vulnerable version is hiding.

Finding it in your dependency tree

json-schema is a transitive dependency for a very large number of projects. It has historically been pulled in through tooling like AWS SDK components, jsprim, and a long chain of build and validation utilities. You almost certainly did not type it into your package.json. Ask npm where it comes from:

# Show every path that resolves json-schema and its version
npm ls json-schema

If the reported version is below 0.4.0, you are exposed. npm audit will typically flag it too:

npm audit

Remediating CVE-2021-3918

Because the package is transitive, the cleanest fix is often to force the resolved version rather than chase the intermediate dependency that requests it. With npm 8.3 or later, use an overrides block in package.json:

{
  "overrides": {
    "json-schema": "0.4.0"
  }
}

With Yarn, use the resolutions field:

{
  "resolutions": {
    "json-schema": "0.4.0"
  }
}

After adding the override, reinstall and re-run npm ls json-schema to confirm every path now resolves 0.4.0 or later. Then run your test suite, because forcing a transitive version occasionally surfaces an incompatibility, though in this case the 0.3.x to 0.4.0 change is a targeted security fix with minimal behavioral impact. Where possible, also upgrade the direct dependency that pulled in the old json-schema, since that removes the need for a permanent override.

Preventing prototype pollution more broadly

CVE-2021-3918 is one instance of a recurring class. You can harden against the category regardless of any single CVE:

  • Use Object.create(null) for objects that hold untrusted key-value data, so there is no prototype to pollute.
  • Prefer Map over plain objects for user-controlled keys.
  • Freeze the prototype with Object.freeze(Object.prototype) at startup if your application can tolerate it.
  • Validate and reject keys like __proto__ and constructor in any recursive merge you write yourself.

The dependency-management side is where most teams get burned, though, because the vulnerable code was never something they chose. Continuous software composition analysis resolves the full graph and flags a transitive json-schema below 0.4.0 even when your own manifest looks clean, and a CI policy gate stops a regression from silently reintroducing it. An SCA tool such as Safeguard can surface the exact resolution path so you know which direct dependency to upgrade. For more on how transitive risk propagates, see our note on tracking indirect dependencies.

FAQ

What is the CVSS score of CVE-2021-3918?

CVE-2021-3918 carries a CVSS 3.1 base score of 9.8, rated Critical. Prototype pollution can escalate to denial of service or, depending on usage, code execution, which drives the high score.

What version of json-schema fixes CVE-2021-3918?

Version 0.4.0 contains the fix. Upgrade to 0.4.0 or later, using an npm overrides or Yarn resolutions entry if the package is transitive.

I never installed json-schema, why is it in my project?

It is almost always a transitive dependency pulled in by build tools, SDKs, and validation libraries. Run npm ls json-schema to see which of your direct dependencies brings it in and at what version.

Is prototype pollution always exploitable for code execution?

No. Its impact depends on how the application uses the polluted property afterward. Many cases result only in data tampering or denial of service, but some configurations can escalate to remote code execution, which is why it is treated as high risk.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.