Skip to main content

flat-to-nested EUVDEUVD-2026-77691

| CVE-2026-55091 HIGH
Improperly Controlled Modification of Dynamically-Determined Object Attributes (CWE-915)
2026-06-19 https://github.com/joaonuno/flat-to-nested-js GHSA-hp36-v28f-w3r4
7.5
CVSS 3.1 · Vendor: https://github.com/joaonuno/flat-to-nested-js
Share

Severity by source

Vendor (https://github.com/joaonuno/flat-to-nested-js) PRIMARY
7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
vuln.today AI
8.2 HIGH

Library is typically reached via network-fed records with no auth or interaction; integrity is high (arbitrary prototype writes), with low availability impact from likely logic crashes and no direct confidentiality loss.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/joaonuno/flat-to-nested-js).

CVSS VectorVendor: https://github.com/joaonuno/flat-to-nested-js

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
None

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 19, 2026 - 21:38 vuln.today
Analysis Generated
Jun 19, 2026 - 21:38 vuln.today

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 91 npm packages depend on flat-to-nested (11 direct, 82 indirect)

Ecosystem-wide dependent count for version 1.1.2.

DescriptionCVE.org

Summary

convert() builds the nested tree by using each flat record's id and parent field values directly as object keys, with no guard against __proto__ / constructor / prototype. A record whose parent is the string "__proto__" makes temp[parent] resolve to Object.prototype, and the following initPush(...) writes attacker-controlled data onto the global prototype. Any application that passes attacker-influenced records to convert() is affected, and the base prototype methods stay intact so the pollution is stealthy.

Details

In index.js, convert() (FlatToNested.prototype.convert):

  • temp = {} (line 45) and pendingChildOf = {} (line 46) are plain objects, so they inherit from Object.prototype.
  • For each record, parent = flatEl[this.config.parent] (line 51) is taken verbatim from input.
  • Line 57: if (temp[parent] ! undefined) - when parent = "__proto__", temp["__proto__"] resolves via the prototype chain to Object.prototype, which is !== undefined, so the

branch is taken.

  • Line 59: initPush(this.config.children, temp[parent], flatEl) → effectively initPush("children", Object.prototype, flatEl).
  • initPush (lines 4-9): Object.prototype["children"] = [] then Object.prototype["children"].push(flatEl) - attacker-controlled data is written onto the global Object.prototype.

There is no sanitization of id / parent anywhere; they flow straight into temp[id], temp[parent], and pendingChildOf[parent] as dynamic keys.

PoC

js
  const FlatToNested = require('flat-to-nested');

  new FlatToNested().convert([
    { id: 1, parent: '__proto__', polluted: 'PWNED' }
  ]);

  console.log(({}).children); // => [ { id: 1, polluted: 'PWNED' } ]
  A freshly-created, unrelated object {} now carries an attacker-controlled children property. ({}).toString === Object.prototype.toString remains true, so existing methods are untouched (stealthy). If the consumer configures a custom children key, that arbitrary prototype property is polluted instead.

Impact

Prototype pollution (CWE-1321). Any service that builds a tree from attacker-influenced flat records (the package's core purpose - e.g. records derived from a DB/REST/user input) can have Object.prototype polluted. Consequences range from application-logic corruption and denial of service to serving as a gadget toward privilege escalation or RCE depending on downstream sinks. No special privileges or user interaction required; the malicious value is ordinary input data.

Suggested fix

Use prototype-less lookup tables so inherited keys like __proto__ cannot be reached: var temp = Object.create(null); var pendingChildOf = Object.create(null); (Optionally also reject id/parent values equal to __proto__, constructor, or prototype.) Verified: with Object.create(null) for both temp and pendingChildOf, the PoC no longer pollutes Object.prototype and normal nesting output is unchanged. A patch with a regression test is ready.

AnalysisAI

Prototype pollution in the npm package flat-to-nested (versions <= 1.1.1) lets attackers who control input records pollute Object.prototype by supplying a record with parent set to the string '__proto__'. The convert() function uses plain objects as lookup tables and writes attacker-controlled data through the prototype chain via initPush(), affecting any application that feeds untrusted flat records (REST input, DB rows, user-supplied data) into the library. No CISA KEV listing and no public exploit identified at time of analysis beyond the proof-of-concept embedded in the GitHub Security Advisory.

Technical ContextAI

flat-to-nested is a small JavaScript utility (pkg:npm/flat-to-nested) that converts flat parent/child records into a nested tree. In index.js, convert() initializes temp = {} and pendingChildOf = {} as plain objects that inherit from Object.prototype, then uses each record's id and parent fields directly as dynamic object keys without any allowlist or denylist. When parent === '__proto__', temp['__proto__'] resolves to Object.prototype and initPush() writes children data onto the global prototype. The root cause class is CWE-1321 / CWE-915 (improperly controlled modification of object prototype attributes), a well-known JavaScript sink-pattern where unsanitized strings used as keys cross the boundary between own properties and inherited prototype properties.

RemediationAI

Vendor-released patch: upgrade flat-to-nested to 1.1.2 or later, which replaces the plain-object lookup tables with Object.create(null) so that '__proto__', 'constructor', and 'prototype' become harmless own keys rather than prototype-chain references (commit 680a5ebe1194edda16fa93baaa56ff14fe0e3d7f, advisory GHSA-hp36-v28f-w3r4). If upgrading is not immediately possible, pre-sanitize input records before calling convert() by rejecting or stripping any record whose id or parent equals '__proto__', 'constructor', or 'prototype' - note this is a strict allowlist on key strings and will reject any legitimate record that happens to use those exact identifiers. As an additional defense, run the consumer process under Node's --frozen-intrinsics flag or call Object.freeze(Object.prototype) at startup; this blocks the write but will break any library that legitimately augments Object.prototype, so test thoroughly before enabling in production.

Share

EUVD-2026-77691 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy