Back to Blog
high SEVERITY6 min read

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-14257) was discovered in the brace-expansion package within the zeroshot-oecp Docker container's dependency tree. The vulnerability allows attackers to craft malicious input patterns that trigger exponential-time processing, potentially freezing or crashing Node.js applications. This fix upgrades the nested brace-expansion dependency to version 5.0.9 using a targeted Dockerfile modification.

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

Answer Summary

CVE-2026-14257 is a denial of service vulnerability in the brace-expansion Node.js package (versions through 5.0.7) caused by exponential-time complexity when processing specially crafted brace patterns. This is related to CWE-1333 (Inefficient Regular Expression Complexity). The fix involves upgrading brace-expansion to version 5.0.9 and manually patching nested dependencies within the node_modules tree during the Docker build process.

Vulnerability at a Glance

cweCWE-1333
fixUpgrade brace-expansion to 5.0.9 and patch nested dependency in @earendil-works/pi-coding-agent
riskApplication freeze or crash from malicious input patterns
languageJavaScript/Node.js
root causeInefficient algorithm in brace-expansion handles nested patterns with exponential time complexity
vulnerabilityDenial of Service (Exponential-Time Complexity)

Introduction

In the zeroshot-oecp Docker container build, we discovered a high-severity denial of service vulnerability lurking in the dependency tree. The culprit? The brace-expansion package at version 5.0.6, nested within @earendil-works/pi-coding-agent. This vulnerability, tracked as CVE-2026-14257, could allow attackers to craft malicious input that triggers exponential-time processing—effectively freezing the application with a single carefully constructed string.

The fix required a creative approach: since the vulnerable package was a transitive dependency (a dependency of a dependency), simply updating package.json wasn't enough. The team had to modify the Dockerfile itself to surgically replace the vulnerable nested module during the build process.

The Vulnerability Explained

What is Brace Expansion?

Brace expansion is a shell feature that generates arbitrary strings. For example, {a,b,c} expands to a b c, and {1..5} expands to 1 2 3 4 5. The brace-expansion npm package provides this functionality for Node.js applications, commonly used by glob matching libraries like minimatch and micromatch.

The Exponential-Time Problem

The vulnerability in brace-expansion versions through 5.0.7 stems from how the package handles deeply nested or complex brace patterns. When processing certain malicious inputs, the algorithm's time complexity grows exponentially with input characteristics rather than linearly.

Consider a pattern like:

{{{{{{{{{a}}}}}}}}}

Each level of nesting can multiply the processing time, and with enough nesting or specific patterns, even a relatively short input string can cause the parser to run for minutes, hours, or indefinitely.

Attack Scenario for zeroshot-oecp

In the zeroshot-oecp service, the @earendil-works/pi-coding-agent package uses brace-expansion internally. If user-controlled input reaches this parsing logic—whether through file paths, configuration strings, or API parameters—an attacker could submit a crafted payload like:

// Malicious input example
const maliciousPattern = "{".repeat(30) + "a" + "}".repeat(30);

This input, when processed by the vulnerable brace-expansion library, would cause the Node.js event loop to block while the exponential algorithm churns through combinations. The result: the entire service becomes unresponsive, denying service to legitimate users.

Real-World Impact

For a containerized service like zeroshot-oecp:
- Service Unavailability: A single malicious request could freeze the container
- Resource Exhaustion: CPU usage spikes to 100% during the attack
- Cascading Failures: In orchestrated environments, frozen containers can trigger restarts, load balancer issues, and service degradation
- No Authentication Required: If the vulnerable code path is reachable from unauthenticated endpoints, any attacker can exploit it

The Fix

The Challenge: Nested Dependencies

The vulnerable brace-expansion@5.0.6 wasn't a direct dependency—it was nested inside @earendil-works/pi-coding-agent/node_modules/. Simply running npm update brace-expansion wouldn't touch this nested copy because npm's dependency resolution had locked it to the vulnerable version within that package's subtree.

The Solution: Dockerfile Surgery

The fix modifies docker/zeroshot-oecp/Dockerfile to manually patch the nested dependency during the build:

Before:

FROM docker.io/library/node:22-bookworm-slim@sha256:f32b81066cde10a75dbac96646099533316d94bac4150c55da1636e1f0ffdc46 AS node-deps
WORKDIR /opt/node-runtime
COPY docker/zeroshot-oecp/package.json docker/zeroshot-oecp/package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund

After:

FROM docker.io/library/node:22-bookworm-slim@sha256:f32b81066cde10a75dbac96646099533316d94bac4150c55da1636e1f0ffdc46 AS node-deps
WORKDIR /opt/node-runtime
COPY docker/zeroshot-oecp/package.json docker/zeroshot-oecp/package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund \
    && npm install brace-expansion@5.0.9 --no-save --ignore-scripts --no-audit --no-fund \
    && cp -r node_modules/brace-expansion/. node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion/ \
    && rm -rf node_modules/brace-expansion \
    && node -e "const pkg = require( \
      './node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion/package.json'); \
      if (pkg.version !== '5.0.9') throw new Error('expected brace-expansion@5.0.9, got ' + pkg.version)"

Breaking Down the Fix

  1. Install the patched version: npm install brace-expansion@5.0.9 --no-save downloads the fixed version without modifying package.json

  2. Copy to nested location: cp -r node_modules/brace-expansion/. node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion/ overwrites the vulnerable nested copy

  3. Clean up: rm -rf node_modules/brace-expansion removes the top-level copy since it's not needed as a direct dependency

  4. Verify the fix: The inline Node.js script confirms the patched version is in place, failing the build if the version doesn't match

This verification step is crucial—it ensures the fix actually worked and provides a clear error message if something goes wrong in future builds.

Prevention & Best Practices

1. Regular Dependency Auditing

Run security scans as part of your CI/CD pipeline:

# npm's built-in audit
npm audit

# Using Trivy for comprehensive scanning
trivy fs --scanners vuln .

2. Understand Your Dependency Tree

Use npm ls to visualize where packages are used:

npm ls brace-expansion

This helps identify nested dependencies that might be hiding vulnerabilities.

3. Consider Dependency Pinning Strategies

For critical applications:
- Use package-lock.json or yarn.lock consistently
- Consider using npm-shrinkwrap.json for published packages
- Implement automated dependency update tools like Dependabot or Renovate

4. Input Validation at Application Boundaries

Even with patched dependencies, validate and sanitize user input:

// Example: Limit pattern complexity before processing
function safeBraceExpand(pattern, maxLength = 100, maxNesting = 5) {
  if (pattern.length > maxLength) {
    throw new Error('Pattern too long');
  }

  const nestingDepth = (pattern.match(/{/g) || []).length;
  if (nestingDepth > maxNesting) {
    throw new Error('Pattern too complex');
  }

  return braceExpansion(pattern);
}

5. Implement Timeouts for Parsing Operations

Protect against algorithmic complexity attacks with timeouts:

const { setTimeout } = require('timers/promises');

async function parseWithTimeout(pattern, timeoutMs = 1000) {
  const controller = new AbortController();
  const timeout = setTimeout(timeoutMs, null, { signal: controller.signal })
    .then(() => { throw new Error('Parse timeout'); });

  try {
    return await Promise.race([
      Promise.resolve(braceExpansion(pattern)),
      timeout
    ]);
  } finally {
    controller.abort();
  }
}

Key Takeaways

  • Nested dependencies can hide vulnerabilities: The vulnerable brace-expansion was buried inside @earendil-works/pi-coding-agent/node_modules/, invisible to simple npm update commands
  • Dockerfile modifications can patch transitive dependencies: When npm's dependency resolution won't cooperate, surgical file operations during build can enforce security fixes
  • Always verify security fixes: The inline node -e verification ensures the patched version is actually installed, preventing silent failures
  • Exponential-time complexity is a real DoS vector: Even without memory corruption or injection, algorithmic inefficiency can take down services
  • Container builds should include security scanning: Tools like Trivy can catch these issues before deployment

How Orbis AppSec Detected This

  • Source: User-controlled input potentially reaching glob/path matching operations in the pi-coding-agent module
  • Sink: brace-expansion parsing function within node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion/
  • Missing control: No version constraint ensuring brace-expansion >= 5.0.8 in the nested dependency tree
  • CWE: CWE-1333 (Inefficient Regular Expression Complexity)
  • Fix: Upgraded brace-expansion to 5.0.9 by modifying the Dockerfile to manually patch the nested dependency and verify the installation

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 demonstrates that denial of service vulnerabilities don't require exotic exploits—sometimes an inefficient algorithm is all it takes. The brace-expansion vulnerability shows how transitive dependencies can introduce risk deep in your dependency tree, and how creative solutions like Dockerfile modifications may be necessary to patch them.

For Node.js developers, this is a reminder to:
1. Regularly audit your full dependency tree, not just direct dependencies
2. Understand where user input flows through your application
3. Implement defense-in-depth with input validation and timeouts
4. Use automated security scanning in your CI/CD pipeline

Security is an ongoing process, and staying ahead of vulnerabilities like this requires vigilance and the right tools.

References

Frequently Asked Questions

What is exponential-time complexity DoS?

A vulnerability where specially crafted input causes an algorithm to run in exponential time relative to input size, consuming excessive CPU resources and potentially freezing or crashing the application.

How do you prevent exponential-time DoS in Node.js?

Keep dependencies updated, implement input validation with size limits, use timeouts for parsing operations, and regularly scan dependencies with tools like Trivy or npm audit.

What CWE is exponential-time complexity DoS?

CWE-1333 (Inefficient Regular Expression Complexity) covers this class of algorithmic complexity vulnerabilities, though it can apply to non-regex algorithms like brace expansion.

Is input length validation enough to prevent this DoS?

Not always—exponential complexity can be triggered by relatively short but deeply nested patterns. Upgrading to patched versions is the most reliable mitigation.

Can static analysis detect exponential-time complexity vulnerabilities?

Yes, tools like Trivy, Snyk, and npm audit can detect known CVEs in dependencies. However, detecting novel algorithmic complexity issues often requires specialized analysis or fuzzing.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #938

Related Articles

critical

How Insecure API Key Transmission Happens in JavaScript Browser Extensions and How to Fix It

A critical vulnerability in `utils/common.js` allowed API keys to be transmitted over unencrypted HTTP connections to remote servers, exposing them to network interception. The `buildModelApiRequest` function at line 490 constructed API requests without validating the transport protocol, enabling man-in-the-middle attacks. The fix enforces HTTPS for all remote API endpoints while preserving HTTP access for local development servers on loopback addresses.

critical

How URL Injection via Unvalidated User Input happens in Node.js and how to fix it

A critical URL injection vulnerability in the QQ info lookup feature allowed attackers to manipulate API request parameters by sending specially crafted messages. Without proper input validation, user-controlled data was directly embedded into external API URLs, potentially exposing sensitive authentication credentials (skey and pskey) to attacker-controlled servers.

high

How Cache-Control Header Mishandling Happens in Node.js HTTP Clients and How to Fix It

CVE-2026-13697 is a high-severity vulnerability in undici, the popular Node.js HTTP client, where the cache interceptor fails to properly validate malformed `Cache-Control: private` directives. This could allow sensitive cached responses to be served to unauthorized users. The fix upgrades undici from 7.28.0 to 7.29.0 (and 6.27.0 to 6.28.0) across the dependency tree, including using npm overrides to patch transitive dependencies.

medium

How XML Entity Expansion Denial of Service happens in Node.js and how to fix it

A critical denial of service vulnerability (CVE-2026-33036) was discovered in fast-xml-parser versions prior to 5.5.6 and 4.5.5, allowing attackers to bypass entity expansion limits and crash Node.js applications through malicious XML payloads. This fix upgrades the dependency in the scripts directory to patched versions, protecting build pipelines and any runtime XML processing from resource exhaustion attacks.

critical

How Arbitrary Code Execution via Command Injection Happens in Node.js shell-quote and How to Fix It

A critical command injection vulnerability (CVE-2026-9277) was discovered in the popular Node.js `shell-quote` package (versions prior to 1.8.4) where unescaped line terminators allowed attackers to inject and execute arbitrary shell commands. The fix upgrades `shell-quote` from version 1.8.1 to 1.8.4, which properly escapes line terminator characters (such as `\n`, `\r`, `\u2028`, and `\u2029`) before passing strings to the shell. This dependency was present in the project's `package-lock.json`

high

How Octal/Decimal IP Parsing Ambiguity happens in JavaScript and how to fix it

CVE-2026-69192 is a high-severity vulnerability in the `ip-address` npm package (versions before 10.3.1) where IPv4 addresses with leading-zero octets — like `010.0.0.1` — are parsed as decimal by the library but interpreted as octal by OS-level resolvers, creating a dangerous mismatch. This discrepancy can allow attackers to bypass IP-based access controls and trust boundaries, potentially enabling Server-Side Request Forgery (SSRF) attacks. Upgrading to `ip-address@10.3.1` in the SAP BW Query