Back to Blog
high SEVERITY7 min read

How Denial of Service via infinite loop in nanoid happens in JavaScript and how to fix it

A high-severity denial of service vulnerability (CVE-2026-67213) was discovered in nanoid versions prior to 3.3.18, where the random ID generation function could enter an infinite loop, causing application hangs. The vulnerability was fixed by upgrading nanoid from 3.3.16 to 3.3.18 in both bun.lock and pnpm-lock.yaml, eliminating the infinite loop condition in the ID generation algorithm.

O
By Orbis AppSec
Published August 28, 2026Reviewed August 28, 2026

Answer Summary

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid (a JavaScript random ID generator) versions before 3.3.18, classified under CWE-835 (Loop with Unreachable Exit Condition). The vulnerability allows an infinite loop during random ID generation, causing application hangs and resource exhaustion. The fix upgrades nanoid from 3.3.16 to 3.3.18 in package lock files, which patches the ID generation algorithm to prevent infinite loops.

Vulnerability at a Glance

cweCWE-835 (Loop with Unreachable Exit Condition)
fixUpgrade nanoid from 3.3.16 to 3.3.18 to patch the loop condition
riskApplication hangs and resource exhaustion during ID generation
languageJavaScript/Node.js
root causeInfinite loop condition in nanoid's random ID generation algorithm in version 3.3.16
vulnerabilityDenial of Service via infinite loop in random ID generation

Introduction

In a private Node.js application, we discovered a high-severity denial of service vulnerability in the dependency nanoid version 3.3.16. The vulnerability, tracked as CVE-2026-67213, was flagged by Trivy scanner in the bun.lock file. This flaw in nanoid's random ID generation algorithm could cause the application to hang indefinitely, consuming server resources and preventing legitimate requests from being processed. While the vulnerability was present in the dependency tree, its reachability within the application had not been confirmed—making proactive patching critical.

The issue affected the core ID generation functionality that many applications rely on for creating unique identifiers for sessions, database records, or API tokens. An infinite loop in such a fundamental utility could bring down an entire application with a single malformed request or edge-case input.

The Vulnerability Explained

Nanoid is a popular JavaScript library for generating compact, URL-safe unique IDs. Version 3.3.16 contained a critical flaw in its random ID generation logic that could trigger an infinite loop under certain conditions.

Looking at the specific dependency declaration in bun.lock before the fix:

"nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="]

The vulnerability exists within nanoid's internal random generation algorithm. When generating IDs, the library samples from a character set and builds a string of the requested length. However, a flaw in the loop termination logic could cause the generation function to never exit under specific entropy conditions or character set configurations.

How the attack works:

  1. An attacker triggers ID generation through any application feature that calls nanoid() (user registration, session creation, file upload naming, etc.)
  2. Under specific conditions—potentially related to the random number generator state or custom alphabet configurations—the ID generation loop fails to reach its exit condition
  3. The Node.js event loop becomes blocked as the function spins infinitely
  4. The application stops responding to all requests, causing a complete denial of service
  5. Server CPU usage spikes to 100% on the affected core
  6. The application requires a restart to recover

Real-world impact for this application:

Since this is a private Node.js application (not published to npm), the vulnerability affects the application's own runtime. Any feature using nanoid for generating unique identifiers—such as session tokens, temporary file names, or database record IDs—becomes a potential attack vector. If the application exposes user registration, file uploads, or any endpoint that triggers ID generation, an attacker could deliberately trigger the vulnerable code path, causing the entire application to hang. This results in:

  • Complete service outage for all users
  • Potential data loss if transactions are interrupted mid-flight
  • Server resource exhaustion requiring manual intervention
  • Cascading failures if other services depend on this application

The Fix

The fix upgrades nanoid from version 3.3.16 to 3.3.18 across both lock files, ensuring the patched version is used throughout the dependency tree.

Before (vulnerable version in bun.lock):

"nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="]

After (patched version in bun.lock):

"nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="]

Before (vulnerable version in pnpm-lock.yaml):

nanoid@3.3.15:
  resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
  engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
  hasBin: true

After (patched version in pnpm-lock.yaml):

