Back to Blog
high SEVERITY4 min read

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 6, 2026•Reviewed October 6, 2026

Answer Summary

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 are affected. An attacker can cause catastrophic backtracking by supplying a crafted extglob pattern such as `@(a|a)*`, freezing the event loop and denying service. The fix upgrades picomatch to 2.3.2, 3.0.2, or 4.0.4. CWE is unknown.

Vulnerability at a Glance

cweunknown
fixUpgrade picomatch to patched version with improved pattern normalization
riskEvent loop blocking leading to complete DoS on any service accepting glob patterns
languageJavaScript (Node.js)
root causeUnbounded backtracking in extglob pattern compilation to regex
vulnerabilityRegular Expression Denial of Service (ReDoS)

Affected Versions

Affected < 2.3.2 (2.x line), < 3.0.2 (3.x line), < 4.0.4 (4.x line)
Fixed in 2.3.2, 3.0.2, 4.0.4
Ecosystem npm
CVE / GHSA CVE-2026-33671 / not assigned
CWE unknown

When Glob Matching Becomes a Denial of Service

picomatch is the glob matching engine behind @rollup/pluginutils, fast-glob, chokidar, and countless build tools. It compiles glob patterns into regular expressions for efficient matching. The vulnerability lies in how picomatch 2.3.1 handles extglob patterns—the Bash-style extended globs like @(pattern-list) for matching one of several alternatives.

When an extglob contains nested alternations combined with quantifiers, the generated regular expression can exhibit catastrophic backtracking. A pattern as simple as @(a|a)* causes the regex engine to explore exponentially many matching paths. On Node.js, this blocks the event loop entirely. No I/O completes. No timers fire. The process appears to hang until killed.

The danger is pervasive because picomatch often processes untrusted input. Build tools accept glob patterns from configuration files. File watchers accept patterns from user preferences. Any service that lets users specify which files to include or exclude is a potential target.

The Vulnerability Explained

The root cause is in picomatch's makeRe() function, which compiles glob patterns to regular expressions without sufficient normalization of extglob structures. Consider this vulnerable code path:

const picomatch = require('picomatch');

// User-controlled input from HTTP request, config file, CLI arg
const userPattern = '@(a|a)*'.repeat(20);  // 20 repetitions of the toxic pattern

// This call blocks the event loop indefinitely
const isMatch = picomatch(userPattern);

The @(a|a) extglob creates a regex alternation (?:a|a). Combined with the * quantifier and repeated, the backtracking search space explodes. The regex engine tries every possible way to group the matches, and with no early bailout, CPU usage hits 100% with no progress.

In production systems, this manifests as:
- Build servers that hang on malicious package.json glob configurations
- File watchers that freeze when processing user-supplied ignore patterns
- CI pipelines that timeout after hours of CPU burn on a single glob evaluation

The attack is low complexity, high impact: a single 200-character string can disable a Node.js process for minutes to hours.

The Fix

The remediation upgrades picomatch from 2.3.1 to 4.0.4 (or the appropriate backport). The key changes in the dependency resolution:

// package.json - forcing patched version via overrides
{
  "overrides": {
    "@rollup/pluginutils": {
      "picomatch": "4.0.4"
    }
  }
}
// package-lock.json version resolution
-      "version": "2.3.1",
-      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
-      "engines": {
-        "node": ">=8.6"
-      }
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "engines": {
+        "node": ">=12"
+      }

The fix involves two critical improvements in picomatch 4.0.4:

  1. Pattern normalization: Extglob patterns are now pre-processed to eliminate redundant alternations like (a|a) before regex compilation. This removes the structural cause of exponential backtracking.

  2. Regex hardening: The generated regular expressions include possessive quantifiers and atomic groups where supported, preventing the regex engine from backtracking into already-matched portions of the pattern.

The engine requirement bump from >=8.6 to >=12 reflects the use of modern JavaScript features in the patch, including better regex capabilities and performance optimizations.

