How pnpm Trust Policy Misconfiguration Happens in Node.js and How to Fix It
The Incident
In a Node.js workspace using pnpm, a routine security scan flagged pnpm-workspace.yaml at line 1: the file was missing a trustPolicy directive entirely. While the workspace had several other hardening settings in place — including minimumReleaseAge: 10080 (a 7-day release quarantine), optimisticRepeatInstall: false, and minimumReleaseAgeStrict: true — the absence of trustPolicy left a meaningful gap. Any package installed into this workspace could, in theory, downgrade those carefully configured security settings. This post explains exactly how that happens, why it matters, and what the two-line fix looks like.
The Vulnerability Explained
What trustPolicy Controls
pnpm's trustPolicy setting, introduced in v10.21.0, governs whether packages installed into a workspace are permitted to modify the workspace's own security configuration. Without it, there is no enforcement boundary: a malicious or compromised package could include configuration that weakens the workspace's security posture — for example, relaxing the minimumReleaseAge quarantine or disabling strict checks — and pnpm would have no instruction to reject that downgrade.
The Vulnerable Configuration
Before the fix, pnpm-workspace.yaml looked like this:
# pnpm-workspace.yaml (vulnerable)
optimisticRepeatInstall: false
minimumReleaseAge: 10080
minimumReleaseAgeIgnoreMissingTime: false
minimumReleaseAgeStrict: true
# ← No trustPolicy directive
The file had meaningful protections: minimumReleaseAge: 10080 enforces a 7-day hold on newly published packages (a strong supply-chain defense), and minimumReleaseAgeStrict: true makes that check non-negotiable for known packages. But none of those settings are self-protecting. There is nothing in this configuration that says "a package cannot instruct pnpm to weaken these rules."
How This Could Be Exploited
Consider a supply-chain attack scenario specific to this workspace:
- A package in the dependency tree is compromised — either through a maintainer account takeover or a malicious transitive dependency.
- The compromised package ships a configuration payload that attempts to reduce
minimumReleaseAgeor disableminimumReleaseAgeStrict, effectively nullifying the 7-day quarantine that was protecting this workspace. - Without
trustPolicy: no-downgrade, pnpm has no policy-level instruction to reject that configuration change. - On the next
pnpm install, the weakened settings take effect, and subsequent installs no longer enforce the release age quarantine — opening the door to fast-moving supply-chain attacks that the quarantine was specifically designed to block.
This is an exploit primitive: not independently exploitable in isolation today, but a meaningful building block for automated exploit-development tooling that chains weaknesses together.
The Fix
The pull request added exactly two lines to pnpm-workspace.yaml:
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -6,3 +6,5 @@ optimisticRepeatInstall: false
minimumReleaseAge: 10080
minimumReleaseAgeIgnoreMissingTime: false
minimumReleaseAgeStrict: true
+trustPolicy: no-downgrade
+blockExoticSubdeps: true
After the Fix
# pnpm-workspace.yaml (hardened)
optimisticRepeatInstall: false
minimumReleaseAge: 10080
minimumReleaseAgeIgnoreMissingTime: false
minimumReleaseAgeStrict: true
trustPolicy: no-downgrade
blockExoticSubdeps: true
What Each Line Does
trustPolicy: no-downgrade
This is the primary fix. The no-downgrade value instructs pnpm to reject any package-level configuration change that would reduce the current security trust level. Concretely:
- The minimumReleaseAge: 10080 quarantine cannot be shortened by a package.
- minimumReleaseAgeStrict: true cannot be set to false by a package.
- No installed package can weaken the workspace's existing hardening settings.
The no-downgrade policy is the most broadly protective option without being so restrictive that it breaks legitimate package behavior. It is the value recommended by the pnpm documentation for production workspaces.
blockExoticSubdeps: true
This companion setting blocks installation of subdependencies that use non-standard or "exotic" resolution protocols — a common vector in supply-chain attacks where a malicious package pulls in dependencies from unusual sources (e.g., git URLs, private registries, or custom protocols) that bypass normal vetting. Combined with trustPolicy: no-downgrade, this closes a second path by which a compromised package could introduce untrusted code.
Together, these two settings make the existing hardening configuration — the 7-day quarantine, the strict release age enforcement — self-protecting and harder to circumvent from within the dependency tree.
Prevention & Best Practices
Always Pair Security Settings with trustPolicy
If your pnpm-workspace.yaml uses any of the following settings, you should also set trustPolicy: no-downgrade to ensure those settings cannot be weakened by installed packages:
minimumReleaseAgeminimumReleaseAgeStrictauditLevel- Any custom security-related workspace configuration
Upgrade to pnpm v10.21.0 or Later
trustPolicy was added in pnpm v10.21.0. If you are running an older version, upgrade first:
npm install -g pnpm@latest
# or
corepack use pnpm@latest
Use Semgrep to Detect Missing Trust Policies
The Semgrep rule package_managers.pnpm.pnpm-trust-policy.pnpm-trust-policy detects both missing and incorrect trustPolicy values in pnpm-workspace.yaml. Add it to your CI pipeline:
# .github/workflows/semgrep.yml
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
r/package_managers.pnpm.pnpm-trust-policy.pnpm-trust-policy
Enforce Release Age Quarantines Consistently
The existing minimumReleaseAge: 10080 (7 days) in this workspace is an excellent supply-chain defense. The CISA and OpenSSF both recommend holding newly published packages before installation to allow time for the community to detect malicious releases. Make sure this setting is protected by trustPolicy: no-downgrade so it cannot be silently removed.
Relevant Standards
- CWE-693: Protection Mechanism Failure — the root CWE for this class of vulnerability, covering cases where a security mechanism can be bypassed or disabled.
- OWASP A06:2021 – Vulnerable and Outdated Components: Supply-chain hardening at the package manager level directly addresses this category.
- OWASP Software Supply Chain Security: Recommends enforcing integrity and policy controls at every layer of the dependency resolution process.
Key Takeaways
pnpm-workspace.yamlsecurity settings are not self-protecting withouttrustPolicy: no-downgrade— a malicious package can attempt to weakenminimumReleaseAgeorminimumReleaseAgeStrictunless this directive is present.- The 7-day quarantine (
minimumReleaseAge: 10080) in this workspace is only as strong as its protection against modification —trustPolicy: no-downgradeis what makes it durable. blockExoticSubdeps: truecloses a second supply-chain vector by preventing packages from pulling in subdependencies through non-standard resolution protocols.- This is an exploit primitive, not just a misconfiguration — automated attack tools can chain this gap with other weaknesses; removing it proactively raises the cost of a successful attack.
- pnpm v10.21.0+ is required — if you cannot upgrade,
trustPolicyis unavailable and alternative controls (lockfile integrity checks, registry mirroring) should be prioritized.
How Orbis AppSec Detected This
- Source: The
pnpm-workspace.yamlconfiguration file, which is processed by pnpm during everyinstallorupdateoperation and is therefore reachable by any package in the dependency tree. - Sink: The absence of a
trustPolicydirective at the workspace configuration level, meaning pnpm had no instruction to reject security-downgrading configuration payloads from installed packages. - Missing control: No
trustPolicydirective was present, leaving the workspace's existing hardening settings (minimumReleaseAge,minimumReleaseAgeStrict) unprotected against modification by packages. - CWE: CWE-693 — Protection Mechanism Failure.
- Fix: Added
trustPolicy: no-downgradeandblockExoticSubdeps: truetopnpm-workspace.yaml, ensuring pnpm enforces a no-downgrade policy on all security settings and blocks exotic subdependency 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
A single missing directive in pnpm-workspace.yaml was enough to leave a carefully hardened Node.js workspace vulnerable to supply-chain policy downgrade attacks. The workspace had invested in meaningful protections — a 7-day release quarantine, strict enforcement, and conservative install behavior — but without trustPolicy: no-downgrade, those protections had no defense against being silently weakened by a compromised package. The fix is two lines. The protection it provides is significant: it makes the workspace's security configuration durable, self-protecting, and resistant to the kind of automated exploit chaining that increasingly capable attack tooling relies on. If you use pnpm v10.21.0 or later, add trustPolicy: no-downgrade to your pnpm-workspace.yaml today.
References
- CWE-693: Protection Mechanism Failure
- OWASP A06:2021 – Vulnerable and Outdated Components
- OWASP Software Supply Chain Security Cheat Sheet
- pnpm
trustPolicydocumentation - pnpm
blockExoticSubdepsdocumentation - Semgrep rule: pnpm trust policy
- harden: missing or incorrect trustpolicy in pnpm-workspace.yaml...