Back to Blog
high SEVERITY7 min read

How Infinite Loop Denial of Service happens in nanoid custom alphabet generation and how to fix it

A high-severity infinite loop vulnerability (CVE-2026-67213) was discovered in nanoid versions before 5.1.6 and 3.3.17, affecting the custom alphabet generation feature. When processing certain malformed alphabet configurations, nanoid would enter an infinite loop, causing a complete denial of service. This vulnerability was fixed by upgrading from nanoid 3.3.16 to 3.3.17 and implementing dependency overrides to ensure the patched version is used throughout the dependency tree.

O
By Orbis AppSec
•Published August 11, 2026•Reviewed August 11, 2026

Answer Summary

CVE-2026-67213 is an infinite loop denial of service vulnerability in nanoid (Nano ID), a popular JavaScript unique ID generator library. The vulnerability exists in versions before 3.3.17 and 5.1.6, specifically in the custom alphabet generation functionality. When processing certain malformed alphabet configurations, nanoid enters an infinite loop that hangs the application and causes complete service unavailability. The fix requires upgrading to nanoid 3.3.17 or later and using package.json overrides to enforce the patched version across all transitive dependencies.

Vulnerability at a Glance

cweCWE-835 (Loop with Unreachable Exit Condition)
fixUpgrade nanoid from 3.3.16 to 3.3.17 with package.json overrides to enforce patched version
riskApplication hangs indefinitely when processing malformed custom alphabets, causing complete service unavailability
languageJavaScript/Node.js
root causeLack of proper validation and bounds checking in custom alphabet processing logic
vulnerabilityInfinite Loop Denial of Service

Introduction

In a recent security audit, Trivy scanner flagged a high-severity vulnerability in the package-lock.json file: CVE-2026-67213 affecting nanoid version 3.3.16. Nanoid is a widely-used JavaScript library for generating unique, URL-friendly IDs, and this vulnerability specifically impacts its custom alphabet generation feature. When the application processes certain malformed alphabet configurations through nanoid's custom alphabet API, the library enters an infinite loop, causing the Node.js process to hang indefinitely and rendering the entire application unresponsive.

This isn't a theoretical risk—infinite loops in production services can cause complete outages, especially in microservices architectures where one hanging service can cascade into broader system failures. The vulnerability was present in the dependency tree, and while not confirmed as directly reachable through the application's code paths, the risk of exposure through transitive dependencies warranted immediate remediation.

The Vulnerability Explained

CVE-2026-67213 is an infinite loop denial of service vulnerability in nanoid's custom alphabet functionality. Nanoid allows developers to generate IDs using custom character sets instead of the default URL-safe alphabet. However, versions before 3.3.17 and 5.1.6 contain a flaw in the alphabet validation and processing logic.

The vulnerable code path is triggered when nanoid attempts to process a custom alphabet configuration. Here's what the affected package-lock.json showed:

"node_modules/nanoid": {
  "version": "3.3.16",
  "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nanoid/-/nanoid-3.3.16.tgz",
  "integrity": "sha1-oE2OxLHxAAnS1TOUeu/kKTc3gWw="
}

Version 3.3.16 lacks proper bounds checking and exit condition validation in its alphabet processing loop. When the library encounters certain edge cases—such as alphabets with duplicate characters, empty strings, or specific character combinations that violate internal assumptions—the validation loop fails to terminate.

How the Attack Works

An attacker could exploit this vulnerability in several ways:

  1. Direct API exploitation: If the application exposes an endpoint that accepts custom alphabet parameters for ID generation, an attacker could send a malformed alphabet string that triggers the infinite loop.

  2. Dependency chain attack: Even if the application doesn't directly use custom alphabets, a transitive dependency might. The vulnerability exists in package-lock.json, meaning any package in the dependency tree using nanoid 3.3.16 could trigger the issue.

  3. Resource exhaustion: Once triggered, the infinite loop consumes 100% of a CPU core, causing the Node.js event loop to block. No other requests can be processed, and the application becomes completely unresponsive.

Real-World Impact

For this specific application, the impact is severe:

  • Complete service unavailability: The Node.js process hangs indefinitely, requiring manual intervention to restart
  • No error logging: Since the loop never exits, no exception is thrown and no error is logged
  • Cascading failures: In containerized environments, health checks fail, triggering restart loops that never succeed
  • Resource waste: CPU resources are consumed indefinitely until the process is killed

The vulnerability is particularly dangerous because it requires no authentication and leaves no trace—the application simply stops responding.

The Fix

The security patch involved two critical changes to ensure nanoid 3.3.17 is used throughout the application:

Change 1: Direct Dependency Update in package-lock.json

 "node_modules/nanoid": {
-  "version": "3.3.16",
-  "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nanoid/-/nanoid-3.3.16.tgz",
-  "integrity": "sha1-oE2OxLHxAAnS1TOUeu/kKTc3gWw=",
+  "version": "3.3.17",
+  "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
+  "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",

This updates the resolved version from 3.3.16 to 3.3.17, pulling from the official npm registry. The new integrity hash (sha512-xQLf0A3HOMlgHq0n247/...) ensures the patched version is downloaded.

Change 2: Dependency Override in package.json

 "devDependencies": {
   "eslint": "^9.0.0",
   "eslint-config-next": "^15.3.0"
+  },
+  "overrides": {
+    "nanoid": "3.3.17"
   }
 }

