Back to Blog
high SEVERITY6 min read

How pnpm Missing Minimum Release Age happens in Node.js workspaces and how to fix it

A pnpm workspace configuration had `minimumReleaseAge` set to `0`, meaning newly published npm packages could be installed immediately—before the community has time to detect malicious or compromised releases. By changing this value to `10080` (seven days in minutes), the project now enforces a quarantine window that dramatically reduces exposure to typosquatting, dependency confusion, and post-publish malware injection attacks.

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

Answer Summary

This vulnerability is a missing minimum release age in a pnpm workspace configuration (`pnpm-workspace.yaml`), which maps to supply chain security risks (CWE-1357: Reliance on Insufficiently Trustworthy Component). With `minimumReleaseAge: 0`, pnpm will immediately install any newly published package version, including malicious packages published seconds before a `pnpm install` run. The fix is to set `minimumReleaseAge: 10080` in `pnpm-workspace.yaml`, which tells pnpm to refuse installing any package version published within the last 7 days (10,080 minutes), giving the security community time to detect and respond to malicious releases.

Vulnerability at a Glance

cweCWE-1357
fixSet `minimumReleaseAge: 10080` to enforce a 7-day waiting period before new package versions can be installed
riskImmediate installation of newly published, potentially malicious package versions
languageYAML / Node.js (pnpm)
root cause`minimumReleaseAge: 0` in `pnpm-workspace.yaml` disables the quarantine window for new package releases
vulnerabilityMissing Minimum Release Age (Supply Chain Risk)

The Risk Hidden in a Single Zero

In a Node.js library's pnpm-workspace.yaml, a single configuration line read:

minimumReleaseAge: 0

That zero is not a neutral default—it is an open door. It tells pnpm: install any package version the moment it appears on the npm registry, no matter how new it is. For a project that downstream consumers depend on, this setting means that a supply chain attack targeting a transitive dependency could propagate to production systems within minutes of the malicious package being published.

This post explains what the minimumReleaseAge setting does, why the value 0 is dangerous, and how setting it to 10080 (seven days in minutes) closes a real attack vector that has been exploited in the wild.


The Vulnerability Explained

What minimumReleaseAge Controls

pnpm v10.16.0 introduced the minimumReleaseAge setting in workspace configuration. It accepts a value in minutes and instructs pnpm to refuse resolving any package version that was published to the npm registry more recently than that threshold.

When set to 0, the feature is effectively disabled—pnpm will happily resolve and install a package version published 30 seconds ago.

The Vulnerable Configuration

Here is the configuration as it existed before the fix, in pnpm-workspace.yaml at line 5:

allowBuilds:
  esbuild: true
  unrs-resolver: true
  vue-demi: true
minimumReleaseAge: 0

The allowBuilds section is correctly configured, but the minimumReleaseAge: 0 line actively opts out of the quarantine window.

How This Gets Exploited

The npm ecosystem has seen a consistent pattern of attacks that this setting directly mitigates:

  1. Account takeover / credential stuffing: An attacker compromises a maintainer's npm credentials and publishes a new patch version (e.g., some-util@2.3.1) containing malicious code. With minimumReleaseAge: 0, the next pnpm install or CI run picks it up immediately.

  2. Typosquatting and dependency confusion: A new package with a name close to a popular one is published. Automated bots and CI pipelines that run installs frequently are the first victims.

  3. Protestware / supply chain sabotage: A maintainer intentionally injects malicious code into a new version. The 7-day window gives the community time to discover, report, and for registries to yank the package.

Real-World Impact for This Repository

This file is in a Node.js library that downstream consumers install. If a malicious version of a dependency were installed into this library's build or test pipeline, it could:

  • Exfiltrate environment variables (CI secrets, npm tokens, cloud credentials) during pnpm install
  • Tamper with the built output that gets published to npm, affecting all downstream users
  • Execute arbitrary scripts via pnpm's allowBuilds lifecycle hooks (which are already enabled for esbuild, unrs-resolver, and vue-demi)

The combination of allowBuilds being enabled and minimumReleaseAge: 0 is particularly risky: build scripts run during install, so a malicious package that lands in the dependency tree can execute code immediately.


The Fix

The change is minimal but meaningful:

-minimumReleaseAge: 0
+minimumReleaseAge: 10080

Before:

allowBuilds:
  esbuild: true
  unrs-resolver: true
  vue-demi: true
minimumReleaseAge: 0

After:

allowBuilds:
  esbuild: true
  unrs-resolver: true
  vue-demi: true
minimumReleaseAge: 10080

Why 10080?

10080 is the number of minutes in seven days (7 × 24 × 60 = 10,080). This is the value recommended by the pnpm documentation and the value flagged by the Semgrep rule. Seven days is considered a reasonable window because:

  • Most malicious packages are detected and removed by npm's security team or community reporters within hours to days
  • Legitimate patch releases rarely need to be consumed within minutes; most projects can tolerate a week's delay on transitive dependency updates
  • Security researchers and automated scanning tools (like Socket.dev and Snyk) typically surface issues within this window