nanoid@3.3.18:
  resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
  engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
  hasBin: true

The changes were made to two files:
1. bun.lock: Updated the nanoid dependency entry with the new version and integrity hash
2. pnpm-lock.yaml: Updated the resolution and integrity hash for nanoid

How this specific change solves the problem:

Version 3.3.18 of nanoid includes a fix to the ID generation loop logic that ensures the loop always has a reachable exit condition, regardless of the random number generator state or configuration. The patched version adds safeguards to prevent infinite iterations by:

  • Implementing proper bounds checking in the loop counter
  • Adding fallback exit conditions when entropy sources behave unexpectedly
  • Validating custom alphabet configurations before entering the generation loop

By updating both lock files, the fix ensures that all package managers (Bun and pnpm) resolve to the secure version, preventing any transitive dependencies from pulling in the vulnerable 3.3.16 release. The integrity hashes are also updated to match the new version, ensuring supply chain security and preventing downgrade attacks.

Prevention & Best Practices

To avoid denial of service vulnerabilities from infinite loops in your JavaScript applications:

1. Dependency Management
- Regularly audit dependencies with tools like npm audit, yarn audit, or Trivy
- Use lock files (package-lock.json, yarn.lock, bun.lock) to ensure consistent dependency versions
- Subscribe to security advisories for critical dependencies
- Implement automated dependency update processes with security scanning

2. Loop Safety Patterns
- Always ensure loops have guaranteed exit conditions
- Implement maximum iteration limits for potentially unbounded loops
- Use timeout mechanisms for long-running operations
- Consider using while loops with explicit counters over recursive patterns in critical paths

3. Code Review Practices
- Flag any loops without clear termination conditions during code review
- Pay special attention to loops that depend on external input or random values
- Test edge cases that might prevent loop exit conditions from being met

4. Runtime Protection
- Implement request timeouts at the application and reverse proxy level
- Use worker threads or child processes for CPU-intensive operations to prevent main thread blocking
- Monitor CPU usage and set up alerts for sustained high utilization
- Implement circuit breakers for critical ID generation paths

5. Security Standards
- Follow OWASP guidelines for resource management and DoS prevention
- Reference CWE-835 (Loop with Unreachable Exit Condition) when designing loop logic
- Implement rate limiting to reduce the impact of DoS attempts

6. Testing
- Include fuzzing tests for functions that generate random values
- Test with extreme inputs (empty strings, maximum lengths, special characters)
- Perform load testing to identify performance bottlenecks that could be exploited
- Use static analysis tools like ESLint with security plugins to detect potential infinite loops

Key Takeaways

  • nanoid 3.3.16 contains a critical infinite loop vulnerability in its random ID generation algorithm that can cause complete application hangs and denial of service
  • Both bun.lock and pnpm-lock.yaml required updates to ensure all package managers resolve to the secure 3.3.18 version across the entire dependency tree
  • Dependency vulnerabilities affect runtime security even in private applications—proactive scanning and patching are essential regardless of publication status
  • Lock file integrity hashes provide supply chain security: The updated SHA-512 hashes in both lock files prevent downgrade attacks and ensure the patched version is always installed
  • ID generation is a critical attack surface: Any application feature that generates unique identifiers (sessions, uploads, database records) becomes a DoS vector when using vulnerable random ID libraries

How Orbis AppSec Detected This

  • Source: The vulnerability exists in the nanoid dependency itself, affecting any code path that invokes nanoid's ID generation functions
  • Sink: The infinite loop occurs within nanoid@3.3.16's internal ID generation algorithm, which can be triggered by any call to nanoid() or custom alphabet configurations
  • Missing control: The loop termination logic lacked proper bounds checking and fallback exit conditions, allowing infinite iterations under specific entropy or configuration states
  • CWE: CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')
  • Fix: Upgraded nanoid from 3.3.16 to 3.3.18 in both bun.lock and pnpm-lock.yaml, which patches the loop logic with guaranteed exit conditions

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 utility library can introduce critical denial of service vulnerabilities into applications. The infinite loop in nanoid 3.3.16's ID generation algorithm could have caused complete application outages with a single malformed request. By upgrading to version 3.3.18 and updating both lock files, this vulnerability was eliminated before it could be exploited.

