Back to Blog
high SEVERITY3 min read

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.

O
By Orbis AppSec
•Published September 25, 2026•Reviewed September 25, 2026

Answer Summary

nanoid versions before 5.1.6 are affected, including the 3.3.16 release that this repository's lockfile had resolved via `postcss`. An attacker or unlucky input that reaches nanoid's `customAlphabet()` generator can trigger a loop that never terminates, hanging the Node.js event loop and denying service to anything sharing that process. The fix is to upgrade to nanoid 5.1.6 or later; this repository pins to 5.1.16 through a pnpm workspace override. The advisory does not assign a CWE.

Vulnerability at a Glance

cweN/A (not assigned)
fixUpgrade nanoid to 5.1.6+ (pinned to 5.1.16 here) via a pnpm override
riskDenial of service via a Node.js process that never returns from ID generation
languageJavaScript (Node.js)
root cause`customAlphabet()`'s rejection-sampling loop lacked a terminating bound before 5.1.6
vulnerabilityInfinite loop / uncontrolled resource consumption

Introduction

nanoid is the small, dependency-free ID generator that ends up in almost every JavaScript build tool's tree — in this case through postcss, which every markdown-to-HTML pipeline in a vitepress-based site touches. CVE-2026-67213 reports that nanoid versions before 5.1.6 contain an infinite loop reachable through the customAlphabet code path, the factory function developers use when they want IDs drawn from a restricted character set instead of nanoid's default alphabet. A generator that is supposed to return a short string in microseconds can instead spin forever, and because ID generation routinely happens on a hot path (every request, every build step, every record insert), a hang there stalls the whole process.

This repository never called customAlphabet directly — it inherited the vulnerable version transitively through postcss@8.5.24. That's the pattern worth internalizing: you don't have to write a single line that touches nanoid to be exposed by it. If anything in your tree resolves the vulnerable release, the vulnerable code ships in your node_modules regardless of whether your own code path exercises it.

Affected Versions

Affected < 5.1.6 (this project had resolved 3.3.16)
Fixed in 5.1.6 (this project pins 5.1.16)
Ecosystem npm
CVE / GHSA CVE-2026-67213 / not assigned
CWE unknown (not assigned in the advisory)

The Vulnerability Explained

nanoid's customAlphabet() builds a generator that repeatedly reads random bytes and rejects any byte that falls outside the range needed to map fairly onto the requested alphabet — a standard rejection-sampling technique used to avoid modulo bias. The bug fixed for CVE-2026-67213 is that, before 5.1.6, that rejection loop could fail to make progress for certain alphabet configurations, so the loop never accumulates enough valid bytes to return and simply never exits.

Because the lockfile before this fix resolved to the vulnerable release, the exposure here didn't come from application code calling customAlphabet — it came from postcss@8.5.24, which lists nanoid as a direct dependency:

   postcss@8.5.24:
     dependencies:
       nanoid: 3.3.16

Any code path that ends up invoking nanoid's ID generation — build tooling, plugin caches, anything postcss uses internally to name intermediate artifacts — inherits whatever bug lives in the resolved nanoid version. If that path is reachable with attacker-influenced input (for example, a build or SSR pipeline that lets a user's request shape what gets built), the practical impact is a hung Node.js process: no crash, no error log, just a worker that never comes back and stops serving anything else scheduled on the same event loop.

The Fix

The fix is a version bump, applied at the workspace level so it overrides every transitive resolution rather than patching one importer at a time. pnpm-workspace.yaml gained a new override entry next to the existing vite pin:

 overrides:
   vitepress>vite: 5.4.19
+  nanoid: 5.1.16

The lockfile reflects the same change for the resolved package entry, and notably the supported Node.js range moved forward with it:

-  nanoid@3.3.16:
-    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+  nanoid@5.1.16:
+    engines: {node: ^18 || >=20}

and the postcss dependency block that previously pinned nanoid: 3.3.16 now resolves nanoid: 5.1.16 automatically, without postcss itself needing a version bump. The override is what makes this effective: without it, pnpm would keep honoring whatever version range postcss declares, and a simple pnpm update could quietly reintroduce 3.3.16 the next time postcss's own manifest is re-resolved. Pinning at the workspace level closes that door for every current and future importer of nanoid in the tree.

Key Takeaways

  • A vulnerable version of nanoid can enter your tree through a build tool you never call direct

Prevention and further reading

Frequently Asked Questions

Does the pnpm override in this fix pin nanoid to the exact version named in the CVE advisory?

No. The advisory says the fix landed in 5.1.6, but the override in `pnpm-workspace.yaml` pins the whole tree to `5.1.16`, a later patch release that still carries the fix.

Which other package in this dependency tree was pulling in the vulnerable nanoid release?

`postcss@8.5.24` depended directly on `nanoid@3.3.16`; the override forces it to resolve `nanoid@5.1.16` instead without requiring a postcss upgrade.

Does jumping from nanoid 3.3.16 to the 5.x line change anything besides the security fix?

Yes. The lockfile's `engines` field for nanoid changes from `^10 || ^12 || ^13.7 || ^14 || >=15.0.1` to `^18 || >=20`, so nanoid 5.x requires Node.js 18 or newer and consumers must expect an ESM-only build.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #157

Related Articles

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

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.

high

Spring Boot Actuator Wildcard Exposure in 2021.04 Provisioning

A misconfigured Spring Boot Actuator in the ArkCase 2021.04 provisioning template exposed all management endpoints through wildcard inclusion. The fix narrows exposure to health and info endpoints only, eliminating unauthenticated access to sensitive runtime data.

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

smol-toml 1.7.0 DoS: Malformed TOML Documents Crash Parser

A denial-of-service vulnerability in smol-toml 1.7.0 allows attackers to crash the parser by supplying malformed TOML documents. The vulnerability affects any application that parses untrusted TOML input. The fix, available in smol-toml 1.7.1, hardens input validation and error recovery.