zip-lib 1.2.1 → 1.2.3

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/README.md CHANGED
@@ -366,7 +366,8 @@ Object
366
366
  ### Options: IExtractOptions <a id="iextractoptions"></a>
367
367
 
368
368
  Object
369
- - `overwrite?`: String (optional) - If it is true, the target directory will be deleted before extract. The default value is `false`.
369
+ - `overwrite?`: Boolean (optional) - If it is true, the target directory will be deleted before extract. The default value is `false`.
370
+ - `safeSymlinksOnly`: Boolean (optional) - Controls the creation phase of symlinks. The default value is `false`.<br>`true`: Refuses to create any symlink whose target is outside the extraction root.<br>`false`: Allows creating external symlinks. **Note:** Subsequent write operations to these links will still be intercepted by the separate AFWRITE security layer.
370
371
  - `symlinkAsFileOnWindows?`: Boolean (optional) - Extract symbolic links as files on Windows. This value is only available on Windows and ignored on other platforms. The default value is `true`.<br>If `true`, the symlink in the zip will be extracted as a normal file on Windows.<br>If `false`, the symlink in the zip will be extracted as a symlink correctly on Windows, but an `EPERM` error will be thrown under non-administrators.
371
372
 
372
373
  > ⚠**WARNING:** On Windows, the default security policy allows only administrators to create symbolic links. If you set `symlinkAsFileOnWindows` to `false` and the zip contains symlink, be sure to run the code under the administrator, otherwise an `EPERM` error will be thrown.
package/lib/fs.d.ts CHANGED
@@ -14,6 +14,7 @@ export type FolderStat = {
14
14
  realpath: string;
15
15
  };
16
16
  export type FileType = "file" | "dir";
17
+ export declare function isOutside(baseDir: string, targetPath: string): boolean;
17
18
  export declare function realpath(target: string): Promise<string>;
18
19
  export declare function readdirp(folder: string): Promise<FileEntry[]>;
19
20
  export declare function getFileEntry(target: string): Promise<FileEntry>;
package/lib/fs.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOutside = isOutside;
3
4
  exports.realpath = realpath;
4
5
  exports.readdirp = readdirp;
5
6
  exports.getFileEntry = getFileEntry;
@@ -11,6 +12,12 @@ const fsSync = require("node:fs");
11
12
  const fs = require("node:fs/promises");
12
13
  const path = require("node:path");
13
14
  const util = require("node:util");
15
+ function isOutside(baseDir, targetPath) {
16
+ const absoluteBase = path.resolve(baseDir);
17
+ const absoluteTarget = path.resolve(targetPath);
18
+ const relative = path.relative(absoluteBase, absoluteTarget);
19
+ return relative.startsWith("..") || path.isAbsolute(relative);
20
+ }
14
21
  async function realpath(target) {
15
22
  // fs.promises.realpath has a bug with long path on Windows.
16
23
  // https://github.com/nodejs/node/issues/51031
package/lib/unzip.d.ts CHANGED
@@ -18,6 +18,17 @@ export interface IExtractOptions {
18
18
  * be sure to run the code under the administrator, otherwise an `EPERM` error will be thrown.
19
19
  */
20
20
  symlinkAsFileOnWindows?: boolean;
21
+ /**
22
+ * Controls the creation phase of symlinks.
23
+ *
24
+ * `true`: Refuses to create any symlink whose target is outside the extraction root.
25
+ *
26
+ * `false`: Allows creating external symlinks. **Note:** Subsequent write operations to these
27
+ * links will still be intercepted by the separate AFWRITE security layer.
28
+ *
29
+ * The default value is `false`.
30
+ */
31
+ safeSymlinksOnly?: boolean;
21
32
  /**
22
33
  * Called before an item is extracted.
23
34
  * @param event
package/lib/unzip.js CHANGED
@@ -64,12 +64,6 @@ class EntryContext {
64
64
  this._symlinkFolders.push({ folder, realpath });
65
65
  }
66
66
  }
67
- isOutside(baseDir, targetPath) {
68
- const absoluteBase = path.resolve(baseDir);
69
- const absoluteTarget = path.resolve(targetPath);
70
- const relative = path.relative(absoluteBase, absoluteTarget);
71
- return relative.startsWith("..") || path.isAbsolute(relative);
72
- }
73
67
  getFilePath() {
74
68
  return path.resolve(path.join(this.targetFolder, this.decodeEntryFileName));
75
69
  }
@@ -83,6 +77,10 @@ class EntryContext {
83
77
  }
84
78
  this._ensuredFolders.push(folder);
85
79
  }
80
+ isSymlinkTargetOutsideTargetFolder(linkTarget, linkFilePath) {
81
+ const fileDir = path.dirname(linkFilePath);
82
+ return exfs.isOutside(this.realTargetFolder, path.resolve(fileDir, linkTarget));
83
+ }
86
84
  async isOutsideTargetFolder(tpath) {
87
85
  if (this._symlinkFileNames.length === 0 && this._symlinkFolders.length === 0) {
88
86
  return false;
@@ -92,7 +90,7 @@ class EntryContext {
92
90
  }
93
91
  for (const { folder, realpath } of this._symlinkFolders) {
94
92
  if (tpath.includes(folder)) {
95
- if (this.isOutside(this.realTargetFolder, realpath)) {
93
+ if (exfs.isOutside(this.realTargetFolder, realpath)) {
96
94
  return true;
97
95
  }
98
96
  }
@@ -100,7 +98,7 @@ class EntryContext {
100
98
  for (const fileName of this._symlinkFileNames) {
101
99
  if (tpath.includes(fileName)) {
102
100
  const realFilePath = await exfs.realpath(tpath);
103
- if (this.isOutside(this.realTargetFolder, realFilePath)) {
101
+ if (exfs.isOutside(this.realTargetFolder, realFilePath)) {
104
102
  return true;
105
103
  }
106
104
  }
@@ -312,7 +310,16 @@ class Unzip extends cancelable_1.Cancelable {
312
310
  }
313
311
  });
314
312
  readStream.once("end", () => {
315
- this.createSymlink(linkContent, filePath).then(c, e);
313
+ var _a;
314
+ if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.safeSymlinksOnly) &&
315
+ entryContext.isSymlinkTargetOutsideTargetFolder(linkContent, filePath)) {
316
+ const error = new Error(`Dangerous link path was refused : "${entryContext.targetFolder}", file: "${filePath}", target: "${linkContent}"`);
317
+ error.name = "AF_ILLEGAL_TARGET";
318
+ e(error);
319
+ }
320
+ else {
321
+ this.createSymlink(linkContent, filePath).then(c, e);
322
+ }
316
323
  });
317
324
  }
318
325
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zip-lib",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "zip and unzip library for node",
5
5
  "main": "lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -30,16 +30,16 @@
30
30
  "author": "fpsqdb",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "yauzl": "^3.2.0",
33
+ "yauzl": "^3.2.1",
34
34
  "yazl": "^3.3.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@biomejs/biome": "^2.3.14",
38
- "@types/node": "^20.19.32",
37
+ "@biomejs/biome": "^2.4.7",
38
+ "@types/node": "^20.19.37",
39
39
  "@types/yauzl": "^2.10.3",
40
40
  "@types/yazl": "^3.3.0",
41
- "@vitest/coverage-v8": "^4.0.18",
42
- "rimraf": "^6.1.2",
41
+ "@vitest/coverage-v8": "^4.1.0",
42
+ "rimraf": "^6.1.3",
43
43
  "typescript": "^5.9.3",
44
44
  "vitest": "^4.0.18"
45
45
  }