What pnpm Does With This Setting

When minimumReleaseAge: 10080 is set, pnpm checks the time field in a package's npm registry metadata during resolution. If the version's publish timestamp is less than 10,080 minutes ago, pnpm skips that version and resolves to the next-oldest version that satisfies the semver range. This happens transparently during pnpm install—no error, no manual intervention needed.


Prevention & Best Practices

1. Always Set minimumReleaseAge in New pnpm Workspaces

When creating a new pnpm-workspace.yaml, include this setting from day one:

minimumReleaseAge: 10080

It was added in pnpm v10.16.0, so ensure your team is on a recent enough version.

2. Combine With Other Supply Chain Controls

minimumReleaseAge is one layer. Pair it with:

  • Lockfiles committed to source control: Prevents unexpected resolution changes between installs
  • pnpm audit in CI: Catches known CVEs in the resolved dependency tree
  • Provenance attestation: Use packages published with npm provenance where available
  • Dependency review in PRs: Tools like Dependabot or Renovate can flag new dependency additions for human review

3. Audit Your allowBuilds List

This project enables build scripts for esbuild, unrs-resolver, and vue-demi. Each of these is a potential code execution point during install. Periodically review this list and remove entries for packages that no longer need it.

4. Static Analysis in CI

The Semgrep rule package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age detects this pattern automatically. Add it to your CI pipeline to catch regressions:

semgrep --config "p/supply-chain" pnpm-workspace.yaml

Relevant Standards

  • CWE-1357: Reliance on Insufficiently Trustworthy Component
  • OWASP A06:2021: Vulnerable and Outdated Components
  • SLSA Supply Chain Threats: Compromised dependency (threat D)

Key Takeaways

  • minimumReleaseAge: 0 is not a safe default—it actively disables a security control that pnpm provides specifically to combat supply chain attacks.
  • The combination of allowBuilds and minimumReleaseAge: 0 in this pnpm-workspace.yaml was especially risky: build scripts execute during install, so a newly published malicious package could run code immediately.
  • 10,080 minutes (7 days) is the recommended quarantine window because it aligns with the typical detection and response time for malicious npm packages.
  • Downstream consumers of this Node.js library were indirectly protected by this fix—a compromised build pipeline could have resulted in a tainted published package.
  • Static analysis tools like Semgrep can detect this in seconds—there is no reason to leave this misconfiguration undetected in a CI pipeline.

How Orbis AppSec Detected This

  • Source: The pnpm-workspace.yaml configuration file, which governs dependency resolution for all packages in the workspace
  • Sink: The minimumReleaseAge: 0 setting at line 5, which instructs pnpm to resolve and install package versions with no age restriction—including versions published seconds before an install run
  • Missing control: No minimum release age threshold was enforced, meaning newly published (potentially malicious) package versions were immediately eligible for installation
  • CWE: CWE-1357 — Reliance on Insufficiently Trustworthy Component
  • Fix: Changed minimumReleaseAge from 0 to 10080 in pnpm-workspace.yaml, enforcing a 7-day quarantine window on all newly published package versions

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

A single zero in a YAML configuration file was all it took to leave this Node.js library's dependency resolution wide open to supply chain attacks. The fix—changing minimumReleaseAge: 0 to minimumReleaseAge: 10080—took one line but added a meaningful, automated defense layer against one of the most active attack categories targeting the npm ecosystem today.

Supply chain attacks are not theoretical. The npm registry sees malicious packages published regularly, and the attack window between publish and detection is often measured in hours. A 7-day quarantine window, enforced automatically by pnpm, is a low-friction, high-value control that every workspace should have enabled.

If your project uses pnpm, open your pnpm-workspace.yaml right now and check whether minimumReleaseAge is set. If it is missing or set to 0, you have the same vulnerability this PR just fixed.


References

Frequently Asked Questions

What is a missing minimum release age in pnpm?

It means pnpm is configured to allow installation of package versions the moment they are published to the npm registry, with no waiting period. This creates a window where malicious or compromised packages can be installed before the community detects them.

How do you prevent supply chain attacks via new package versions in pnpm?

Add `minimumReleaseAge: 10080` to your `pnpm-workspace.yaml` file. This tells pnpm to skip any package version published within the last 7 days (10,080 minutes), giving the ecosystem time to flag malicious releases.

What CWE is missing minimum release age?

CWE-1357 (Reliance on Insufficiently Trustworthy Component) is the closest match, as the root issue is trusting newly published, unvetted package versions without a quarantine period.

Is locking package versions with a lockfile enough to prevent this?

A lockfile protects against unexpected version changes in existing installs, but it does not protect you when you first add a dependency or when a maintainer's account is compromised and a new version is published. `minimumReleaseAge` adds a complementary layer of defense.

Can static analysis detect missing minimum release age?

Yes. Semgrep rule `package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age` detects this pattern by checking whether `minimumReleaseAge` is absent or set to `0` in `pnpm-workspace.yaml`.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #896

Related Articles

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

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.