GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-325J-MG25-8Q58

Vulnerability from github – Published: 2026-09-11 22:11 – Updated: 2026-09-11 22:11
VLAI
Summary
yayson: Prototype pollution in Store/LegacyStore deserialization
Details

Summary

Store/LegacyStore key internal lookup tables by the type, id, and relationship names from a JSON:API document. Because these were plain objects, a document with type: "__proto__" writes onto Object.prototype, polluting every object in the process.

Severity

Suggested CVSS v3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H (8.1). The guaranteed impact is process-wide DoS / logic corruption; escalation to authorization bypass or RCE is dependent on gadgets in the consuming application.

Affected / Patched

  • Affected: <= 4.2.0 (verified in 3.0.0 and 4.2.0; all 3.x and 4.x).
  • Patched: 4.3.0.

Details

type is a string value, untouched by JSON.parse, and is used directly as an object key:

if (!models[type]) models[type] = {}          // models["__proto__"] is Object.prototype → skipped
if (!models[type][id]) models[type][id] = model // → Object.prototype[id] = model

The attacker controls the polluted key (id) and value (the model, populated from attributes). Pollution persists for the process lifetime. The malicious type can also arrive via an included resource referenced by a relationship, bypassing any data.type allow-list. LegacyStore is additionally reachable when a configured types mapping resolves to "__proto__".

Proof of concept

const { Store } = require('yayson')()
new Store().sync({ data: { type: '__proto__', id: 'polluted', attributes: { x: 1 } } })
console.log(({}).polluted) // { x: 1, id: 'polluted' }  ← Object.prototype polluted

Workarounds

Reject documents whose type or relationship names are __proto__, constructor, or prototype, or run Node with --disable-proto=throw.

Fix

Null-prototype lookup tables, rejection of __proto__/constructor/prototype as document-derived member names, Object.keys() iteration, and null-prototype normalization of caller-supplied caches.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.2.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "yayson"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-61534"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1321"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-11T22:11:38Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "# Summary\n`Store`/`LegacyStore` key internal lookup tables by the `type`, `id`, and relationship names from a JSON:API document. Because these were plain objects, a document with `type: \"__proto__\"` writes onto `Object.prototype`, polluting every object in the process.\n\n# Severity\nSuggested CVSS v3.1 `AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H` (8.1). The guaranteed impact is process-wide DoS / logic corruption; escalation to authorization bypass or RCE is dependent on gadgets in the consuming application.\n\n# Affected / Patched\n- Affected: `\u003c= 4.2.0` (verified in `3.0.0` and `4.2.0`; all 3.x and 4.x).\n- Patched: `4.3.0`.\n\n# Details\n`type` is a string *value*, untouched by `JSON.parse`, and is used directly as an object key:\n```js\nif (!models[type]) models[type] = {}          // models[\"__proto__\"] is Object.prototype \u2192 skipped\nif (!models[type][id]) models[type][id] = model // \u2192 Object.prototype[id] = model\n```\nThe attacker controls the polluted key (`id`) and value (the model, populated from `attributes`). Pollution persists for the process lifetime. The malicious type can also arrive via an `included` resource referenced by a relationship, bypassing any `data.type` allow-list. `LegacyStore` is additionally reachable when a configured `types` mapping resolves to `\"__proto__\"`.\n\n# Proof of concept\n```js\nconst { Store } = require(\u0027yayson\u0027)()\nnew Store().sync({ data: { type: \u0027__proto__\u0027, id: \u0027polluted\u0027, attributes: { x: 1 } } })\nconsole.log(({}).polluted) // { x: 1, id: \u0027polluted\u0027 }  \u2190 Object.prototype polluted\n```\n\n# Workarounds\nReject documents whose `type` or relationship names are `__proto__`, `constructor`, or `prototype`, or run Node with `--disable-proto=throw`.\n\n# Fix\nNull-prototype lookup tables, rejection of `__proto__`/`constructor`/`prototype` as document-derived member names, `Object.keys()` iteration, and null-prototype normalization of caller-supplied caches.",
  "id": "GHSA-325j-mg25-8q58",
  "modified": "2026-09-11T22:11:38Z",
  "published": "2026-09-11T22:11:38Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/confetti/yayson/security/advisories/GHSA-325j-mg25-8q58"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/confetti/yayson"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "yayson: Prototype pollution in Store/LegacyStore deserialization"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…