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

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 dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

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.

critical

How Remote Code Execution Happens in Handlebars Template Compilation and How to Fix It

CVE-2026-33937 is a critical remote code execution vulnerability in Handlebars.js that allows attackers to execute arbitrary code by passing maliciously crafted Abstract Syntax Tree (AST) objects to the compile() function. The vulnerability was patched in version 4.7.9, and we've upgraded to protect against this threat vector.

critical

How Denial of Service via Gzip Bomb happens in Node.js and how to fix it

A critical Denial of Service vulnerability (CVE-2026-59873) in the `tar` npm package allowed attackers to craft malicious gzip archives that could exhaust memory or CPU during decompression. The fix upgrades `tar` from 7.5.11 to 7.5.21 across `package.json` and `package-lock.json`, closing the resource-exhaustion path without changing any application code.