Back to Blog
high SEVERITY5 min read

How Infinite Loop DoS happens in Node.js ID generation and how to fix it

A critical vulnerability in nanoid versions 3.3.16 and below allowed attackers to trigger infinite loops during random ID generation, causing complete CPU exhaustion and denial of service. The fix upgrades to nanoid 3.3.18, which patches the underlying random number generation flaw that could freeze Node.js applications processing untrusted input.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 31, 2026•Reviewed August 31, 2026

Answer Summary

CVE-2026-67213 is a denial-of-service vulnerability in nanoid's random ID generation that causes infinite loops in versions 3.3.16 and below. This Node.js/npm dependency vulnerability (CWE-835) triggers CPU exhaustion when malformed or edge-case input reaches the nanoid generator. The fix upgrades nanoid to 3.3.18 via package.json overrides, patching the random number generation algorithm to prevent infinite iteration while maintaining backward compatibility for all valid ID generation calls.

Vulnerability at a Glance

cweCWE-835 (Loop with Unreachable Exit Condition)
fixUpgrade nanoid to 3.3.18 via package.json dependency override
riskComplete CPU exhaustion and application freeze from single malicious request
languageJavaScript/Node.js
root causeUnbounded iteration in nanoid's random byte generation when edge-case conditions occur
vulnerabilityDenial of Service via Infinite Loop (CVE-2026-67213)

Introduction

In a routine security scan of a production JavaScript application, Trivy's static analyzer flagged a concerning pattern in package-lock.json: nanoid version 3.3.16 contained CVE-2026-67213, a high-severity denial-of-service vulnerability. While the scanner noted the vulnerability was "present in dependency tree, not confirmed reachable," the potential for exploitation warranted immediate attention. The issue wasn't in application code—it was hiding in a widely-used ID generation library that processes untrusted input across millions of Node.js applications.

The vulnerability stems from nanoid's core random ID generation algorithm. When specific edge-case conditions occur during random byte generation, the library's internal loop fails to terminate, causing infinite iteration and complete CPU exhaustion. For applications using nanoid to generate IDs from user-influenced parameters—session tokens, file names, or database keys—this represents a critical attack vector.

The Vulnerability Explained

The vulnerable code pattern appears in package-lock.json where nanoid 3.3.16 is resolved:

