@pnpm/fetching.binary-fetcher 1005.0.1 → 1005.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +22 -0
- package/lib/index.js +29 -2
- package/lib/index.js.map +1 -1
- package/package.json +11 -7
package/LICENSE
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2015-2016 Rico Sta. Cruz and other contributors
|
|
4
|
-
Copyright (c) 2016-
|
|
4
|
+
Copyright (c) 2016-2026 Zoltan Kochan and other contributors
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -8,6 +8,28 @@
|
|
|
8
8
|
pnpm add @pnpm/fetching.binary-fetcher
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Testing
|
|
12
|
+
|
|
13
|
+
### Test Fixtures
|
|
14
|
+
|
|
15
|
+
The `test/fixtures/` directory contains malicious ZIP files for testing path traversal protection:
|
|
16
|
+
|
|
17
|
+
| File | Entry Path | Purpose |
|
|
18
|
+
|------|------------|---------|
|
|
19
|
+
| `path-traversal.zip` | `../../../.npmrc` | Tests `../` escape sequences |
|
|
20
|
+
| `absolute-path.zip` | `/etc/passwd` | Tests absolute path entries |
|
|
21
|
+
| `backslash-traversal.zip` | `..\..\..\evil.txt` | Tests Windows backslash traversal (Windows-only) |
|
|
22
|
+
|
|
23
|
+
These fixtures are manually crafted because AdmZip's `addFile()` sanitizes paths automatically.
|
|
24
|
+
|
|
25
|
+
> **Note:** The backslash test only runs on Windows because `\` is a valid filename character on Unix.
|
|
26
|
+
|
|
27
|
+
### Regenerating Fixtures
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node --experimental-strip-types scripts/create-fixtures.ts
|
|
31
|
+
```
|
|
32
|
+
|
|
11
33
|
## License
|
|
12
34
|
|
|
13
35
|
MIT
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const promises_1 = __importDefault(require("fs/promises"));
|
|
|
10
10
|
const error_1 = require("@pnpm/error");
|
|
11
11
|
const worker_1 = require("@pnpm/worker");
|
|
12
12
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
13
|
+
const is_subdir_1 = __importDefault(require("is-subdir"));
|
|
13
14
|
const rename_overwrite_1 = __importDefault(require("rename-overwrite"));
|
|
14
15
|
const tempy_1 = __importDefault(require("tempy"));
|
|
15
16
|
const ssri_1 = __importDefault(require("ssri"));
|
|
@@ -118,13 +119,39 @@ async function downloadWithIntegrityCheck(fetchFromRegistry, { url, integrity },
|
|
|
118
119
|
* @param zipPath - Path to the zip file
|
|
119
120
|
* @param basename - Base name of the file (without extension)
|
|
120
121
|
* @param targetDir - Directory where contents should be extracted
|
|
121
|
-
* @throws {PnpmError} When extraction fails
|
|
122
|
+
* @throws {PnpmError} When extraction fails or path traversal is detected
|
|
122
123
|
*/
|
|
123
124
|
async function extractZipToTarget(zipPath, basename, targetDir) {
|
|
124
125
|
const zip = new adm_zip_1.default(zipPath);
|
|
125
126
|
const nodeDir = basename === '' ? targetDir : path_1.default.dirname(targetDir);
|
|
127
|
+
// Validate basename/prefix doesn't escape the target directory
|
|
128
|
+
if (basename !== '') {
|
|
129
|
+
validatePathSecurity(nodeDir, basename);
|
|
130
|
+
}
|
|
131
|
+
// Extract each entry with path validation to prevent path traversal attacks
|
|
132
|
+
for (const entry of zip.getEntries()) {
|
|
133
|
+
const entryPath = entry.entryName;
|
|
134
|
+
validatePathSecurity(nodeDir, entryPath);
|
|
135
|
+
zip.extractEntryTo(entry, nodeDir, true, true);
|
|
136
|
+
}
|
|
126
137
|
const extractedDir = path_1.default.join(nodeDir, basename);
|
|
127
|
-
zip.extractAllTo(nodeDir, true);
|
|
128
138
|
await (0, rename_overwrite_1.default)(extractedDir, targetDir);
|
|
129
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Validates that a path does not escape the base directory via path traversal.
|
|
142
|
+
*
|
|
143
|
+
* @param basePath - The base directory that should contain the target
|
|
144
|
+
* @param targetPath - The relative path to validate
|
|
145
|
+
* @throws {PnpmError} When path traversal is detected
|
|
146
|
+
*/
|
|
147
|
+
function validatePathSecurity(basePath, targetPath) {
|
|
148
|
+
// Explicitly reject absolute paths - they should never be allowed as prefixes or entry names
|
|
149
|
+
if (path_1.default.isAbsolute(targetPath)) {
|
|
150
|
+
throw new error_1.PnpmError('PATH_TRAVERSAL', `Refusing to extract path "${targetPath}" - absolute paths are not allowed`);
|
|
151
|
+
}
|
|
152
|
+
const normalizedTarget = path_1.default.resolve(basePath, targetPath);
|
|
153
|
+
if (!(0, is_subdir_1.default)(basePath, normalizedTarget) && normalizedTarget !== basePath) {
|
|
154
|
+
throw new error_1.PnpmError('PATH_TRAVERSAL', `Refusing to extract path "${targetPath}" outside of target directory`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
130
157
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/fetching.binary-fetcher",
|
|
3
|
-
"version": "1005.0.
|
|
3
|
+
"version": "1005.0.2",
|
|
4
4
|
"description": "A fetcher for binary archives",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -25,20 +25,23 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"adm-zip": "^0.5.16",
|
|
28
|
+
"is-subdir": "^1.2.0",
|
|
28
29
|
"rename-overwrite": "^6.0.2",
|
|
29
30
|
"ssri": "10.0.5",
|
|
30
31
|
"tempy": "^1.0.1",
|
|
31
|
-
"@pnpm/fetching-types": "1000.2.0",
|
|
32
32
|
"@pnpm/error": "1000.0.5",
|
|
33
|
-
"@pnpm/fetcher-base": "1001.2.
|
|
33
|
+
"@pnpm/fetcher-base": "1001.2.2",
|
|
34
|
+
"@pnpm/fetching-types": "1000.2.0"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
|
-
"@pnpm/worker": "^1000.6.
|
|
37
|
+
"@pnpm/worker": "^1000.6.2"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
40
|
+
"@jest/globals": "29.7.0",
|
|
39
41
|
"@types/adm-zip": "^0.5.7",
|
|
40
42
|
"@types/ssri": "^7.1.5",
|
|
41
|
-
"
|
|
43
|
+
"tempy": "^1.0.1",
|
|
44
|
+
"@pnpm/fetching.binary-fetcher": "1005.0.2"
|
|
42
45
|
},
|
|
43
46
|
"engines": {
|
|
44
47
|
"node": ">=18.12"
|
|
@@ -47,8 +50,9 @@
|
|
|
47
50
|
"preset": "@pnpm/jest-config"
|
|
48
51
|
},
|
|
49
52
|
"scripts": {
|
|
50
|
-
"
|
|
51
|
-
"test": "pnpm run compile",
|
|
53
|
+
"_test": "jest",
|
|
54
|
+
"test": "pnpm run compile && pnpm run _test",
|
|
55
|
+
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
52
56
|
"compile": "tsc --build && pnpm run lint --fix"
|
|
53
57
|
}
|
|
54
58
|
}
|