The package manager pnpm prior to version 9.15.0 seems to mishandle overrides and global cache: Overrides from one workspace leak into npm…
GitHub_M·CWE-426·Published 2024-12-10
The package manager pnpm prior to version 9.15.0 seems to mishandle overrides and global cache: Overrides from one workspace leak into npm metadata saved in global cache; npm metadata from global cache affects other workspaces; and installs by default don't revalidate the data (including on first lockfile generation). This can make workspace A (even running with `ignore-scripts=true`) posion global cache and execute scripts in workspace B. Users generally expect `ignore-scripts` to be sufficient to prevent immediate code execution on install (e.g. when the tree is just repacked/bundled without executing it). Here, that expectation is broken. Global state integrity is lost via operations that one would expect to be secure, enabling subsequently running arbitrary code execution on installs. Version 9.15.0 fixes the issue. As a work-around, use separate cache and store dirs in each workspace.
The package manager pnpm prior to version 9.15.0 seems to mishandle overrides and global cache: Overrides from one workspace leak into npm metadata saved in global cache; npm metadata from global cache affects other workspaces; and installs by default don't revalidate the data (including on first lockfile generation). This can make workspace A (even running with `ignore-scripts=true`) posion global cache and execute scripts in workspace B. Users generally expect `ignore-scripts` to be sufficient to prevent immediate code execution on install (e.g. when the tree is just repacked/bundled without executing it). Here, that expectation is broken. Global state integrity is lost via operations that one would expect to be secure, enabling subsequently running arbitrary code execution on installs. Version 9.15.0 fixes the issue. As a work-around, use separate cache and store dirs in each workspace.
### Summary pnpm seems to mishandle overrides and global cache: 1. Overrides from one workspace leak into npm metadata saved in global cache 2. npm metadata from global cache affects other workspaces 3. installs by default don't revalidate the data (including on first lockfile generation) This can make workspace A (even running with `ignore-scripts=true`) posion global cache and execute scripts in workspace B Users generally expect `ignore-scripts` to be sufficient to prevent immediate code execution on install (e.g. when the tree is just repacked/bundled without executing it). Here, that expectation is broken ### Details See PoC. In it, overrides from a single run of A get leaked into e.g. `~/Library/Caches/pnpm/metadata/registry.npmjs.org/rimraf.json` and persistently affect all other projects using the cache ### PoC Postinstall code used in PoC is benign and can be inspected in <https://www.npmjs.com/package/ponyhooves?activeTab=code>, it's just a `console.log` 1. Remove store and cache On mac: `rm -rf ~/Library/Caches/pnpm ~/Library/pnpm/store` This step is not required in general, but we'll be using a popular package for PoC that's likely cached 2. Create `A/package.json`: ```json { "name": "A", "pnpm": { "overrides": { "rimraf>glob": "npm:ponyhooves@1" } }, "dependencies": { "rimraf": "6.0.1" } } ``` Install it with `pnpm i --ignore-scripts` (the flag is not required, but the point of the demo is to show that it doesn't help) 4. Create `B/package.json`: ```json { "name": "B", "dependencies": { "rimraf": "6.0.1" } } ``` Install it with `pnpm i` Result: ```console Packages: +3 +++ Progress: resolved 3, reused 3, downloaded 0, added 3, done node_modules/.pnpm/ponyhooves@1.0.1/node_modules/ponyhooves: Running postinstall script, done in 51ms dependencies: + rimraf 6.0.1 Done in 1.4s ``` Also, that code got leaked into another project and it's lockfile now! ### Impact Global state integrity is lost via operations that one would expect to be secure, enabling subsequently running arbitrary code execution on installs As a work-around, use separate cache and store dirs in each workspace
### Summary pnpm seems to mishandle overrides and global cache: 1. Overrides from one workspace leak into npm metadata saved in global cache 2. npm metadata from global cache affects other workspaces 3. installs by default don't revalidate the data (including on first lockfile generation) This can make workspace A (even running with `ignore-scripts=true`) posion global cache and execute scripts in workspace B Users generally expect `ignore-scripts` to be sufficient to prevent immediate code execution on install (e.g. when the tree is just repacked/bundled without executing it). Here, that expectation is broken ### Details See PoC. In it, overrides from a single run of A get leaked into e.g. `~/Library/Caches/pnpm/metadata/registry.npmjs.org/rimraf.json` and persistently affect all other projects using the cache ### PoC Postinstall code used in PoC is benign and can be inspected in <https://www.npmjs.com/package/ponyhooves?activeTab=code>, it's just a `console.log` 1. Remove store and cache On mac: `rm -rf ~/Library/Caches/pnpm ~/Library/pnpm/store` This step is not required in general, but we'll be using a popular package for PoC that's likely cached 2. Create `A/package.json`: ```json { "name": "A", "pnpm": { "overrides": { "rimraf>glob": "npm:ponyhooves@1" } }, "dependencies": { "rimraf": "6.0.1" } } ``` Install it with `pnpm i --ignore-scripts` (the flag is not required, but the point of the demo is to show that it doesn't help) 4. Create `B/package.json`: ```json { "name": "B", "dependencies": { "rimraf": "6.0.1" } } ``` Install it with `pnpm i` Result: ```console Packages: +3 +++ Progress: resolved 3, reused 3, downloaded 0, added 3, done node_modules/.pnpm/ponyhooves@1.0.1/node_modules/ponyhooves: Running postinstall script, done in 51ms dependencies: + rimraf 6.0.1 Done in 1.4s ``` Also, that code got leaked into another project and it's lockfile now! ### Impact Global state integrity is lost via operations that one would expect to be secure, enabling subsequently running arbitrary code execution on installs As a work-around, use separate cache and store dirs in each workspace
El administrador de paquetes pnpm anterior a la versión 9.15.0 parece manejar mal las anulaciones y la caché global: las anulaciones de un espacio de trabajo se filtran en los metadatos de npm guardados en la caché global; los metadatos de npm de la caché global afectan a otros espacios de trabajo; y las instalaciones por defecto no revalidan los datos (incluso en la primera generación del archivo de bloqueo). Esto puede hacer que el espacio de trabajo A (incluso ejecutándose con `ignore-scripts=true`) posea la caché global y ejecute scripts en el espacio de trabajo B. Los usuarios generalmente esperan que `ignore-scripts` sea suficiente para evitar la ejecución inmediata del código en la instalación (por ejemplo, cuando el árbol simplemente se vuelve a empaquetar/agrupar sin ejecutarlo). Aquí, esa expectativa se rompe. La integridad del estado global se pierde a través de operaciones que uno esperaría que fueran seguras, lo que permite ejecutar posteriormente la ejecución de código arbitrario en las instalaciones. La versión 9.15.0 corrige el problema. Como workaround, use directorios de caché y de almacenamiento separados en cada espacio de trabajo.
| Version | Type | Source | Base | Exp | Impact | Vector |
|---|---|---|---|---|---|---|
| 3.1 | Primary | NVD | 9.8 | 3.9 | 5.9 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 4.0 | Primary | cve.org | 5.8 | — | — | CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H |
| 4.0 | Primary | cve.org | 5.8 | — | — | CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H |
| 4.0 | Secondary | NVD | 5.8 | — | — | CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X |
| 4.0 | Secondary | GHSA | 5.8 | — | — | CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H |