Back to Blog
critical SEVERITY5 min read

How Supply Chain Vulnerabilities Happen in pnpm Workspaces and How to Fix Them

A critical supply chain vulnerability in a pnpm workspace configuration allowed immediate installation of newly published packages, exposing downstream consumers to potentially malicious dependencies. The fix adds `minimumReleaseAge: 10080` and two additional hardening directives to enforce a seven-day quarantine period.

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

Answer Summary

This is a **supply chain security vulnerability** in pnpm workspace configuration (CWE-829: Inclusion of Functionality from Untrusted Control Sphere). The vulnerable `pnpm-workspace.yaml` lacked `minimumReleaseAge`, allowing immediate installation of packages that could be malicious or compromised. The fix adds `minimumReleaseAge: 10080` (7 days), `blockExoticSubdeps: true`, and `trustPolicy: no-downgrade` to enforce package maturity validation and prevent downgrade attacks.

Vulnerability at a Glance

cweCWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
fixAdded minimumReleaseAge: 10080, blockExoticSubdeps: true, and trustPolicy: no-downgrade
riskImmediate consumption of malicious or compromised packages published to npm registry
languageYAML (pnpm configuration)
root causeMissing minimumReleaseAge setting in pnpm-workspace.yaml allowed zero-day package installation
vulnerabilitySupply Chain / Dependency Confusion

How Supply Chain Vulnerabilities Happen in pnpm Workspaces and How to Fix Them

In a Node.js library repository, we discovered a critical supply chain exposure in pnpm-workspace.yaml at line 1. The configuration lacked package maturity validation, creating a window where newly published malicious dependencies could be immediately consumed by downstream users—before the security community could detect and flag them.

This vulnerability represents a growing class of supply chain attacks where the attack surface isn't your code, but the code you trust. With npm publishing over 1 million packages weekly, the "freshness" of a package has become a security signal. Zero-day packages—those published minutes or hours ago—carry disproportionate risk.

The Vulnerability Explained

The vulnerable pnpm-workspace.yaml file defined workspace catalogs without any release age enforcement:

catalog:
  vite: npm:@voidzero-dev/vite-plus-core@0.2.9
  vite-plus: 0.2.9

The critical missing line: No minimumReleaseAge directive meant pnpm would install packages immediately upon publication, regardless of how recently they appeared on the registry.

How This Could Be Exploited

An attacker could exploit this through several chained scenarios:

  1. Compromised Maintainer Account: An attacker gains access to a popular package (e.g., vite or dependencies in the catalog) and publishes a malicious patch version. Downstream consumers using this workspace would install it within minutes—before the compromise is detected and the package flagged.

  2. Typosquatting with Immediate Propagation: An attacker publishes vite-plu (typo of vite-plus) with a higher version number. If dependency resolution falls through or if exotic subdependencies are permitted, the malicious package installs without delay.

  3. Dependency Confusion: A private package name in the catalog without proper scoping could be shadowed by a public malicious package published to npm. The zero-maturity window means no community vetting period exists.

The real-world impact extends beyond this repository to all downstream consumers. As a library, this package's vulnerable configuration propagates through dependency trees, affecting applications that transitively include these workspace definitions.

The Fix

The pull request harden: this pnpm workspace configuration does not set ... in... implemented three defensive controls at the start of pnpm-workspace.yaml:

+minimumReleaseAge: 10080
+blockExoticSubdeps: true
+trustPolicy: no-downgrade
+
 catalog:
   vite: npm:@voidzero-dev/vite-plus-core@0.2.9
   vite-plus: 0.2.9
Directive Purpose Security Benefit
minimumReleaseAge: 10080 Requires packages to exist for 10,080 minutes (7 days) before installation Creates community vetting window; malicious packages typically detected within 24-72 hours
blockExoticSubdeps: true Prevents resolution of "exotic" dependencies (git urls, tarballs, etc.) Clones attack surface to registry-only packages with established metadata
trustPolicy: no-downgrade Prevents version downgrade attacks Stops attackers from forcing installation of known-vulnerable older versions

Why These Three Controls Together?

The minimumReleaseAge alone addresses supply chain poisoning, but blockExoticSubdeps eliminates a bypass vector where attackers could reference malicious code via direct URLs. The trustPolicy: no-downgrade prevents a subtle attack where an attacker tricks dependency resolution into installing an older, vulnerable version that predates security patches.

This defense-in-depth approach anticipates how automated exploit development tools might chain these primitives—each control removes an attack path that could be combined with other weaknesses.

Prevention & Best Practices

For pnpm Workspace Maintainers

  1. Always set minimumReleaseAge: Start with 10080 minutes (7 days) and adjust based on your security requirements. Critical infrastructure may warrant 30+ days.

  2. Enable blockExoticSubdeps: Unless you specifically need git URL or tarball dependencies, this should be enabled workspace-wide.

  3. Use trustPolicy appropriately:
    - no-downgrade for most cases
    - strict for maximum security (requires explicit package verification)

  4. Audit catalog dependencies: The @voidzero-dev/vite-plus-core scoped package in this example is good practice—scoped packages reduce typosquatting risk.

Detection Tools

  • Semgrep: Rule package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age specifically flags this vulnerability
  • pnpm audit: Run pnpm audit regularly for known vulnerabilities
  • Dependency scanning: Tools like Snyk, Dependabot, and Orbis AppSec monitor for supply chain risks

Security Standards

  • OWASP Software Component Verification Standard (SCVS): Domain 2 (Software Component Analysis)
  • CWE-829: Inclusion of Functionality from Untrusted Control Sphere
  • SLSA (Supply-chain Levels for Software Artifacts): Level 1+ requirements for dependency verification

Key Takeaways

  • Never omit minimumReleaseAge in production pnpm workspaces—the default behavior of immediate package installation is unsafe for supply chain security
  • The catalog: section in pnpm-workspace.yaml requires the same hardening as direct dependencies—workspace catalogs are frequently overlooked in security reviews
  • Scoped packages (@voidzero-dev/...) reduce but don't eliminate supply chain risk—maturity validation remains essential regardless of package scope
  • Three-line configuration changes can prevent systemic supply chain attacks—the fix scope was minimal but the security impact was maximal

How Orbis AppSec Detected This

Source: The pnpm-workspace.yaml file itself, which defines workspace-wide dependency resolution behavior affecting all packages in the workspace.

Sink: The implicit package installation behavior when minimumReleaseAge is unset, allowing immediate consumption of registry packages at pnpm-workspace.yaml:1 and affecting all catalog entries.

Missing control: No maturity validation (minimumReleaseAge), no exotic dependency blocking (blockExoticSubdeps), and no downgrade protection (trustPolicy).

CWE: CWE-829: Inclusion of Functionality from Untrusted Control Sphere

Fix: Added minimumReleaseAge: 10080, blockExoticSubdeps: true, and trustPolicy: no-downgrade to enforce package maturity validation and prevent downgrade attacks.

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

Supply chain security in JavaScript ecosystems requires configuration-level defenses, not just dependency scanning. The pnpm-workspace.yaml vulnerability demonstrates how a single missing directive can expose entire dependency trees to zero-day package attacks.

The fix—three lines of YAML configuration—creates a seven-day security buffer that aligns with industry detection timelines. For library maintainers, this is particularly critical: your configuration choices propagate to hundreds or thousands of downstream applications.

Review your pnpm workspace configurations today. The minimumReleaseAge setting costs nothing to implement and provides substantial protection against the fastest-growing category of software supply chain attacks.


References

Frequently Asked Questions

What is a supply chain vulnerability in pnpm?

A configuration flaw that allows immediate installation of untrusted packages without maturity validation, exposing projects to malicious dependencies published to public registries.

How do you prevent supply chain vulnerabilities in pnpm?

Set `minimumReleaseAge` in pnpm-workspace.yaml to enforce a waiting period (recommended: 10080 minutes/7 days), enable `blockExoticSubdeps` to prevent exotic dependency resolution, and use `trustPolicy: no-downgrade` to prevent version rollback attacks.

What CWE is this pnpm supply chain vulnerability?

CWE-829: Inclusion of Functionality from Untrusted Control Sphere, specifically related to insufficient verification of package authenticity and maturity.

Is using private registries enough to prevent this supply chain vulnerability?

No. Private registries can still proxy or cache malicious packages. The `minimumReleaseAge` defense is registry-agnostic and protects against compromised maintainer accounts and typo-squatting attacks regardless of registry source.

Can static analysis detect missing minimumReleaseAge in pnpm?

Yes. Semgrep rules specifically flag missing `minimumReleaseAge` in pnpm-workspace.yaml files as a HIGH severity issue, as demonstrated by the automated detection in this case.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #9

Related Articles

high

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

A high-severity vulnerability in the nanoid package (CVE-2026-67213) could trigger an infinite loop in random ID generation when processing specially crafted input. This fix upgrades nanoid from version 3.3.12 to 3.3.18 and 5.1.6, eliminating the denial-of-service attack vector in the frontend application's dependency tree.

critical

How Rate Limiting Vulnerabilities Happen in FastAPI and How to Fix Them

A critical denial-of-service vulnerability was discovered in a FastAPI application controlling Tesla Powerwall systems, where all 113+ API endpoints—including critical control endpoints for `/control/reserve` and `/control/mode`—lacked any rate limiting protection. An attacker could flood these endpoints with unlimited requests, exhausting server resources and disrupting powerwall monitoring and control operations. The fix introduces a configurable, pure-ASGI rate limiting middleware that can be

critical

How Command Injection Happens in Python Flask Applications and How to Fix It

A critical command injection vulnerability was discovered in a Flask application where `subprocess.Popen` and `subprocess.run` were called with `shell=True`, allowing attackers to execute arbitrary system commands through shell metacharacters. The fix replaces dangerous shell execution with `shlex.split()` for proper argument parsing and sets `shell=False` to prevent command injection attacks.

critical

How Resource Exhaustion via Missing Fetch Timeouts Happens in Node.js and How to Fix It

A critical resource exhaustion vulnerability was discovered in the `dsh-plugin-marketplace` GitHub client where multiple `fetch()` calls in `lib/index.js` lacked timeout configuration. While one fetch call at line 1161 correctly used `AbortSignal.timeout()`, other calls at lines 101 and 145 had no timeout mechanism, allowing attackers to exhaust connection pools by targeting slow or unresponsive GitHub API endpoints. The fix ensures all fetch operations consistently apply the configurable `regis

high

How Denial of Service via Invalid Binary POST Requests happens in Socket.IO and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-59725) was discovered in engine.io versions prior to 6.6.7, where invalid binary POST requests could crash Socket.IO servers. The fix upgrades engine.io from 6.6.5 to 6.6.7, which includes improved validation for binary packet handling and prevents malformed requests from taking down real-time communication channels.

critical

How a vulnerable websocket-driver dependency happens in Node.js lockfiles and how to fix it

A Trivy scan flagged `websocket-driver@0.7.4` in this repository's `bun.lock` as affected by CVE-2026-54466, a critical issue in a WebSocket protocol handler that parses untrusted HTTP upgrade requests and frame data. The fix upgrades the package to `0.7.5` and adds an explicit `websocket-driver` entry to the lockfile's override block so every transitive consumer — webpack-dev-server, sockjs, faye-websocket — resolves to the patched build instead of the pinned vulnerable one.