Back to Blog
high SEVERITY8 min read

How missing minimum release age happens in pnpm workspaces and how to fix it

A pnpm workspace configuration was missing the `minimumReleaseAge` setting, meaning freshly published — and potentially malicious or unstable — npm packages could be installed immediately. By adding `minimumReleaseAge: 10080` to `pnpm-workspace.yaml`, the project now enforces a seven-day waiting period before any newly released package version can be installed. This single configuration change significantly reduces the attack surface for supply chain attacks targeting this Node.js library and it

O
By Orbis AppSec
Published July 27, 2026Reviewed July 27, 2026

Answer Summary

This vulnerability is a missing supply chain control in a pnpm workspace (CWE-1395: Dependency on Vulnerable Third-Party Component). The `pnpm-workspace.yaml` file lacked a `minimumReleaseAge` setting, allowing npm packages to be installed the moment they are published — before the community has had time to detect malicious or compromised versions. The fix adds `minimumReleaseAge: 10080` (7 days in minutes), `blockExoticSubdeps: true`, and `trustPolicy: no-downgrade` to the workspace configuration, enforcing a safety buffer against typosquatting, account-takeover, and dependency confusion attacks.

Vulnerability at a Glance

cweCWE-1395 (Dependency on Vulnerable Third-Party Component)
fixAdded `minimumReleaseAge: 10080` (7 days), `blockExoticSubdeps: true`, and `trustPolicy: no-downgrade` to `pnpm-workspace.yaml`
riskMalicious or unstable newly published packages can be installed automatically before community detection
languageYAML / Node.js (pnpm)
root cause`pnpm-workspace.yaml` did not set `minimumReleaseAge`, allowing zero-day package installations
vulnerabilityMissing Minimum Release Age in pnpm Workspace

How Missing Minimum Release Age Happens in pnpm Workspaces and How to Fix It


The Vulnerability at a Glance

Field Detail
Vulnerability Missing Minimum Release Age in pnpm Workspace
CWE CWE-1395 — Dependency on Vulnerable Third-Party Component
Language YAML / Node.js (pnpm)
Risk Malicious or unstable newly published packages installed automatically
Root Cause pnpm-workspace.yaml lacked minimumReleaseAge setting
Fix Added minimumReleaseAge: 10080, blockExoticSubdeps: true, trustPolicy: no-downgrade

Summary

A pnpm workspace configuration was missing the minimumReleaseAge setting, meaning freshly published — and potentially malicious or unstable — npm packages could be installed immediately. By adding minimumReleaseAge: 10080 to pnpm-workspace.yaml, the project now enforces a seven-day waiting period before any newly released package version can be installed. This single configuration change significantly reduces the attack surface for supply chain attacks targeting this Node.js library and its downstream consumers.


Introduction