Key Takeaways

  • Extglob patterns are regex injection vectors: Any code that accepts @(...), *(...), +(...), ?(...), or !(...) patterns from untrusted sources must treat them as potentially malicious input.

  • The overrides field is essential for transitive dependencies: When a vulnerable package appears deep in the dependency tree (as picomatch does via @rollup/pluginutils), npm overrides forces the patched version without waiting for every intermediate package to update.

  • Node.js engine requirements are security boundaries: The >=12 requirement in picomatch 4.0.4 isn't arbitrary—older Node versions lack the regex features and performance characteristics needed for a robust fix. Running EOL Node versions blocks security patches.

  • Glob compilation must have timeouts: Applications processing user-supplied globs should wrap picomatch() calls in AbortController-based timeouts or move compilation to worker threads to prevent event loop blocking.

How Orbis AppSec Detected This

Source: The pattern parameter passed to picomatch() or picomatch.makeRe() from user-controlled configuration, HTTP requests, or CLI arguments.

Sink: The internal regex compilation within picomatch's makeRe() function, specifically the path that processes extglob syntax into JavaScript RegExp objects.

Missing control: No validation of extglob complexity, no regex compilation timeouts, and no normalization of redundant alternation patterns before regex generation.

CWE: unknown (ReDoS patterns typically map to CWE-1333, but no official assignment was made for this CVE).

Fix: Force upgrade to picomatch 2.3.2, 3.0.2, or 4.0.4 via direct dependency update or npm overrides, ensuring the patched pattern normalization logic prevents catastrophic backtracking.

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-33671 demonstrates how even "safe" pattern-matching libraries can become denial-of-service weapons when regex compilation meets untrusted input. The picomatch upgrade to 4.0.4 eliminates the backtracking vulnerability through structural pattern normalization, but the broader lesson applies to any code that compiles user input into regular expressions. Use npm overrides to force security fixes in transitive dependencies, validate pattern complexity before compilation, and always consider the regex engine's worst-case behavior when accepting untrusted glob expressions.

Prevention and further reading

Frequently Asked Questions

Does the picomatch upgrade from 2.3.1 to 4.0.4 require Node.js 12 or higher?

Yes. The patched version updates the engine requirement from `>=8.6` to `>=12`. Applications running on Node.js 8.6–11.x must upgrade their runtime or use the 2.3.2 backport.

Which picomatch API entry points actually compile extglob patterns to vulnerable regexes?

The `picomatch()` function and its `makeRe()` method both compile extglob patterns. Any code path that passes user input through `picomatch(pattern, options)` or `picomatch.makeRe(pattern)` without prior validation is vulnerable.

How does the npm `overrides` field in package.json force the patched version across nested dependencies?

The `overrides` field specifically targets `@rollup/pluginutils` to use `picomatch@4.0.4`, ensuring that even transitive dependencies receive the fix without waiting for upstream packages to update.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #3

Related Articles

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas

high

brace-expansion Stack Exhaustion: CVE-2026-102276 Patched

A critical stack exhaustion vulnerability in brace-expansion allows attackers to crash Node.js applications by supplying specially crafted brace patterns that trigger unbounded recursion. The fix upgrades the library across all maintained version lines to enforce depth limits on recursive expansion. This vulnerability affects any service that expands user-controlled brace patterns without input validation.

critical

BFF Proxy QR Code Endpoint Prototype Pollution via Unvalidated JSON

A critical prototype pollution vulnerability in a backend-for-frontend (BFF) proxy endpoint allowed attackers to inject malicious properties into the JavaScript Object prototype by crafting JSON requests with forbidden keys. This could compromise application behavior across all objects. The fix adds explicit key validation to reject payloads containing `__proto__`, `constructor`, or `prototype`.

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.

high

ItemPicker `_commitTraitInput` XSS via Unescaped Trait Chip Rendering

The `_commitTraitInput` function in ItemPicker accepted arbitrary user input for trait values without sanitization, then rendered those values directly into HTML chip elements through `_updateList`. An attacker could inject malicious JavaScript payloads that executed when trait chips were displayed. The fix applies a strict whitelist filter removing all non-alphanumeric characters except spaces and hyphens.