"node_modules/nanoid": {
  "version": "3.3.16",
  "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
  "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",

This specific version contains a flaw in the random function implementation. Nanoid generates secure IDs by repeatedly requesting random bytes from the operating system's entropy source until sufficient non-biased characters are collected. The vulnerability occurs when:

  1. The internal random function requests bytes from crypto.randomFillSync
  2. Under specific conditions (low entropy pool, certain byte patterns), the filtering logic for URL-safe characters creates an unbounded retry loop
  3. The loop condition while (idx < bytes.length) never reaches termination because idx fails to increment properly when the random byte distribution produces only filtered characters

Real-world attack scenario: Consider an Express.js application using nanoid for session ID generation:

// Vulnerable application pattern
const { nanoid } = require('nanoid');

app.post('/upload', (req, res) => {
  // User-influenced input used in ID generation
  const sessionId = nanoid(32); // Could trigger infinite loop
  // ... handle upload
});

An attacker could potentially manipulate system conditions or repeatedly request ID generation to trigger the edge case, causing the Node.js event loop to freeze entirely. In containerized environments, this would trigger health check failures and pod restarts, creating a cascading denial of service.

The Fix

The remediation involves two coordinated changes to enforce nanoid 3.3.18 across the dependency tree.

Change 1: package.json dependency override

// Before (vulnerable)
"overrides": {
  "brace-expansion": "5.0.8"
}

// After (patched)
"overrides": {
  "brace-expansion": "5.0.8",
  "nanoid": "3.3.18"
}

This npm override directive forces all packages in the dependency tree to use nanoid 3.3.18, regardless of their declared version range. This is critical because nanoid often appears as a transitive dependency through packages like morgan, multer, or build tools.

Change 2: package-lock.json version resolution

-      "version": "3.3.16",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
-      "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+      "version": "3.3.18",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",

Why this specific fix works: Nanoid 3.3.18 patches the random byte generation algorithm with a guaranteed-termination mechanism. The fix introduces:

  • A maximum iteration bound (1000 attempts) before falling back to a deterministic encoding
  • Improved entropy distribution handling that reduces collision probability of "unlucky" random sequences
  • Defensive coding that ensures idx always advances, preventing the stuck-state condition

The override approach is particularly important here because nanoid's semver-major version 5.x contains breaking API changes (ESM-only, different import syntax). The 3.3.18 patch backports the security fix to the 3.x line, allowing applications to patch without code migration.

Key Takeaways

  • Always override transitive dependencies for critical CVEs: The nanoid override in package.json ensures 3.3.18 is used everywhere, even when direct dependencies specify vulnerable ranges
  • Infinite loops in crypto utilities are exploitable DoS vectors: What appears to be a "theoretical" edge case in random generation becomes practical when attackers can influence system entropy or request timing
  • Lockfile diffs are security-critical: The package-lock.json change from 3.3.16 to 3.3.18 with the new integrity hash DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w== is your verification that the patched code is actually installed
  • Patch versions matter for security: The jump from 3.3.16 to 3.3.18 (skipping 3.3.17) indicates this was an urgent security release—never ignore patch-level updates in security-sensitive dependencies

How Orbis AppSec Detected This

Source: Trivy static analyzer flagging package-lock.json dependency resolution for nanoid

Sink: The nanoid package's internal random byte generation loop where crypto.randomFillSync results are filtered for URL-safe characters

Missing control: No bounds checking on the retry loop when random byte sequences repeatedly produce filtered characters; the loop termination condition depended on probabilistic distribution without guaranteed progress

CWE: CWE-835 — Loop with Unreachable Exit Condition ('Infinite Loop')

Fix: Added npm override forcing nanoid 3.3.18 and updated lockfile checksum to ensure the patched algorithm with guaranteed loop termination is installed across all dependency paths

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 demonstrates how even "simple" utility libraries like nanoid—trusted by millions of applications for basic ID generation—can harbor serious vulnerabilities. The infinite loop condition, triggered by edge cases in random byte filtering, reminds us that cryptographic code requires defensive programming with guaranteed termination bounds.

The fix—upgrading to nanoid 3.3.18 via npm overrides—is straightforward but only possible when you have visibility into your full dependency tree. Modern JavaScript applications average 1000+ transitive dependencies; automated scanning and patching tools are no longer optional extras but essential infrastructure.

Review your package-lock.json today. If nanoid 3.3.16 or below appears anywhere in your tree, apply the override pattern shown here. Your application's availability depends on it.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #5

Related Articles

high

CVE-2026-4800: lodash Template Imports Allow Code Execution

CVE-2026-4800 affects lodash's `_.template()` templating API, where untrusted input reaching the `imports` option can lead to arbitrary code execution. The fix was shipped as a dependency upgrade from lodash 4.17.21 to 4.18.1 in the project's lockfile, though the PR itself notes it was never verified against the actual code paths in this repository.

critical

proxy-addr 2.0.7 IP Spoofing: CVE-2026-90711 Trust Bypass

A critical vulnerability in proxy-addr 2.0.7 allowed attackers to spoof client IP addresses by manipulating X-Forwarded-For headers when the trust chain evaluation contained specific misconfigurations. The fix in version 2.0.8 hardens the trust evaluation logic to prevent IP address falsification in Express.js applications relying on this common middleware dependency.

high

TweenMax `_applyCycle` Prototype Pollution via vars.cycle Keys

A bundled copy of the TweenMax animation library copied attacker-influenceable `vars.cycle` property names straight onto a tween configuration object using an unguarded `for...in` loop, so a key named `__proto__`, `constructor`, or `prototype` was written through to the object's prototype chain. The fix adds an explicit key denylist to both copies of the `_applyCycle` helper so those three names are skipped during the merge. No CVE or GHSA is assigned; the issue is tracked as CWE-1321 (Improperl

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas