Back to Blog
high SEVERITY7 min read

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.

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

Answer Summary

A missing `minimumReleaseAge` in `pnpm-workspace.yaml` is a supply chain security misconfiguration (related to CWE-1357: Reliance on Insufficiently Trustworthy Component) that allows pnpm to immediately install newly published npm packages, including malicious or compromised ones. In pnpm v10.16.0+, you can fix this by adding `minimumReleaseAge: 10080` to your `pnpm-workspace.yaml`, which enforces a 7-day waiting period before any new package version can be installed, giving the security community time to detect and flag malicious releases.

Vulnerability at a Glance

cweCWE-1357 (Reliance on Insufficiently Trustworthy Component)
fixAdd minimumReleaseAge: 10080 to pnpm-workspace.yaml to enforce a 7-day quarantine window
riskAutomatic installation of newly published malicious or compromised npm packages
languageNode.js / YAML (pnpm configuration)
root causepnpm-workspace.yaml lacks a minimumReleaseAge setting, allowing zero-delay package installation
vulnerabilityMissing Minimum Release Age in pnpm Workspace

The Silent Risk in Your pnpm Workspace

Every time a developer runs pnpm install, pnpm reaches out to the npm registry and resolves the best matching version of each dependency. By default, it will happily install a package version that was published seconds ago — no questions asked. That zero-delay trust is exactly what supply chain attackers count on.

In a recent security audit of a Node.js library's pnpm-workspace.yaml, Orbis AppSec flagged a high-severity misconfiguration: the workspace had no minimumReleaseAge setting. A single line was missing, and its absence meant that any newly published npm package — including a typosquatted, dependency-confused, or account-hijacked one — could be pulled into the build immediately after publication, with no waiting period for the community or security tooling to catch it.


The Vulnerability Explained

