Skip to content

Prototype pollution in jsonld.flatten() when processing a node with @id __proto__ #584

Description

@Dremig

Description

jsonld.flatten() appears to be vulnerable to prototype pollution when processing a JSON-LD document that contains a node whose @id is __proto__.

When flattening the document, the internal node map uses the node identifier as an object key. If the identifier is __proto__, the lookup resolves to Object.prototype, and subsequent JSON-LD properties are written onto the global object prototype.

This is reachable through the public jsonld.flatten() API and does not require using internal package paths.

Affected Version

Tested with:

  • jsonld@9.0.0
  • Node.js v24.10.0

Proof of Concept

const jsonld = require("jsonld");

(async () => {
  delete Object.prototype["@id"];
  delete Object.prototype["http://example.com/polluted"];

  const doc = {
    "@context": {
      polluted: "http://example.com/polluted"
    },
    "@id": "__proto__",
    polluted: "yes"
  };

  await jsonld.flatten(doc);

  console.log("Object.prototype[@id]:", JSON.stringify(Object.prototype["@id"]));
  console.log(
    "Object.prototype[http://example.com/polluted]:",
    JSON.stringify(Object.prototype["http://example.com/polluted"])
  );
  console.log(
    "plain object inherited polluted IRI:",
    JSON.stringify(({})["http://example.com/polluted"])
  );

  delete Object.prototype["@id"];
  delete Object.prototype["http://example.com/polluted"];
})();

Observed output:

Object.prototype[@id]: "__proto__"
Object.prototype[http://example.com/polluted]: [{"@id":"__proto__"}]
plain object inherited polluted IRI: [{"@id":"__proto__"}]

Impact

This is a prototype pollution issue in the JSON-LD flattening path.

The impact depends on how applications process untrusted JSON-LD documents and whether other code in the same process is sensitive to inherited properties. Possible effects include logic corruption, unexpected inherited values, denial of service, or gadget-dependent escalation.

The pollution is not an arbitrary short-key pollution primitive like isAdmin = true; the polluted keys are JSON-LD-related keys such as @id and expanded IRIs derived from the document context. However, the input is a normal JSON-LD document and the affected API is commonly used to process external JSON-LD data.

Expected Behavior

Processing a JSON-LD document with @id: "__proto__" should not write to Object.prototype.

Possible fixes include:

  • storing node map entries in null-prototype objects;
  • rejecting or safely escaping prototype-related identifiers such as __proto__, constructor, and prototype;
  • checking own properties explicitly instead of allowing inherited prototype lookups to become node map entries.

Root Cause

The issue appears to come from the node map construction during flattening. A subject is stored and retrieved by identifier using a normal JavaScript object. When the identifier is __proto__, the object lookup reaches the inherited prototype instead of a safe own property slot, and later writes mutate Object.prototype.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions