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, 2026Reviewed 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.

Prevention & Best Practices

To prevent infinite loop vulnerabilities in your own code and dependencies:

1. Implement Bounded Iteration

Always use loop guards and maximum iteration counts:

// Bad: Unbounded loop
while (condition) {
  // processing
}

// Good: Bounded loop with maximum iterations
let iterations = 0;
const MAX_ITERATIONS = 10000;
while (condition && iterations++ < MAX_ITERATIONS) {
  // processing
}
if (iterations >= MAX_ITERATIONS) {
  throw new Error('Maximum iterations exceeded');
}

2. Validate Loop Exit Conditions

Ensure loop exit conditions can always be satisfied:

// Bad: Exit condition might never be true
while (value !== target) {
  value = processValue(value);
}

// Good: Multiple exit conditions with timeout
const startTime = Date.now();
const TIMEOUT_MS = 5000;
while (value !== target && (Date.now() - startTime) < TIMEOUT_MS) {
  value = processValue(value);
  if (!isValidValue(value)) break;
}

3. Use Dependency Scanning

Implement automated dependency scanning in your CI/CD pipeline:

  • Trivy: Comprehensive vulnerability scanner that detected this issue
  • npm audit: Built-in npm security auditing
  • Snyk: Continuous dependency monitoring
  • Dependabot: Automated dependency updates with security alerts

4. Leverage Package Overrides

Use overrides (npm) or resolutions (yarn) to enforce secure versions across your entire dependency tree:

{
  "overrides": {
    "nanoid": ">=3.3.17",
    "vulnerable-package": ">=secure-version"
  }
}

5. Monitor Runtime Behavior

Implement monitoring to detect infinite loops in production:

  • CPU usage alerts for sustained 100% utilization
  • Request timeout monitoring
  • Event loop lag detection using libraries like loopbench
  • Health check endpoints with reasonable timeouts

Security Standards Reference

This vulnerability maps to several security standards:

  • CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')
  • OWASP Top 10 2021 - A06:2021: Vulnerable and Outdated Components
  • NIST SP 800-53: SI-10 (Information Input Validation)

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.

References

Frequently Asked Questions

What is an infinite loop denial of service vulnerability?

An infinite loop DoS occurs when code enters a loop that never terminates due to missing exit conditions or improper validation. The application hangs indefinitely, consuming CPU resources and becoming completely unresponsive, effectively denying service to all users.

How do you prevent infinite loop vulnerabilities in JavaScript?

Implement proper input validation before loops, use bounded iteration with maximum iteration limits, add timeout mechanisms for long-running operations, validate loop exit conditions, and use static analysis tools like ESLint with complexity checks to detect potentially infinite loops during development.

What CWE is infinite loop denial of service?

Infinite loop vulnerabilities are classified as CWE-835 (Loop with Unreachable Exit Condition). This CWE covers situations where a loop's exit condition can never be satisfied, causing the loop to execute indefinitely and hang the application.

Is input validation enough to prevent infinite loop DoS?

Input validation is critical but not always sufficient. You also need bounded iteration (maximum loop counts), timeout mechanisms, proper error handling, and comprehensive testing with edge cases. Defense-in-depth requires multiple layers: validate inputs, bound iterations, implement timeouts, and monitor resource consumption.

Can static analysis detect infinite loop vulnerabilities?

Yes, static analysis tools can detect many infinite loop patterns through control flow analysis, complexity metrics, and pattern matching. Tools like Trivy, Semgrep, and specialized JavaScript linters can identify suspicious loop structures, missing exit conditions, and unbounded iterations, though complex logic may require manual review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #104

Related Articles

critical

How Sensitive Data Exposure in Error Logging happens in TypeScript/Deno and how to fix it

A critical vulnerability in Supabase Edge Functions allowed sensitive authentication errors and API credentials to leak through verbose error logging. The `cancel-subscription/index.ts` function logged full error objects to the console, potentially exposing Paddle API keys and auth tokens in deployment logs. The fix sanitizes all error messages to log only safe error text while preserving debugging capability.

critical

How HTTP Header Injection Happens in Go and How to Fix It

A critical vulnerability in the file upload handler allowed attackers to inject CRLF sequences into HTTP response headers through crafted filenames. The fix sanitizes user-supplied filenames before using them in Content-Disposition headers, preventing header injection attacks that could lead to cache poisoning, session fixation, or XSS.

high

How Path Traversal and Security Policy Bypass Happens in Node.js Dependencies and How to Fix It

A high-severity vulnerability in the fast-uri package (CVE-2026-6321) allowed attackers to bypass security policies through improper Unicode hostname canonicalization and path traversal. This issue affected the @apralabs/apra-fleet project through its dependency tree, and was resolved by upgrading fast-uri from version 3.1.0 to 4.1.2 using npm overrides.

high

How Command Injection happens in Node.js child_process calls and how to fix it

A high-severity command injection vulnerability was discovered in `tools/utils/lang/helpers.ts` where the `prettier()` function passed a user-controllable `fileName` argument directly into a shell command string via `exec()`. By replacing `exec()` with `execFile()` and passing arguments as an array, the fix eliminates shell interpolation entirely, preventing attackers from injecting arbitrary shell commands through malicious filenames.

high

How Quadratic CPU Consumption in YAML Parsing happens in JavaScript and how to fix it

A high-severity vulnerability in js-yaml versions 3.x and 4.x allowed attackers to cause quadratic CPU consumption through specially crafted YAML documents using the `!!omap` type. This denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was fixed by upgrading from js-yaml 4.3.0 to 4.3.1, protecting applications from algorithmic complexity attacks during YAML parsing.

high

How Arbitrary HTTP Header Injection via Prototype Pollution happens in JavaScript and how to fix it

A high-severity vulnerability (CVE-2026-42035) in axios version 1.13.5 allowed attackers to inject arbitrary HTTP headers through prototype pollution. The fix upgrades axios to version 1.18.0 in the frontend's dependency tree, which includes proper prototype chain validation when constructing HTTP request headers. This prevents attackers from manipulating outgoing requests to perform SSRF, session hijacking, or cache poisoning attacks.