A AegiFlow
HIGHCVSS 8.4

CVE-2026-114604

GraphQL Tools: Prototype pollution in mergeIncrementalResult via non-primitive path segments

Published
2026-10-22
Modified
2026-10-22
Aliases
GHSA-c4qx-w738-7hr3
Sources
github-advisory

Summary

## Report metadata | Field | Value | | --- | --- | | Package | `@graphql-tools/utils` (npm, `pkg:github/ardatan/graphql-tools`) | | Repository | https://github.com/ardatan/graphql-tools | | Component | `mergeIncrementalResult` / `setObjectKeyPath` (`packages/utils/src/mergeIncrementalResult.ts`) | | Vulnerability class | Prototype pollution (via unvalidated object path traversal) | | Verified against | Source read at `134d67ae36a559651f43ec7fe4a9da76f4a00c2b`; `master` HEAD `ccca0ff0dc8ea20726fd116b2f19a38709c30e62`; published npm builds 10.10.0, 10.10.1, 11.0.0, 12.0.0, 12.0.1 | | Affected versions | `@graphql-tools/utils` >= 10.10.1 through the scanned 12.0.0 source; introduced by `fbb58b51c4993ba9680ce6c2ef7203ab3a142713` ("remove dset dependency"). 12.0.1 also reproduces. 10.10.0 does not. | | API surface | Public, documented export (`packages/utils/src/index.ts:55`) | | Affected in default configuration | Yes — no options are involved; the forbidden-key check in `setObjectKeyPath` is the only defense and it is on by default | ## Summary `mergeIncrementalResult` merges an incremental GraphQL response part into an accumulated execution result by walking `['data', ...incrementalResult.path]` and assigning at the resolved location. `setObjectKeyPath` tries to reject dangerous segments with strict string equality against `'__proto__'`, `'constructor'` and `'prototype'` (lines 50-52 and 60-62), but it never validates that a path segment is a primitive string or number. A JSON path segment such as `["__proto__"]` is an array object, so `["__proto__"] !== '__proto__'` and the guard passes; the very next statement does `current[key]`, whose `ToPropertyKey` coercion turns that array into the string key `"__proto__"` and resolves `Object.prototype`. The following, innocuous-looking segment is then assigned directly onto `Object.prototype`. In a consuming app, `incrementalResult` is the response part being merged — data parsed from an upstream `@defer`/`@stream` multipart or incremental-delivery response from another GraphQL server or transport. The `ExecutionResult` TypeScript annotation is not a runtime check, so a malicious, compromised, or MITM'd upstream can choose the `path` value and therefore choose global prototype keys and values in the gateway or client process. Comparable paths through the same helper, for contrast: - Plain string segment `"__proto__"` -> blocked by strict equality, lines 50-52 (confirmed by a control case in testing) - Array segment `["__proto__"]` -> **unsafe: passes the guard at lines 50-52, then coerced to the `__proto__` key at lines 53-57** - Same operation in 10.10.0 and earlier, which used `dset` -> not exploitable (0/5 attack payloads succeeded) ## Impact A malicious incremental GraphQL response can add arbitrary properties with attacker-chosen values to `Object.prototype` in the consuming Node.js process. Because every plain object inherits from it, this can change authorization and configuration checks and the behavior of unrelated code elsewhere in the process — a networked PoC set `Object.prototype.isAdmin = true` and `Object.prototype.ownerEmail`, after which a consumer-style check `opts.isAdmin === true` on a freshly created `{}` returned `true`. The delta over the safe behaviour: without this traversal flaw, a hostile upstream response can only influence the GraphQL data returned to the caller — a bounded, in-band effect. With `path: [["__proto__"], "isAdmin"]`, the write lands on `Object.prototype`, escaping the response object entirely and affecting objects created later and code that never touches the response. Scoring note: **this report deliberately carries no CVSS vector.** No score was produced for it, and exploitability depends on the consuming application's data flow — specifically whether it merges incremental results from an upstream it does not control, and how much it relies on inherited defaults. Consumers who are **not** affected: anything on `@graphql-tools/utils`

Affected packages

EcosystemPackageAffected versionsFixed versions
npm@graphql-tools/utils—12.0.2

Remediation: Upgrade to 12.0.2 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.

CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.