Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-66922

CVE-2026-66922: Pivotick Prototype Pollution DoS Vulnerability

CVE-2026-66922 is a prototype pollution denial-of-service vulnerability in Pivotick's graph processing components. Attackers can exploit crafted node identifiers to corrupt visualizations or crash the application. This article covers the technical details, impact, and mitigation strategies.

Published:

CVE-2026-66922 Overview

CVE-2026-66922 is a prototype pollution vulnerability [CWE-1321] in Pivotick, a JavaScript graph visualization and analytics library. The tree-layout and cycle-detection components used plain JavaScript objects as lookup tables keyed by caller-controlled node identifiers. Node identifiers matching inherited properties such as constructor, toString, or __proto__ were treated as valid keys rather than reserved names. Attackers who supply crafted graph data can silently omit nodes or edges, corrupt hierarchy levels, bypass cycle detection, or trigger exceptions that interrupt rendering.

Critical Impact

Crafted node identifiers can pollute internal prototypes, corrupt graph analytics results, and cause client-side denial of service in applications embedding Pivotick.

Affected Products

  • Pivotick graph visualization library (pre-patch commit 4e12922)
  • src/plugins/analytics/cycle.ts cycle-detection component
  • src/plugins/layout/Tree.ts tree-layout component

Discovery Timeline

  • 2026-07-28 - CVE-2026-66922 published to NVD
  • 2026-07-30 - Last updated in NVD database

Technical Details for CVE-2026-66922

Vulnerability Analysis

The flaw is a classic prototype-pollution pattern [CWE-1321] arising from using plain objects as maps. In hasCycle, the adjacency table adj: Record<string, string[]> was indexed by node.id values supplied by the caller. When a node identifier collides with an inherited property such as toString or constructor, a read like adj[source.id] resolves to the inherited function rather than an own edge list. A subsequent .push() throws, or the edge is skipped, corrupting cycle-detection results.

A node identifier of __proto__ is more damaging: assigning adj[node.id] = [] overwrites the prototype of the internal lookup object, affecting every subsequent property lookup in the graph pipeline.

The Tree layout used a similar levels: Record<string, number> structure, inheriting the same issues. Additionally, the maximum-depth calculation used Math.max(...allLevels), which exceeds the JavaScript function-argument limit on large graphs and throws a RangeError.

Root Cause

JavaScript objects inherit from Object.prototype, so any property access on a plain-object map can resolve to inherited members. Trusting caller-controlled strings as keys allows attackers to conflate own properties with inherited ones and to write to __proto__.

Attack Vector

An attacker who can supply graph data to a Pivotick-based application, for example through an imported dataset or a rendered API response, can craft node identifiers that manipulate the internal lookup tables. User interaction is required to load the malicious graph, but no authentication is needed.

typescript
// Patch: src/plugins/analytics/cycle.ts
export default function hasCycle(nodes: Node[], edges: Edge[]): boolean {
-    const adj: Record<string, string[]> = {}
+    // A Map, not a plain object: node ids come from the caller, and one called `constructor` or
+    // `toString` would otherwise resolve to an inherited property instead of its own edge list.
+    const adj = new Map<string, string[]>()
     for (const node of nodes) {
-        adj[node.id] = []
+        adj.set(node.id, [])
     }
     for (const { source, target } of edges) {
-        if (!adj[source.id])
-            adj[source.id] = []
-        adj[source.id].push(target.id)
+        const list = adj.get(source.id)
+        if (list) list.push(target.id)
+        else adj.set(source.id, [target.id])
     }

     const visited = new Set<string>()

Source: Pivotick commit 4e12922

Detection Methods for CVE-2026-66922

Indicators of Compromise

  • Graph datasets containing node identifiers equal to __proto__, constructor, prototype, toString, hasOwnProperty, or valueOf.
  • Client-side exceptions such as TypeError: adj[source.id].push is not a function or RangeError: Maximum call stack size exceeded originating from Pivotick modules.
  • Rendered graphs that silently omit nodes or edges present in the source data, or cycle-detection results that disagree with a known-good implementation.

Detection Strategies

  • Inspect graph inputs at the application boundary and reject or normalize node IDs matching Object.prototype member names.
  • Add integration tests that feed a corpus containing polluting identifiers and assert that node and edge counts are preserved.
  • Monitor front-end error telemetry for exceptions originating from cycle.ts and Tree.ts stack frames.

Monitoring Recommendations

  • Enable client-side error reporting to capture TypeError and RangeError conditions during graph rendering.
  • Track the Pivotick dependency version in software composition analysis (SCA) tooling and alert on installations below the patched commit 4e12922.
  • Correlate anomalous graph-rendering failures with the identity of the user or dataset that supplied the graph data.

How to Mitigate CVE-2026-66922

Immediate Actions Required

  • Upgrade Pivotick to a release that includes commit 4e12922627029af77476c6f1ab8a14e98d5ef451 or later.
  • Audit application code paths that pass externally sourced graph data into Pivotick and add server-side validation of node identifiers.
  • Rebuild and redeploy any bundled front-end artifacts that vendor the vulnerable Pivotick source.

Patch Information

The upstream fix is published in the Pivotick security commit 4e12922. It replaces identifier-keyed plain objects with Map instances in both src/plugins/analytics/cycle.ts and src/plugins/layout/Tree.ts, ignores edges whose source node is absent from the node set, and calculates the maximum tree depth iteratively instead of via Math.max(...levels).

Workarounds

  • Sanitize inbound graph payloads by rejecting node IDs that match members of Object.prototype, for example __proto__, constructor, prototype, toString, hasOwnProperty, and valueOf.
  • Where feasible, coerce user-supplied identifiers to a namespaced form such as n_<uuid> before passing them to Pivotick.
  • Cap the maximum node count accepted from untrusted sources to reduce exposure to the Math.max argument-limit denial of service.
bash
# Example jq filter to strip prototype-polluting node IDs before rendering
jq '.nodes |= map(select(.id | test("^(__proto__|constructor|prototype|toString|hasOwnProperty|valueOf)$") | not))' input.json > sanitized.json

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.