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
•Technically reviewed by Anupam Mediratta•Published August 22, 2026•Reviewed 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

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.