Back to Blog
high SEVERITY6 min read

How Denial of Service via Infinite Loop happens in JavaScript (nanoid) and how to fix it

A high-severity denial of service vulnerability (CVE-2026-67213) was discovered in nanoid versions before 5.1.6 and 3.3.18, where the `customAlphabet` function could enter an infinite loop during random ID generation. The fix upgrades the transitive nanoid dependency from 3.3.16 to 3.3.18 using pnpm overrides, ensuring the vulnerable code path is eliminated from the entire dependency tree including PostCSS.

O
By Orbis AppSec
Published August 22, 2026Reviewed August 22, 2026

Answer Summary

CVE-2026-67213 is a Denial of Service vulnerability in the JavaScript nanoid package (CWE-835: Loop with Unreachable Exit Condition) where the `customAlphabet` function can enter an infinite loop during random ID generation. The fix is to upgrade nanoid to version 3.3.18 (or 5.1.6 for the v5 line) by adding a pnpm override in `pnpm-workspace.yaml` to force the patched version across all transitive dependencies including PostCSS.

Vulnerability at a Glance

cweCWE-835
fixUpgrade nanoid from 3.3.16 to 3.3.18 using pnpm overrides to force resolution across the dependency tree
riskApplication hang or crash via CPU exhaustion during ID generation
languageJavaScript/Node.js
root causenanoid's customAlphabet function contained a loop with an unreachable exit condition under certain inputs
vulnerabilityDenial of Service via Infinite Loop

Introduction

In the aas-web-ui project, a high-severity Denial of Service vulnerability was lurking in the dependency tree—not in the application's own code, but in a transitive dependency pulled in by PostCSS. The pnpm-lock.yaml file pinned nanoid@3.3.16 as a dependency of postcss@8.5.25, and this version of nanoid contained a critical flaw: its customAlphabet function could enter an infinite loop during random ID generation, effectively freezing any process that triggered it.

This is a textbook example of supply chain risk. The application developers never explicitly chose nanoid 3.3.16—it was resolved automatically as a transitive dependency. Yet the vulnerability was real, exploitable, and capable of taking down the entire web UI build process or any runtime ID generation.

The Vulnerability Explained

What is nanoid?

Nanoid is one of the most popular JavaScript libraries for generating unique, URL-friendly string IDs. It's used by millions of projects, often indirectly through tools like PostCSS (which uses it to generate unique class identifiers during CSS processing).

The Infinite Loop Bug

CVE-2026-67213 affects nanoid's customAlphabet function—the API that allows developers to generate IDs from a custom set of characters. In versions before 3.3.18 (v3 line) and 5.1.6 (v5 line), specific inputs to this function could trigger a loop with an unreachable exit condition.

Here's the critical dependency chain in the vulnerable lockfile:

postcss@8.5.25:
  dependencies:
    nanoid: 3.3.16
    picocolors: 1.1.1
    source-map-js: 1.2.1

The nanoid@3.3.16 resolution was explicitly present:

nanoid@3.3.16:
  resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
  engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
  hasBin: true

How Could It Be Exploited?

In the context of aas-web-ui, consider these attack scenarios:

  1. Build-time DoS: If the application uses PostCSS during build (which it does—PostCSS is a direct dependency), and a crafted CSS input triggers nanoid's customAlphabet with problematic parameters, the build process hangs indefinitely. In a CI/CD pipeline, this means deployments stall.

  2. Runtime DoS: If any part of the application calls nanoid's customAlphabet at runtime (e.g., for generating session tokens, unique component IDs, or request identifiers), a malicious input that influences the alphabet or size parameters could freeze the Node.js event loop entirely.

  3. Supply chain amplification: Since nanoid is used by PostCSS, which is used by virtually every modern web application, this vulnerability has an enormous blast radius across the JavaScript ecosystem.

The infinite loop consumes 100% of a CPU core with no timeout or escape mechanism, making it a reliable denial of service vector.

The Fix

The fix involves three coordinated changes that force the entire dependency tree to use the patched nanoid@3.3.18:

1. Adding pnpm overrides in pnpm-workspace.yaml

# Before: No overrides
packages:
    - '.'

# After: Force nanoid resolution
packages:
    - '.'

overrides:
  "nanoid@": 3.3.18

This is the most critical change. The overrides field in pnpm tells the package manager: "No matter what version any package in the tree requests for nanoid, resolve it to 3.3.18." This catches transitive dependencies like PostCSS that would otherwise continue pulling in the vulnerable version.

2. Updating pnpm-lock.yaml

The lockfile changes accomplish three things:

Removing the vulnerable version entry:

# Removed
-  nanoid@3.3.16:
-    resolution: {integrity: sha512-bzlKTyNJ7+...}
-    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
-    hasBin: true

Removing the vulnerable snapshot:

# Removed
-  nanoid@3.3.16: {}

Updating PostCSS's dependency resolution:

# Before
postcss@8.5.25:
  dependencies:
    nanoid: 3.3.16

# After
postcss@8.5.25:
  dependencies:
    nanoid: 3.3.18

3. Adding the override declaration in the lockfile header

overrides:
  nanoid@: 3.3.18

This ensures the lockfile is self-consistent and any pnpm install will respect the override even if workspace configuration is parsed differently.

Why Each File Change Was Necessary

File Purpose
pnpm-workspace.yaml Declares the override policy for the workspace
pnpm-lock.yaml Materializes the override by removing 3.3.16 and rewiring PostCSS to 3.3.18
package.json Ensures the override is recognized at the project level

Key Takeaways

  • Transitive dependencies are attack surface: nanoid was never directly imported by aas-web-ui, yet it exposed the application to a high-severity DoS through PostCSS's dependency on nanoid@3.3.16.
  • pnpm overrides are essential for supply chain security: Without the "nanoid@": 3.3.18 override in pnpm-workspace.yaml, PostCSS would have continued resolving to the vulnerable version regardless of any direct dependency updates.
  • Infinite loops in ID generation libraries are particularly dangerous: nanoid's customAlphabet is called frequently and often in hot paths—a single infinite loop call freezes the entire Node.js event loop.
  • Lockfile integrity matters: The fix required removing both the resolution entry AND the snapshot for nanoid@3.3.16 to ensure no install path could accidentally resolve the old version.
  • SCA tools catch what code review misses: No amount of application code review would have revealed this vulnerability—it required scanning the dependency tree against CVE databases.

How Orbis AppSec Detected This

  • Source: The pnpm-lock.yaml dependency resolution for postcss@8.5.25, which transitively pulled in nanoid@3.3.16 as a required dependency for CSS class ID generation.
  • Sink: nanoid's customAlphabet() function internal loop, which under specific input conditions enters an infinite loop with no exit condition (CWE-835).
  • Missing control: No pnpm override existed to force the patched nanoid version across transitive dependencies; the lockfile allowed resolution of the known-vulnerable 3.3.16 version.
  • CWE: CWE-835 (Loop with Unreachable Exit Condition)
  • Fix: Added "nanoid@": 3.3.18 override in pnpm-workspace.yaml and updated the lockfile to eliminate all references to the vulnerable nanoid@3.3.16 version.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-67213 is a stark reminder that modern JavaScript applications are only as secure as their deepest transitive dependency. A single vulnerable version of nanoid—pulled in automatically by PostCSS—could have allowed an attacker to freeze the application's build pipeline or runtime with a carefully crafted input that triggers an infinite loop.

The fix was surgical: a pnpm override forcing nanoid@3.3.18 across the entire dependency tree, combined with lockfile cleanup to ensure no installation path could resolve the vulnerable version. This pattern—using package manager overrides to patch transitive vulnerabilities—is a critical tool in every JavaScript developer's security toolkit.

Keep your dependencies scanned, your overrides intentional, and your build pipelines protected with timeouts. Supply chain security isn't optional—it's the foundation your application stands on.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1482

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.