Back to Blog
high SEVERITY6 min read

How Denial of Service via Regex Happens in Node.js and how to fix it

Apache Superset's Cypress testing infrastructure depended on `brace-expansion` 5.0.7, a Node.js package vulnerable to denial of service via malformed brace pattern input (CVE-2026-14257). Upgrading to version 5.0.8 in `superset-frontend/cypress-base/package-lock.json` closes the vulnerability. While the affected file is part of the test toolchain, it lives in the production codebase and is flagged by Trivy as likely exploitable.

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

Answer Summary

CVE-2026-14257 is a denial-of-service vulnerability (CWE-400: Uncontrolled Resource Consumption) in the `brace-expansion` npm package through version 5.0.7, affecting Node.js applications including Apache Superset's Cypress test infrastructure. An attacker can craft a malicious brace-expansion pattern that causes catastrophic backtracking in the package's regular expression engine, hanging the process. The fix is to upgrade `brace-expansion` to 5.0.8, which patches the vulnerable regex, as reflected in the changes to `superset-frontend/cypress-base/package-lock.json` and `package.json`.

Vulnerability at a Glance

cweCWE-400
fixUpgrade `brace-expansion` to 5.0.8, which ships a patched regex that eliminates the backtracking path.
riskAn attacker supplying a crafted brace-expansion pattern can cause the Node.js process to hang indefinitely, making the service unavailable.
languageJavaScript / Node.js
root causeThe `brace-expansion` package ≤5.0.7 used a regular expression susceptible to catastrophic backtracking on pathological input strings.
vulnerabilityDenial of Service via Uncontrolled Regex Evaluation (ReDoS)

How Denial of Service via Regex Happens in Node.js and how to fix it

Introduction

The superset-frontend/cypress-base/ directory in Apache Superset houses the Cypress end-to-end testing harness. It is easy to dismiss test tooling as a low-risk area of the codebase, but its package-lock.json sits in the production repository, is pulled during CI, and—critically—was pinned to a version of brace-expansion that carries a confirmed denial-of-service vulnerability: CVE-2026-14257.

Trivy flagged brace-expansion 5.0.7 in this lockfile as HIGH severity. The fix was a single-version bump to 5.0.8, but understanding why this matters requires a closer look at what brace-expansion does, how its regex went wrong, and what an attacker can do with it.


The Vulnerability Explained

What is brace-expansion?

brace-expansion is a tiny but widely-used npm package that expands shell-style brace expressions like {a,b,c} or file{1..5}.txt into their constituent strings. It is a transitive dependency of tools like glob, minimatch, and mocha—meaning it quietly powers file-matching logic across the Node.js ecosystem.

The Root Cause: Catastrophic Backtracking

In versions through 5.0.7, brace-expansion contained a regular expression used to parse brace tokens. That regex was susceptible to catastrophic backtracking—a class of bug where the regex engine explores an exponentially growing number of possible match paths when given a specially crafted input.

The vulnerable pattern looked structurally similar to:

// Simplified illustration of the problematic regex shape in brace-expansion ≤5.0.7
const re = /(\{[^{}]*\})+/;

When an attacker supplies a deeply nested or repetitive brace string—such as:

{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{

—the engine must consider every possible way the nested groups can match or fail before concluding the string is invalid. The evaluation time grows exponentially with input length, not linearly. A string of ~50 characters can stall a Node.js event loop for seconds; a string of ~100 characters can stall it for minutes.

Why This Matters for Superset

Superset's Cypress base runs in CI pipelines that are often exposed to external pull requests or parameterized build triggers. If any part of the build pipeline passes user-controlled strings through a glob or minimatch call backed by the vulnerable brace-expansion, an attacker can submit a crafted PR or trigger a parameterized build that hangs the runner indefinitely—a classic Continuous Integration denial of service attack.

Even outside CI, the package is present in the production repository. Trivy's assessment of "likely exploitable" reflects that the vulnerable code path is reachable, not merely theoretical.


The Fix

The fix is a precise version bump in two places within superset-frontend/cypress-base/:

package-lock.json — Before

"node_modules/brace-expansion": {
  "version": "5.0.7",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
  "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
  "dev": true,
  "peer": true,
  "dependencies": {
    "balanced-match": "^4.0.2"
  },
  "engines": {
    "node": "18 || 20 || >=22"
  }
}

package-lock.json — After

"node_modules/brace-expansion": {
  "version": "5.0.8",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
  "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
  "dev": true,
  "license": "MIT",
  "peer": true,
  "dependencies": {
    "balanced-match": "^4.0.2"
  },
  "engines": {
    "node": "20 || >=22"
  }
}

Three concrete changes stand out:

Field 5.0.7 5.0.8 Significance
version 5.0.7 5.0.8 Picks up the patched regex
integrity sha512-7oFy703... sha512-JZyDyq3... New tarball hash; npm will reject the old one
engines.node "18 \|\| 20 \|\| >=22" "20 \|\| >=22" Node 18 dropped from support matrix
license (absent) "MIT" Explicit SPDX identifier added

The integrity hash change is especially important from a supply-chain perspective: it means the lockfile now pins to a specific, audited tarball. Any attempt to serve the old 5.0.7 tarball under the new version number would fail the integrity check.

The same version bump is reflected in the flat "brace-expansion" entry lower in the lockfile (the legacy npm v1/v2 format section), ensuring both resolution paths point to the patched package.


Prevention & Best Practices

1. Run a Software Composition Analysis (SCA) scanner on every PR

Tools like Trivy, Snyk, or OWASP Dependency-Check will catch known-CVE package versions before they reach main. Integrate them as a required CI check, not an optional advisory one.

# Example: Trivy filesystem scan targeting the lockfile
trivy fs superset-frontend/cypress-base/package-lock.json \
  --severity HIGH,CRITICAL \
  --exit-code 1

2. Keep lockfiles committed and regularly updated

A committed package-lock.json gives scanners a precise target. Use npm audit or Dependabot/Renovate to automate patch-version bumps for known CVEs.

npm audit fix --package-lock-only

3. Treat test-tooling dependencies as production dependencies for security purposes

The dev: true flag in a lockfile does not mean "safe to ignore." If the lockfile lives in the repo, scanners will (correctly) flag it. Apply the same patching discipline to devDependencies as to runtime dependencies.

4. Avoid writing custom brace/glob parsers with hand-rolled regexes

If you ever need to parse shell-style patterns in Node.js, use a well-maintained library and keep it current. If you write your own regex for pattern matching, test it against tools like vuln-regex-detector or the ReDoS checker.

5. Relevant standards

  • CWE-400: Uncontrolled Resource Consumption — the canonical classification for ReDoS
  • CWE-1333: Inefficient Regular Expression Complexity — the more specific sub-classification
  • OWASP A05:2021 – Security Misconfiguration: Outdated or vulnerable components fall under this category
  • OWASP Dependency-Check: https://owasp.org/www-project-dependency-check/

Key Takeaways

  • brace-expansion ≤5.0.7 is vulnerable to ReDoS; any Node.js project with this version in its lockfile should upgrade immediately, regardless of whether the dependency is dev or peer.
  • Lockfile integrity hashes are a security control, not just a reproducibility aid—the new sha512 hash in 5.0.8 ensures the patched tarball is the only one npm will accept.
  • CI pipelines are attack surfaces; a crafted brace-expansion string passed through a glob call can hang a build runner, wasting compute and blocking deployments.
  • The engines field change (dropping Node 18 support) is a signal that 5.0.8 is a deliberate maintenance release, not just a patch bump—review your Node version compatibility when upgrading.
  • Trivy flagging a package-lock.json as "likely exploitable" should be treated as a blocking issue, not a warning to defer.

How Orbis AppSec Detected This

  • Source: The brace-expansion package version 5.0.7 declared in superset-frontend/cypress-base/package-lock.json, which is user-influenced in the sense that any contributor or automated tool can propose a lockfile containing this version.
  • Sink: The regex evaluation path inside brace-expansion's token parser, reachable whenever a glob pattern containing brace expressions is evaluated during the Cypress test run or any tooling that imports the package.
  • Missing control: No version constraint in package.json or lockfile pinning excluded the vulnerable 5.0.7 release; no SCA gate blocked the vulnerable version from being merged.
  • CWE: CWE-400 — Uncontrolled Resource Consumption (ReDoS variant, also CWE-1333).
  • Fix: Upgraded brace-expansion from 5.0.7 to 5.0.8 in both the node_modules and legacy flat sections of package-lock.json, replacing the vulnerable tarball integrity hash with the patched one.

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-14257 is a reminder that no dependency is too small to be dangerous. brace-expansion is a 200-line utility package, but its flawed regex was enough to earn a HIGH severity rating and a Trivy alert in one of the most widely deployed open-source analytics platforms in the world. The fix—bumping one version number and updating two integrity hashes—took seconds to apply but required a scanner to find in the first place.

Keep your lockfiles current, run SCA on every PR, and never assume that devDependencies are outside your threat model. The build pipeline that tests your secure code needs to be just as secure as the code it tests.


References

Frequently Asked Questions

What is a ReDoS (Regular Expression Denial of Service) vulnerability?

ReDoS occurs when a regex engine takes exponential time to evaluate certain crafted inputs due to catastrophic backtracking, allowing an attacker to freeze or crash a process with a small payload.

How do you prevent ReDoS in Node.js?

Use well-maintained regex libraries, audit dependencies with tools like Trivy or npm audit, prefer linear-time regex engines (e.g., RE2), and keep third-party packages up to date.

What CWE is ReDoS?

ReDoS is classified under CWE-400 (Uncontrolled Resource Consumption) and sometimes CWE-1333 (Inefficient Regular Expression Complexity).

Is input validation enough to prevent ReDoS?

Not always. If the vulnerable regex is inside a transitive dependency, user input may reach it indirectly. Patching the dependency is the most reliable fix.

Can static analysis detect ReDoS?

Yes. Tools like Trivy, Semgrep, and dedicated ReDoS scanners (e.g., vuln-regex-detector) can identify known-vulnerable package versions and dangerous regex patterns.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #42435

Related Articles

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 Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A high-severity misconfiguration in `.github/dependabot.yml` left three `package-ecosystem` entries without a cooldown period, meaning Dependabot could immediately propose updates from newly published—potentially malicious—packages. The fix adds a `cooldown` block with `default-days: 7` to each entry, introducing a mandatory waiting period before any newly released package version is surfaced as an update candidate. For a Node.js library whose vulnerabilities ripple downstream to all consumers,

critical

How Unauthenticated Proxy Endpoints Enable DoS Amplification in FastAPI and how to fix it

Public proxy endpoints in `backend/api/proxy.py` had no rate limiting, allowing any attacker to flood the httpx connection pool with unauthenticated requests and amplify denial-of-service attacks against downstream tile and coordinate-conversion services. The fix introduces a per-IP sliding-window rate limiter using environment-configurable thresholds, closing the amplification vector without breaking legitimate usage.

high

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

A missing `cooldown` block in `.github/dependabot.yml` meant that Dependabot could immediately propose updates to newly published npm packages — including those that may be malicious, compromised, or unstable. By adding a `cooldown` with `default-days: 7`, the project now waits one week before surfacing new package versions, giving the security community time to detect and flag bad releases before they reach production.