Back to Blog
high SEVERITY5 min read

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

A high-severity vulnerability in the nanoid package (CVE-2026-67213) allowed attackers to trigger infinite loops through the customAlphabet function, potentially causing complete denial of service. This fix upgrades nanoid from version 3.3.16 to 3.3.17 in the app_store dependency tree, eliminating the DoS risk through a simple version override.

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

Answer Summary

CVE-2026-67213 is a high-severity Denial of Service vulnerability in the nanoid npm package (versions before 3.3.17 and 5.1.6) where the customAlphabet function can enter an infinite loop when processing malicious input. This is related to CWE-835 (Loop with Unreachable Exit Condition). The fix requires upgrading nanoid to version 3.3.17 or 5.1.6+ by adding a version override in package.json and updating package-lock.json.

Vulnerability at a Glance

cweCWE-835
fixUpgrade nanoid to version 3.3.17 or 5.1.6
riskApplication hangs, service unavailability, resource exhaustion
languageJavaScript/Node.js
root causeUnreachable loop exit condition in nanoid's customAlphabet function
vulnerabilityDenial of Service (Infinite Loop)

Introduction

In the app_store application, a high-severity vulnerability was discovered lurking in the dependency tree. The nanoid package—a popular library for generating unique, URL-friendly IDs—contained a critical flaw in its customAlphabet function that could send your Node.js application into an infinite loop. This isn't a theoretical risk; CVE-2026-67213 affects any application using nanoid versions before 3.3.17 (for the 3.x branch) or 5.1.6 (for the 5.x branch).

The vulnerability was flagged in app_store/package-lock.json, where nanoid version 3.3.16 was locked as a transitive dependency. While the assessment indicated the vulnerability was "present in dependency tree, not confirmed reachable," the high severity rating and potential for complete service disruption made this a priority fix.

The Vulnerability Explained

What is nanoid?

Nanoid is a tiny, secure, URL-friendly unique string ID generator for JavaScript. It's commonly used for generating session IDs, database keys, and other identifiers. The library includes a customAlphabet function that allows developers to create ID generators with custom character sets.

The Infinite Loop Problem

The vulnerability exists in the customAlphabet function of nanoid versions before 3.3.17 and 5.1.6. Under specific conditions involving the custom alphabet configuration, the function's internal loop can fail to reach its exit condition, causing the application to hang indefinitely.

Here's what the vulnerable dependency looked like in package-lock.json:

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

Attack Scenario

Consider an application that allows users to configure custom alphabets for ID generation—perhaps for generating human-readable codes or specific formatting requirements. An attacker could:

  1. Submit a specially crafted alphabet configuration to the application
  2. Trigger the customAlphabet function with malicious parameters
  3. Cause the function to enter an infinite loop
  4. Exhaust server resources as the thread becomes permanently blocked
  5. Repeat the attack to consume all available worker threads, causing complete denial of service

Even if user input doesn't directly reach customAlphabet, any code path that processes untrusted data and eventually calls this function could be exploited.

Real-World Impact

For the app_store application, this vulnerability could mean:
- Service unavailability: The application could become completely unresponsive
- Resource exhaustion: CPU usage spikes to 100% on affected threads
- Cascading failures: Dependent services timeout waiting for responses
- Business impact: Users unable to access the app store functionality

The Fix

The fix involves upgrading nanoid from version 3.3.16 to 3.3.17. Two files were modified to ensure the patched version is used throughout the dependency tree.

Change 1: package-lock.json Update

The direct dependency version was updated:

Before:

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

After:

"node_modules/nanoid": {
  "version": "3.3.17",
  "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
  "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g=="
}

Change 2: package.json Override

An overrides section was added to package.json to ensure all transitive dependencies also use the patched version:

{
  "overrides": {
    "nanoid": "3.3.17"
  }
}

This override is crucial because nanoid might be pulled in by other dependencies at different versions. The override ensures that regardless of what version other packages request, npm will resolve to the secure 3.3.17 version.

Why This Works

The patched version (3.3.17) fixes the loop exit condition in the customAlphabet function, ensuring that:
- The loop always has a reachable termination point
- Malicious inputs cannot trigger infinite execution
- The function maintains its performance characteristics for valid inputs

Key Takeaways

  • Transitive dependencies matter: Even if you don't directly use nanoid, it may be in your dependency tree through other packages—always check with npm ls nanoid
  • The overrides field is essential: When you can't control what version a dependency requests, overrides in package.json ensures the secure version is used everywhere
  • Infinite loop DoS is often overlooked: Unlike injection attacks, DoS vulnerabilities in utility functions are easy to miss but can be just as devastating
  • Version 3.3.16 of nanoid is vulnerable: If your package-lock.json shows this version, you need to upgrade immediately
  • Automated scanning catches what humans miss: This vulnerability was detected by Trivy scanning the package-lock.json file

How Orbis AppSec Detected This

  • Source: The nanoid package version 3.3.16 in app_store/package-lock.json dependency tree
  • Sink: The customAlphabet function in nanoid that contains the infinite loop vulnerability
  • Missing control: No version constraint ensuring the patched nanoid version (3.3.17+) was used
  • CWE: CWE-835 (Loop with Unreachable Exit Condition)
  • Fix: Added version override in package.json and updated package-lock.json to use nanoid 3.3.17

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 small utility library can introduce significant security risks into your application. The nanoid package is used by millions of projects, and this infinite loop vulnerability could have caused widespread service disruptions.

The fix was straightforward—a version upgrade from 3.3.16 to 3.3.17—but identifying the vulnerability and ensuring all transitive dependencies use the patched version requires vigilance. By implementing automated security scanning, keeping dependencies updated, and using npm overrides strategically, you can protect your applications from similar vulnerabilities.

Remember: your application is only as secure as its weakest dependency. Regular audits and automated scanning are essential components of a robust security posture.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #7

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.