The pnpm-workspace.yaml file is the control center for a pnpm monorepo. It defines which directories are workspace packages and — critically — governs the security policies that apply when resolving and installing dependencies across all of apps/* and packages/*. In this project, the file was doing its job of mapping workspace directories, but it was leaving a significant supply chain door wide open: there was no minimumReleaseAge setting, meaning pnpm would happily install a package version the instant it appeared on the npm registry, with zero waiting period.

This matters because supply chain attacks increasingly rely on that narrow window between publication and detection. Whether it's a typosquatted package name, a compromised maintainer account, or a malicious version bump, attackers count on automated CI pipelines pulling fresh packages before anyone notices something is wrong.


The Vulnerability Explained

What Was Missing

Here is the original pnpm-workspace.yaml before the fix:

packages:
  - "apps/*"
  - "packages/*"

Three lines. Functional, but completely silent on package vetting policy. Without minimumReleaseAge, pnpm operates with the implicit assumption that any version on the npm registry is safe to install the moment it is published.

Why This Is a Real Risk

The npm registry publishes tens of thousands of new package versions every day. The security community — including automated scanners, maintainers, and researchers — needs time to review new releases. The gap between "published" and "detected as malicious" is often measured in hours to days.

Three concrete attack vectors exploit this gap:

1. Account Takeover (ATO) Attacks
An attacker compromises a popular package maintainer's npm credentials and publishes a malicious patch version (e.g., some-util@2.3.1some-util@2.3.2). Any project running pnpm install within the first few hours after publication will pull the compromised version before npm or the security community has time to yank it.

2. Dependency Confusion
An attacker publishes a public package with the same name as an internal private package used in this monorepo (which covers apps/* and packages/*). Without a release age gate, the first pnpm install after publication could resolve to the attacker's version.

3. Typosquatting
A developer makes a typo in a new dependency name. The attacker has pre-registered that typo on npm with a malicious payload. Without a minimum release age, the malicious package installs immediately.

Why This Project Is Particularly Exposed

The PR notes this is a Node.js library — meaning vulnerabilities don't just affect the project itself, but propagate downstream to every consumer of the library. A supply chain compromise here has a multiplier effect: one poisoned install can affect dozens or hundreds of downstream projects.


The Fix

What Changed

The fix added three security settings to pnpm-workspace.yaml:

Before:

packages:
  - "apps/*"
  - "packages/*"

After:

packages:
  - "apps/*"
  - "packages/*"

minimumReleaseAge: 10080
blockExoticSubdeps: true
trustPolicy: no-downgrade

Breaking Down Each Change

minimumReleaseAge: 10080

This is the core fix. The value 10080 is the number of minutes in seven days (7 × 24 × 60 = 10,080). With this setting, pnpm will refuse to install any package version that was published less than seven days ago.

This seven-day window is deliberate and meaningful:
- npm's security team typically identifies and yanks malicious packages within hours to a few days
- Community security researchers and automated scanners have time to flag suspicious releases
- The project's own developers are more likely to notice unusual dependency updates in code review

This setting was introduced in pnpm v10.16.0 and is referenced in the official pnpm documentation.

blockExoticSubdeps: true

This setting prevents installation of dependencies that use non-standard or exotic resolution mechanisms — a common vector in dependency confusion and supply chain attacks where attackers exploit unusual package specifiers.

trustPolicy: no-downgrade

This prevents pnpm from silently downgrading the trust level of packages already in the workspace. Combined with minimumReleaseAge, it ensures that once a package version has been vetted and installed, it cannot be quietly replaced with a less-trusted version.

The Combined Effect

These three settings work together as a layered defense:

New package published on npm
        
        
┌─────────────────────────────┐
  minimumReleaseAge: 10080      Blocks install for 7 days
  (time-based vetting gate)  
└─────────────────────────────┘
         (after 7 days)
        
┌─────────────────────────────┐
  blockExoticSubdeps: true      Blocks non-standard resolution
└─────────────────────────────┘
        
        
┌─────────────────────────────┐
  trustPolicy: no-downgrade     Prevents trust regression
└─────────────────────────────┘
        
        
    Package installed 

Prevention & Best Practices

1. Enable minimumReleaseAge in All pnpm Projects

Any project using pnpm v10.16.0 or later should add this to pnpm-workspace.yaml or .npmrc:

# pnpm-workspace.yaml
minimumReleaseAge: 10080  # 7 days in minutes

For projects where a 7-day wait is too long (e.g., rapid iteration environments), consider a shorter but non-zero value:

minimumReleaseAge: 1440  # 1 day minimum

Even a 24-hour window catches many automated malicious publish attacks.

2. Use a Lockfile and Commit It

A pnpm-lock.yaml committed to your repository ensures that pnpm install in CI always installs the exact same versions. The minimumReleaseAge setting protects you when you update the lockfile — the lockfile protects you when you use it.

3. Enable Provenance and Audit in CI

Add these to your CI pipeline:

pnpm audit --audit-level=high

And consider enabling npm provenance for packages you publish, so consumers can verify the build origin.

4. Monitor Dependencies with Automated Tools

Tools like Dependabot, Renovate, and Socket.dev provide ongoing monitoring of your dependency tree for newly introduced risks.

5. Apply Defense-in-Depth for Supply Chain

The OWASP Software Component Verification Standard (SCVS) and SLSA framework provide structured guidance for supply chain security beyond just package manager settings.

Relevant Standards

  • CWE-1395: Dependency on Vulnerable Third-Party Component
  • OWASP A06:2021: Vulnerable and Outdated Components
  • SSDF PW.4: Reuse existing, well-secured software where feasible

Key Takeaways

  • pnpm-workspace.yaml is a security surface, not just a directory map. The absence of minimumReleaseAge in this file meant all apps/* and packages/* workspaces were exposed to zero-day package installations.
  • 10,080 minutes (7 days) is the recommended minimum. This specific value aligns with the typical detection window for malicious npm packages and is the value recommended by the pnpm team.
  • Three settings work better than one. The combination of minimumReleaseAge, blockExoticSubdeps, and trustPolicy: no-downgrade provides layered supply chain defense that no single setting achieves alone.
  • Library projects have amplified risk. Because this is a Node.js library with downstream consumers, a supply chain compromise here propagates further than it would in a standalone application — making this fix especially high-value.
  • This is a configuration gap, not a code bug. Static analysis tools like Semgrep can detect missing security configurations just as effectively as they detect vulnerable code patterns — don't limit your scanning to source code only.

How Orbis AppSec Detected This

  • Source: The pnpm-workspace.yaml file at line 1, which governs dependency resolution policy for all packages in the apps/* and packages/* workspace directories.
  • Sink: Any pnpm install or pnpm update invocation that resolves package versions — specifically, the absence of a time-based gate before newly published versions are accepted.
  • Missing control: The minimumReleaseAge setting was entirely absent from pnpm-workspace.yaml, meaning pnpm applied no waiting period to newly published package versions.
  • CWE: CWE-1395 — Dependency on Vulnerable Third-Party Component
  • Fix: Added minimumReleaseAge: 10080, blockExoticSubdeps: true, and trustPolicy: no-downgrade to pnpm-workspace.yaml, enforcing a 7-day vetting window and blocking non-standard dependency resolution.

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 attacks are one of the fastest-growing categories of security incidents in the npm ecosystem, and they specifically exploit the trust that package managers place in newly published versions. The missing minimumReleaseAge setting in this project's pnpm-workspace.yaml was a quiet but meaningful gap — one that required zero lines of malicious code to exploit, just a lucky timing window.

The fix is elegantly simple: three lines of YAML that enforce a 7-day waiting period, block exotic dependency resolution, and prevent trust downgrades. For a Node.js library with downstream consumers, this configuration change delivers outsized security value relative to its implementation cost.

The broader lesson for developers: your package manager configuration files are security policy documents. Treat them with the same scrutiny you'd apply to authentication middleware or input validation logic. A missing setting in pnpm-workspace.yaml can be just as dangerous as a missing bounds check in application code.


References

Frequently Asked Questions

What is a missing minimum release age in pnpm?

It means your pnpm workspace has no waiting period before installing newly published package versions. Without this setting, a malicious or compromised package can be pulled into your project the instant it appears on the npm registry.

How do you prevent missing minimum release age in pnpm?

Add `minimumReleaseAge: 10080` to your `pnpm-workspace.yaml` file. This requires pnpm v10.16.0 or later and enforces a 7-day (10,080-minute) delay before any newly published package version is eligible for installation.

What CWE is missing minimum release age?

It maps most closely to CWE-1395: Dependency on Vulnerable Third-Party Component, since the root issue is the unconditional trust and immediate use of third-party packages without any vetting delay.

Is locking dependency versions (lockfile) enough to prevent this?

A lockfile protects existing installs but does not protect you when you intentionally upgrade or add new packages. `minimumReleaseAge` adds a time-based safety buffer that a lockfile alone cannot provide.

Can static analysis detect missing minimum release age?

Yes. Semgrep rule `package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age` detects the absence of this setting in `pnpm-workspace.yaml` automatically, as demonstrated in this fix.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #27

Related Articles

critical

How Plaintext Credential Storage happens in JSON Configuration Files and how to fix it

A critical security issue was discovered in `assets/settings/global.json` where a real phone number (PII) was stored in plaintext alongside placeholder patterns for API keys and payment credentials. This design encouraged developers to substitute real credentials directly into a version-controlled file, creating a high risk of credential exposure via repository access or filesystem reads. The fix replaces the hardcoded phone number with a placeholder and reinforces safe configuration patterns.

high

How Quadratic CPU Consumption Vulnerabilities Happen in JavaScript YAML Parsers and How to Fix Them

A high-severity denial-of-service vulnerability in js-yaml versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through specially crafted YAML documents using the !!omap tag. This fix upgrades js-yaml from 4.1.1 to 4.3.1 and from 3.14.2 to 3.15.1, eliminating the algorithmic complexity attack vector that could freeze Node.js applications processing untrusted YAML input.

high

How javascript.lang.security.detect-child-process.detect-child-process happens in Node.js and how to fix it

A high-severity command injection vulnerability was discovered in `scripts/build.js` where `execSync` was called with string-interpolated arguments (`sourceDir` and `outputPath`) inside a shell command. By replacing `execSync` with `spawnSync` using an argument array (no shell), the fix eliminates the possibility of shell metacharacter injection while preserving identical build behavior.

high

How Command Injection happens in Node.js child_process and how to fix it

A command injection vulnerability in nix.js's Release class allowed potentially malicious input through the `arch` parameter to be executed via shell commands. The fix replaced `execSync()` with `execFileSync()`, eliminating shell interpretation and preventing command injection by passing arguments as an array instead of a concatenated string.

critical

How Sensitive Data Exposure in Error Logging happens in TypeScript/Deno and how to fix it

A critical vulnerability in Supabase Edge Functions allowed sensitive authentication errors and API credentials to leak through verbose error logging. The `cancel-subscription/index.ts` function logged full error objects to the console, potentially exposing Paddle API keys and auth tokens in deployment logs. The fix sanitizes all error messages to log only safe error text while preserving debugging capability.

critical

How HTTP Header Injection Happens in Go and How to Fix It

A critical vulnerability in the file upload handler allowed attackers to inject CRLF sequences into HTTP response headers through crafted filenames. The fix sanitizes user-supplied filenames before using them in Content-Disposition headers, preventing header injection attacks that could lead to cache poisoning, session fixation, or XSS.