What minimumReleaseAge Does (and Doesn't Do by Default)

pnpm v10.16.0 introduced the minimumReleaseAge setting in pnpm-workspace.yaml. When set, it instructs pnpm to refuse to install any package version that was published to the npm registry more recently than the specified number of minutes ago. It acts as a quarantine window.

When this setting is absent — as it was in this project — pnpm's default behavior is to install any version as soon as it is available on the registry. Here is what the vulnerable configuration looked like:

# pnpm-workspace.yaml (before fix)
allowBuilds:
  puppeteer: false
  unrs-resolver: false

minimumReleaseAgeExclude:
  - "@qlik/*"

Notice that minimumReleaseAgeExclude is present — a list of packages exempted from the release age check — but there is no minimumReleaseAge value to apply in the first place. This is a subtle but critical gap: the exclusion list is defined, but the rule it references does not exist.

How This Gets Exploited

Supply chain attacks targeting npm have grown dramatically in sophistication. The attack patterns that minimumReleaseAge defends against include:

Dependency Confusion: An attacker publishes a public npm package with the same name as a private internal package, hoping the registry resolves to theirs first. The window of maximum risk is the first few hours after publication, before security researchers or automated scanners flag it.

Account Takeover / Maintainer Compromise: A legitimate package maintainer's npm account is compromised, and the attacker publishes a malicious patch version (e.g., lodash@4.17.22) containing a backdoor. Without a release age window, this version can be installed the moment it lands.

Typosquatting: A package named expres (missing the s) is published to catch developers who mistype express. New publications are most dangerous before they appear in threat intelligence feeds.

In this specific project — a Node.js library consumed by downstream users — a supply chain compromise would not just affect this repository. It would propagate to every project that depends on this library, multiplying the blast radius significantly.

The Specific Misconfiguration at Line 1

Semgrep's rule package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age matched this file at line 1 because the entire pnpm-workspace.yaml lacks the minimumReleaseAge key. The presence of minimumReleaseAgeExclude without a corresponding minimumReleaseAge is particularly misleading — it creates the appearance of a policy while providing none of the protection.


The Fix

The fix is minimal and precise. A single setting was added to pnpm-workspace.yaml:

# pnpm-workspace.yaml (after fix)
allowBuilds:
  puppeteer: false
  unrs-resolver: false

minimumReleaseAge: 10080

minimumReleaseAgeExclude:
  - "@qlik/*"

Before vs. After

Before After
Setting present ✗ Missing minimumReleaseAge: 10080
Quarantine window 0 minutes (instant) 10,080 minutes (7 days)
Exclusions active Defined but inert Correctly applied to @qlik/*
Supply chain risk High Significantly reduced

Why 10080?

10080 is the number of minutes in exactly 7 days (7 × 24 × 60 = 10,080). This is the value recommended in the pnpm documentation and represents a pragmatic balance:

  • Long enough for security researchers, automated scanners, and the community to identify malicious packages before they reach your build.
  • Short enough that it does not meaningfully delay legitimate dependency updates in most development workflows.
  • Consistent with the npm ecosystem's informal "watch period" that many security teams apply manually.

The @qlik/* Exclusion

The existing minimumReleaseAgeExclude entry for @qlik/* packages is now functional. This is appropriate for first-party or highly trusted scoped packages where the team wants to consume updates immediately. The key point is that this exclusion is now a deliberate, documented exception to an active policy — not an orphaned configuration key.


Prevention & Best Practices

1. Enable minimumReleaseAge in Every pnpm Workspace

Any project using pnpm v10.16.0 or later should include this in pnpm-workspace.yaml:

minimumReleaseAge: 10080

If you have packages that legitimately need faster update cycles (internal packages, trusted scoped registries), use minimumReleaseAgeExclude judiciously:

minimumReleaseAge: 10080
minimumReleaseAgeExclude:
  - "your-internal-scope/*"

2. Combine with Other Supply Chain Controls

minimumReleaseAge is one layer in a defense-in-depth strategy. Pair it with:

  • allowBuilds allowlist (already present in this repo): Restricts which packages can run postinstall scripts, preventing malicious build-time code execution.
  • Lockfile integrity checks: Commit and verify pnpm-lock.yaml in CI to detect unexpected dependency changes.
  • Dependency review in PRs: Use tools like pnpm audit or GitHub's dependency review action to flag new vulnerabilities before merging.
  • Private registry mirroring: Route package installs through a controlled registry (e.g., Verdaccio, Artifactory) that applies its own vetting policies.

3. Audit Your pnpm-workspace.yaml with Semgrep

The Semgrep rule that caught this issue can be run locally:

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

Or add it to your CI pipeline to catch regressions:

# .github/workflows/security.yml
- name: Semgrep scan
  uses: semgrep/semgrep-action@v1
  with:
    config: p/supply-chain

4. Understand the Threat Model for Libraries

This repository is a Node.js library — its consumers inherit its dependency tree. A compromised transitive dependency here does not just affect this project; it affects every downstream application. Library maintainers have a heightened responsibility to apply supply chain controls because their security posture directly impacts their users' security posture.

Relevant Standards

  • CWE-1357: Reliance on Insufficiently Trustworthy Component
  • OWASP A06:2021: Vulnerable and Outdated Components
  • SLSA (Supply Chain Levels for Software Artifacts): Recommends provenance verification and controlled dependency ingestion

Key Takeaways

  • minimumReleaseAgeExclude without minimumReleaseAge is a no-op — the exclusion list in pnpm-workspace.yaml was defined but had nothing to exclude from, creating a false sense of security.
  • Zero-delay package installation is the default — pnpm (and npm/yarn) will install packages the moment they appear on the registry unless you explicitly configure a waiting period.
  • 10,080 minutes (7 days) is the recommended quarantine window — this matches community expectations for vetting new package versions and is the value specified in pnpm's official documentation.
  • Library projects carry amplified supply chain risk — a compromised dependency in a published library propagates to all downstream consumers, making these controls especially important for reusable packages.
  • Static analysis can catch configuration-level supply chain gaps — this vulnerability was not in application code but in a YAML configuration file, demonstrating that security scanning must cover infrastructure and tooling configs, not just source code.

How Orbis AppSec Detected This

  • Source: The pnpm-workspace.yaml file at line 1, which controls how pnpm resolves and installs all workspace dependencies from the npm registry.
  • Sink: Any pnpm install invocation that resolves a newly published package version — the dangerous "call site" is the package resolution step itself, where a freshly published malicious package could be fetched without delay.
  • Missing control: The minimumReleaseAge key was entirely absent from pnpm-workspace.yaml, meaning pnpm applied no time-based trust policy to incoming package versions.
  • CWE: CWE-1357 — Reliance on Insufficiently Trustworthy Component
  • Fix: Added minimumReleaseAge: 10080 to pnpm-workspace.yaml, enforcing a 7-day quarantine window before any newly published package version can be installed.

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 missing line in pnpm-workspace.yamlminimumReleaseAge: 10080 — was the difference between a workspace that blindly trusts the npm registry in real time and one that applies a 7-day vetting window to every new package version. This is not a theoretical risk: supply chain attacks via freshly published packages are an active and growing threat vector, and the npm ecosystem has seen high-profile incidents of exactly this kind.

The fix is trivially small. The protection it provides is substantial. If your project uses pnpm v10.16.0 or later and your pnpm-workspace.yaml does not include minimumReleaseAge, add it today — especially if you maintain a library consumed by others.


References

Frequently Asked Questions

What is a missing minimumReleaseAge in pnpm?

It means pnpm will install newly published package versions immediately, without any waiting period. This exposes your project to packages that were just published and may be malicious, compromised, or unstable before the community has had a chance to vet them.

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

Add `minimumReleaseAge: 10080` to your `pnpm-workspace.yaml` file. This setting (available in pnpm v10.16.0+) prevents pnpm from installing any package version published less than 10,080 minutes (7 days) ago.

What CWE is missing minimumReleaseAge?

It maps most closely to CWE-1357: Reliance on Insufficiently Trustworthy Component, as the configuration allows unconditional trust in any package version the moment it appears on the npm registry.

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

A lockfile protects against version drift in existing installs, but it does not protect you during the initial resolution of new dependencies or when a lockfile is regenerated. The minimumReleaseAge setting adds a time-based trust layer that lockfiles alone cannot provide.

Can static analysis detect missing minimumReleaseAge?

Yes. Semgrep rule `package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age` detects this misconfiguration by scanning `pnpm-workspace.yaml` for the absence of the `minimumReleaseAge` key.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1058

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 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.

high

How Shell Injection via os.system() happens in Python and how to fix it

A shell injection vulnerability in TensorFlow's DELF dataset download script allowed attackers who controlled the `data_dir` parameter to execute arbitrary shell commands by injecting metacharacters into `os.system()` calls. The fix replaces all four `os.system()` invocations with `subprocess.run()` using argument lists, eliminating shell interpretation entirely. This change closes a high-severity code execution path in production ML infrastructure.