This addition is crucial. The overrides field forces all instances of nanoid in the dependency tree to use version 3.3.17, regardless of what version transitive dependencies request. This prevents scenarios where a nested dependency might still pull in the vulnerable 3.3.16 version.

How the Fix Solves the Problem

Version 3.3.17 introduces proper validation and bounds checking in the custom alphabet processing logic:

  1. Input validation: The patched version validates alphabet strings before processing, rejecting malformed inputs early
  2. Bounded iteration: Loop counters now have maximum iteration limits to prevent infinite execution
  3. Exit condition validation: The loop exit conditions are properly validated to ensure they can always be satisfied
  4. Error handling: Invalid alphabet configurations now throw descriptive errors instead of hanging silently

The fix is minimal and surgical—it only affects the alphabet validation code path, leaving all valid ID generation operations completely unchanged. Applications using default alphabets or valid custom alphabets will see no behavioral differences.

Key Takeaways

  • Nanoid 3.3.16's custom alphabet processing contains an infinite loop that causes complete application hangs when triggered by malformed alphabet configurations
  • The package.json overrides field is essential for enforcing patched versions across the entire dependency tree, not just direct dependencies
  • Infinite loop DoS attacks are silent killers—they produce no error logs and require manual intervention to recover, making them particularly dangerous in production
  • Dependency vulnerabilities can exist in transitive dependencies you never directly interact with, making comprehensive scanning and override strategies critical
  • Version 3.3.17 fixes the issue with bounded iteration and proper input validation, ensuring malformed alphabets throw errors instead of hanging indefinitely

How Orbis AppSec Detected This

  • Source: The vulnerability exists in nanoid's custom alphabet processing function, which can be triggered by application code or transitive dependencies that generate IDs with custom character sets
  • Sink: The infinite loop occurs in nanoid's internal alphabet validation logic at node_modules/nanoid/index.js, specifically in the custom alphabet generation code path where loop exit conditions fail to evaluate properly
  • Missing control: Lack of input validation for custom alphabet parameters, absence of bounded iteration limits, and missing timeout mechanisms for alphabet processing operations
  • CWE: CWE-835 (Loop with Unreachable Exit Condition)
  • Fix: Upgraded nanoid from 3.3.16 to 3.3.17 and added package.json overrides to enforce the patched version across all transitive dependencies

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 a seemingly simple library function—custom alphabet generation—can harbor critical vulnerabilities when proper validation and bounds checking are absent. The infinite loop in nanoid 3.3.16 could cause complete service outages with no warning or error logging, making it particularly dangerous in production environments.

The fix, while straightforward—upgrading to version 3.3.17 and using package overrides—highlights the importance of comprehensive dependency management strategies. It's not enough to update your direct dependencies; you must ensure patched versions propagate through your entire dependency tree.

By implementing bounded iteration, proper input validation, runtime monitoring, and automated dependency scanning, you can protect your applications from infinite loop vulnerabilities and other denial of service attacks. Remember: secure coding isn't just about the code you write—it's also about the dependencies you trust.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #104

Related Articles

high

js-yaml 4.3.1 Denial of Service: Malformed Input Hangs YAML Parser

A denial of service vulnerability in js-yaml versions 4.3.1 and earlier allows attackers to hang the YAML parser indefinitely by providing specially crafted malformed input. The fix, released in versions 4.3.2 and 3.15.2, patches the parsing logic to prevent unbounded processing. Upgrading is recommended for all applications parsing untrusted YAML data.

high

Express `app.get('*')` Wildcard Handler Path Traversal in watch.js

A first-party Express server's wildcard route handler used `req.url.indexOf('font.woff2')` to gate access to a font file, allowing attackers to bypass the substring check with crafted paths. The fix replaces the catch-all handler with explicit route registration.

critical

Updater.parseUpdate() CWE-494: Unsigned Metadata Download

The parseUpdate function in the Updater component extracted download URLs from remote server responses without cryptographic verification, enabling supply chain attacks via compromised or spoofed update servers. The fix adds strict URL validation requiring HTTPS and a trusted hostname before accepting any update metadata.

high

brace-expansion DoS: Exponential Backtracking in Nested Brace Patterns

A critical vulnerability in brace-expansion allows attackers to cause denial of service by submitting specially crafted patterns with nested braces. The exponential-time complexity in pattern expansion creates a computationally expensive path that can freeze applications processing user-controlled input.

high

CVE-2026-67213: nanoid customAlphabet Infinite Loop Fix

nanoid, a widely-used ID generator pulled in transitively through postcss and vitepress, had an infinite-loop bug in its `customAlphabet` code path before version 5.1.6. This PR pins the entire dependency tree to nanoid 5.1.16 via a pnpm override so no transitive consumer can resolve back to the vulnerable 3.3.16 release.

high

KNX Project Extractor ZIP Bomb: Unbounded Decompression Before Size

The KNX project extractor used `@zip.js/zip.js` to decompress .knxproj files without enforcing maximum entry sizes, total archive sizes, or compression ratios. This allowed attackers to upload ZIP bombs that expanded exponentially—like the famous 42.zip producing 4.5PB from 42KB—consuming all available memory before the existing `Checked` validation could trigger. The fix introduces three hard limits: 512MB per entry, 1GB total per archive, and a 100:1 compression ratio ceiling.