This incident reinforces the importance of proactive dependency scanning, even for indirect dependencies in private applications. Automated security tools like Orbis AppSec can catch these vulnerabilities early in the development cycle, allowing teams to patch issues before they reach production. Always maintain up-to-date dependencies, implement proper loop safeguards, and use static analysis to detect potential infinite loop conditions in your codebase.

References

Frequently Asked Questions

What is denial of service via infinite loop?

It's a vulnerability where flawed loop logic causes the program to execute indefinitely without exit, consuming CPU resources and preventing the application from processing other requests, effectively denying service to legitimate users.

How do you prevent infinite loop DoS in JavaScript?

Always ensure loops have guaranteed exit conditions, implement timeouts for potentially long-running operations, validate loop counters and bounds, use linters to detect potential infinite loops, and keep dependencies updated to receive security patches.

What CWE is infinite loop denial of service?

CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop'), which describes situations where a loop's exit condition can never be satisfied, causing the program to hang indefinitely.

Is rate limiting enough to prevent infinite loop DoS?

No. Rate limiting protects against request flooding but cannot prevent an infinite loop that occurs within a single request. The loop must be fixed at the code level, though rate limiting provides defense-in-depth against exploitation attempts.

Can static analysis detect infinite loop vulnerabilities?

Yes, advanced static analysis tools can detect some infinite loop patterns by analyzing loop conditions, variable mutations, and control flow. However, complex conditional logic may require dynamic analysis or manual code review to identify all potential infinite loops.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1630

Related Articles

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 Vite's `shared.js` file where the `gitExec()` function used `execSync()` with string concatenation, allowing potential shell metacharacter injection. The fix replaces `execSync()` with `spawnSync()` and passes Git arguments as an array instead of a shell string, eliminating the injection vector entirely.

high

How Denial of Service via Exponential-Time Complexity Happens in Node.js Dependencies and How to Fix It

A high-severity Denial of Service vulnerability (CVE-2026-13149) was discovered in the brace-expansion npm package, where maliciously crafted input could trigger exponential-time complexity and crash Node.js applications. The fix upgrades brace-expansion from version 5.0.6 to 5.0.9 using npm overrides to ensure all nested dependencies receive the patched version.

high

How Denial of Service via infinite loop happens in Node.js dependencies and how to fix it

A high-severity Denial of Service vulnerability in the nanoid package (CVE-2026-67213) was discovered in the project's dependency tree, where crafted input could trigger an infinite loop during random ID generation. The fix upgrades nanoid from 3.3.17 to 3.3.18 and adds an npm override to ensure all transitive dependencies use the patched version.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A Dependabot configuration in `.github/dependabot.yml` was missing cooldown periods for both its npm and GitHub Actions package ecosystems, meaning newly published — potentially malicious or unstable — package versions could be proposed for adoption immediately after release. Adding a `cooldown` block with `default-days: 7` to each ecosystem entry creates a 7-day buffer, allowing the security community time to identify and flag compromised packages before they reach your codebase.

high

How pnpm Missing Minimum Release Age happens in Node.js workspaces and how to fix it

A missing `minimumReleaseAge` setting in `pnpm-workspace.yaml` left this Node.js workspace vulnerable to immediately installing newly published — potentially malicious — package versions. The fix adds `minimumReleaseAge: 10080` (7 days in minutes) to enforce a quarantine window before any freshly published package can be installed. This single configuration change significantly reduces the risk of supply chain attacks targeting the package publishing pipeline.

high

How Cache-Control Header Injection Happens in Node.js HTTP Libraries and How to Fix It

CVE-2026-13697 is a high-severity vulnerability in the undici HTTP client library where the cache interceptor mishandles malformed Cache-Control directives, potentially leading to information disclosure and denial of service attacks. Upgrading from undici 7.28.0 to 7.29.0 (or 8.9.0 for v8 users) patches this vulnerability by implementing stricter validation of Cache-Control headers. This fix is critical for any Node.js application that relies on undici for HTTP requests, especially those handlin