@procedures/archive 0.0.0-stage → 0.3.0
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/CHANGELOG.md +47 -0
- package/LICENSE +21 -0
- package/README.md +95 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +1 -0
- package/dist/format.d.ts +25 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +34 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +148 -0
- package/dist/module.js.map +1 -0
- package/dist/paths.d.ts +58 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +205 -0
- package/dist/paths.js.map +1 -0
- package/dist/procedures/archive/create.d.ts +14 -0
- package/dist/procedures/archive/create.d.ts.map +1 -0
- package/dist/procedures/archive/create.js +81 -0
- package/dist/procedures/archive/create.js.map +1 -0
- package/dist/procedures/archive/extract.d.ts +17 -0
- package/dist/procedures/archive/extract.d.ts.map +1 -0
- package/dist/procedures/archive/extract.js +101 -0
- package/dist/procedures/archive/extract.js.map +1 -0
- package/dist/procedures/archive/index.d.ts +4 -0
- package/dist/procedures/archive/index.d.ts.map +1 -0
- package/dist/procedures/archive/index.js +4 -0
- package/dist/procedures/archive/index.js.map +1 -0
- package/dist/procedures/archive/list.d.ts +15 -0
- package/dist/procedures/archive/list.d.ts.map +1 -0
- package/dist/procedures/archive/list.js +47 -0
- package/dist/procedures/archive/list.js.map +1 -0
- package/dist/register.d.ts +6 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +8 -0
- package/dist/register.js.map +1 -0
- package/dist/tar.d.ts +38 -0
- package/dist/tar.d.ts.map +1 -0
- package/dist/tar.js +193 -0
- package/dist/tar.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/walk.d.ts +34 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +67 -0
- package/dist/walk.js.map +1 -0
- package/dist/zip.d.ts +57 -0
- package/dist/zip.d.ts.map +1 -0
- package/dist/zip.js +397 -0
- package/dist/zip.js.map +1 -0
- package/package.json +77 -3
- package/src/errors.ts +33 -0
- package/src/format.ts +64 -0
- package/src/index.ts +9 -0
- package/src/module.ts +192 -0
- package/src/paths.ts +230 -0
- package/src/procedures/archive/create.ts +84 -0
- package/src/procedures/archive/extract.ts +103 -0
- package/src/procedures/archive/index.ts +3 -0
- package/src/procedures/archive/list.ts +50 -0
- package/src/register.ts +10 -0
- package/src/tar.ts +198 -0
- package/src/types.ts +101 -0
- package/src/walk.ts +85 -0
- package/src/zip.ts +419 -0
package/dist/paths.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The check of the entries of an archive before an extract ("zip slip", roadmap R46).
|
|
3
|
+
*
|
|
4
|
+
* `planExtract()` examines all the entries before the extract writes a file. A hostile archive
|
|
5
|
+
* thus changes nothing. The rules:
|
|
6
|
+
*
|
|
7
|
+
* 1. A path must be relative and must not have `..`. A drive letter, a UNC path, a NUL character
|
|
8
|
+
* and (on Windows) a colon are refused too. A backslash counts as a separator.
|
|
9
|
+
* 2. An entry must not go through a symbolic link of the archive: a link `a` and a file `a/b`.
|
|
10
|
+
* 3. A symbolic link must stay in the target. Its target is relative, and the resolution through
|
|
11
|
+
* the other links of the archive stays in the target folder. The check knows all the links of
|
|
12
|
+
* the archive first, so the order of the entries does not matter.
|
|
13
|
+
* 4. A path of a link appears only one time in the archive.
|
|
14
|
+
* 5. A hard link names a file that came before it in the archive.
|
|
15
|
+
* 6. A folder of the target that the extract goes through must not be a symbolic link.
|
|
16
|
+
* 7. Without `overwrite`, an existing file at the path of an entry is an error.
|
|
17
|
+
*
|
|
18
|
+
* Rules 2 and 3 compare the paths two times: with the case, and without the case. A file system
|
|
19
|
+
* that ignores the case (Windows, macOS) then gets the same protection as one that does not.
|
|
20
|
+
*/
|
|
21
|
+
import { lstatSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { ArchiveError } from "./errors.js";
|
|
24
|
+
/** The longest chain of links that a resolution follows. More is a loop. */
|
|
25
|
+
const MAX_LINK_STEPS = 40;
|
|
26
|
+
const isWindows = process.platform === "win32";
|
|
27
|
+
/** True for a path that starts at a root: `/x`, `\x`, `C:x`, `C:\x` or `\\server\share`. */
|
|
28
|
+
export function isAbsoluteName(name) {
|
|
29
|
+
return /^[/\\]/.test(name) || /^[A-Za-z]:/.test(name);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* This function gives the parts of the path of an entry. It throws `UNSAFE_PATH` for an absolute
|
|
33
|
+
* path, a `..`, a NUL character, and on Windows a colon (an alternate data stream).
|
|
34
|
+
*/
|
|
35
|
+
export function entryParts(name, code = "UNSAFE_PATH") {
|
|
36
|
+
if (name.includes("\0"))
|
|
37
|
+
throw new ArchiveError(code, `The entry ${JSON.stringify(name)} has a NUL character`, name);
|
|
38
|
+
if (isAbsoluteName(name))
|
|
39
|
+
throw new ArchiveError(code, `The entry ${name} has an absolute path`, name);
|
|
40
|
+
const parts = name.split(/[/\\]+/).filter((part) => part !== "" && part !== ".");
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
if (part === "..")
|
|
43
|
+
throw new ArchiveError(code, `The entry ${name} has "..": it can go out of the target folder`, name);
|
|
44
|
+
if (isWindows && part.includes(":"))
|
|
45
|
+
throw new ArchiveError(code, `The entry ${name} has a colon, which Windows reads as a data stream`, name);
|
|
46
|
+
}
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
/** The parts of the target of a symbolic link. `..` stays: the resolution follows it. */
|
|
50
|
+
function linkParts(target) {
|
|
51
|
+
return target.split(/[/\\]+/).filter((part) => part !== "" && part !== ".");
|
|
52
|
+
}
|
|
53
|
+
/** The key of a path, with the case or without it. */
|
|
54
|
+
function keyOf(parts, fold) {
|
|
55
|
+
const key = parts.join("/");
|
|
56
|
+
return fold ? key.normalize("NFC").toLowerCase() : key;
|
|
57
|
+
}
|
|
58
|
+
/** The status of a path, or undefined when it does not exist. The results stay in a cache. */
|
|
59
|
+
function statOf(cache, path) {
|
|
60
|
+
if (cache.has(path))
|
|
61
|
+
return cache.get(path);
|
|
62
|
+
let stats;
|
|
63
|
+
try {
|
|
64
|
+
stats = lstatSync(path);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
stats = undefined;
|
|
68
|
+
}
|
|
69
|
+
cache.set(path, stats);
|
|
70
|
+
return stats;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The resolution of parts through the links of the archive: the parts of the place, or undefined
|
|
74
|
+
* when the resolution goes out of the root, meets an absolute link, meets a link of the target
|
|
75
|
+
* folder that is not in the archive, or follows too many links.
|
|
76
|
+
*/
|
|
77
|
+
function resolve(parts, links, fold, foreignLink, budget) {
|
|
78
|
+
const place = [];
|
|
79
|
+
for (const part of parts) {
|
|
80
|
+
if (part === "..") {
|
|
81
|
+
if (place.length === 0)
|
|
82
|
+
return undefined;
|
|
83
|
+
place.pop();
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
place.push(part);
|
|
87
|
+
const target = links.get(keyOf(place, fold));
|
|
88
|
+
if (target === undefined) {
|
|
89
|
+
if (foreignLink(place))
|
|
90
|
+
return undefined;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (--budget.steps < 0 || isAbsoluteName(target))
|
|
94
|
+
return undefined;
|
|
95
|
+
const resolved = resolve([...place.slice(0, -1), ...linkParts(target)], links, fold, foreignLink, budget);
|
|
96
|
+
if (resolved === undefined)
|
|
97
|
+
return undefined;
|
|
98
|
+
place.splice(0, place.length, ...resolved);
|
|
99
|
+
}
|
|
100
|
+
return place;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* This function examines the entries of an archive for an extract into `target` (a real path),
|
|
104
|
+
* and gives them with their parts. It throws the first problem. It writes nothing.
|
|
105
|
+
*/
|
|
106
|
+
export function planExtract(entries, target, options) {
|
|
107
|
+
if (entries.length > options.maxEntries) {
|
|
108
|
+
throw new ArchiveError("TOO_LARGE", `The archive has ${entries.length} entries, more than the limit of ${options.maxEntries}`);
|
|
109
|
+
}
|
|
110
|
+
let bytes = 0;
|
|
111
|
+
const planned = [];
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
if (entry.kind === "other")
|
|
114
|
+
throw new ArchiveError("UNSUPPORTED_ENTRY", `The entry ${entry.name} is a special file (a device or a FIFO)`, entry.name);
|
|
115
|
+
const parts = entryParts(entry.name);
|
|
116
|
+
if (parts.length === 0) {
|
|
117
|
+
if (entry.kind === "directory")
|
|
118
|
+
continue;
|
|
119
|
+
throw new ArchiveError("UNSAFE_PATH", `The entry ${JSON.stringify(entry.name)} has no name`, entry.name);
|
|
120
|
+
}
|
|
121
|
+
if (entry.kind === "file") {
|
|
122
|
+
bytes += entry.size;
|
|
123
|
+
if (bytes > options.maxBytes)
|
|
124
|
+
throw new ArchiveError("TOO_LARGE", `The files of the archive have more than ${options.maxBytes} bytes`, entry.name);
|
|
125
|
+
}
|
|
126
|
+
planned.push({ ...entry, parts, path: parts.join("/") });
|
|
127
|
+
}
|
|
128
|
+
// The links of the archive, with the case and without it
|
|
129
|
+
const links = [new Map(), new Map()];
|
|
130
|
+
for (const entry of planned) {
|
|
131
|
+
if (entry.kind !== "symlink")
|
|
132
|
+
continue;
|
|
133
|
+
for (const [index, map] of links.entries())
|
|
134
|
+
map.set(keyOf(entry.parts, index === 1), entry.linkTarget ?? "");
|
|
135
|
+
}
|
|
136
|
+
const stats = new Map();
|
|
137
|
+
const foreignLink = (parts) => statOf(stats, join(target, ...parts))?.isSymbolicLink() === true;
|
|
138
|
+
const files = new Set();
|
|
139
|
+
const seenLinks = new Set();
|
|
140
|
+
for (const entry of planned) {
|
|
141
|
+
const keys = [keyOf(entry.parts, false), keyOf(entry.parts, true)];
|
|
142
|
+
// Rule 2: no part before the last is a link of the archive
|
|
143
|
+
for (let length = 1; length < entry.parts.length; length++) {
|
|
144
|
+
const prefix = entry.parts.slice(0, length);
|
|
145
|
+
if (links[0].has(keyOf(prefix, false)) || links[1].has(keyOf(prefix, true))) {
|
|
146
|
+
throw new ArchiveError("UNSAFE_PATH", `The entry ${entry.name} goes through the link ${prefix.join("/")} of the archive`, entry.name);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Rule 4: the path of a link appears one time
|
|
150
|
+
const isLinkPath = links[0].has(keys[0]) || links[1].has(keys[1]);
|
|
151
|
+
if (isLinkPath && (entry.kind !== "symlink" || seenLinks.has(keys[1]))) {
|
|
152
|
+
throw new ArchiveError("UNSAFE_LINK", `The path ${entry.name} is a link and another entry of the archive`, entry.name);
|
|
153
|
+
}
|
|
154
|
+
if (entry.kind === "symlink") {
|
|
155
|
+
seenLinks.add(keys[1]);
|
|
156
|
+
// Rule 3: the target stays in the target folder
|
|
157
|
+
const linkTarget = entry.linkTarget ?? "";
|
|
158
|
+
if (linkTarget === "" || isAbsoluteName(linkTarget) || linkTarget.includes("\0")) {
|
|
159
|
+
throw new ArchiveError("UNSAFE_LINK", `The link ${entry.name} points to ${JSON.stringify(linkTarget)}: a link must point to a relative path`, entry.name);
|
|
160
|
+
}
|
|
161
|
+
for (const fold of [false, true]) {
|
|
162
|
+
const resolved = resolve([...entry.parts.slice(0, -1), ...linkParts(linkTarget)], links[fold ? 1 : 0], fold, foreignLink, { steps: MAX_LINK_STEPS });
|
|
163
|
+
if (resolved === undefined) {
|
|
164
|
+
throw new ArchiveError("UNSAFE_LINK", `The link ${entry.name} points to ${linkTarget}, out of the target folder`, entry.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (entry.kind === "hardlink") {
|
|
169
|
+
// Rule 5: a hard link names an earlier file of the archive
|
|
170
|
+
const linked = entryParts(entry.linkTarget ?? "", "UNSAFE_LINK");
|
|
171
|
+
if (!files.has(keyOf(linked, true))) {
|
|
172
|
+
throw new ArchiveError("UNSAFE_LINK", `The hard link ${entry.name} names ${entry.linkTarget}, which is not an earlier file of the archive`, entry.name);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (entry.kind === "file")
|
|
176
|
+
files.add(keys[1]);
|
|
177
|
+
// Rule 6: the folders of the target on the way are folders, not links
|
|
178
|
+
for (let length = 1; length < entry.parts.length; length++) {
|
|
179
|
+
const folder = join(target, ...entry.parts.slice(0, length));
|
|
180
|
+
const found = statOf(stats, folder);
|
|
181
|
+
if (found === undefined)
|
|
182
|
+
break;
|
|
183
|
+
if (found.isSymbolicLink())
|
|
184
|
+
throw new ArchiveError("UNSAFE_PATH", `The folder ${folder} of the target is a link: the entry ${entry.name} can go out through it`, entry.name);
|
|
185
|
+
if (!found.isDirectory())
|
|
186
|
+
throw new ArchiveError("EXISTS", `${folder} is a file, and the entry ${entry.name} needs a folder there`, entry.name);
|
|
187
|
+
}
|
|
188
|
+
// Rule 7: an existing path
|
|
189
|
+
const existing = statOf(stats, join(target, ...entry.parts));
|
|
190
|
+
if (existing !== undefined) {
|
|
191
|
+
if (entry.kind === "directory") {
|
|
192
|
+
if (!existing.isDirectory())
|
|
193
|
+
throw new ArchiveError("EXISTS", `${join(target, ...entry.parts)} exists and is not a folder`, entry.name);
|
|
194
|
+
}
|
|
195
|
+
else if (existing.isDirectory()) {
|
|
196
|
+
throw new ArchiveError("EXISTS", `${join(target, ...entry.parts)} is a folder, and the entry ${entry.name} is not`, entry.name);
|
|
197
|
+
}
|
|
198
|
+
else if (!options.overwrite) {
|
|
199
|
+
throw new ArchiveError("EXISTS", `${join(target, ...entry.parts)} exists: give overwrite: true to replace it`, entry.name);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return planned;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.create procedure
|
|
3
|
+
*
|
|
4
|
+
* Make a tar.gz, tar or zip archive of a folder or of files. The procedure writes a temporary
|
|
5
|
+
* file next to the archive, and renames it at the end: a failed call leaves no part of an
|
|
6
|
+
* archive. The walk does not follow a symbolic link: the archive gets the link.
|
|
7
|
+
*/
|
|
8
|
+
import { type ArchiveSettings } from "../../format.js";
|
|
9
|
+
import type { ArchiveCreateInput, ArchiveCreateOutput } from "../../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Make a tar.gz or zip archive of a folder or of files
|
|
12
|
+
*/
|
|
13
|
+
export declare function archiveCreate(input: ArchiveCreateInput, settings?: ArchiveSettings): Promise<ArchiveCreateOutput>;
|
|
14
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/procedures/archive/create.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAK/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAsB9E;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,EAAE,QAAQ,GAAE,eAA0C,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAyCjJ"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.create procedure
|
|
3
|
+
*
|
|
4
|
+
* Make a tar.gz, tar or zip archive of a folder or of files. The procedure writes a temporary
|
|
5
|
+
* file next to the archive, and renames it at the end: a failed call leaves no part of an
|
|
6
|
+
* archive. The walk does not follow a symbolic link: the archive gets the link.
|
|
7
|
+
*/
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
10
|
+
import { dirname, resolve } from "node:path";
|
|
11
|
+
import { ArchiveError } from "../../errors.js";
|
|
12
|
+
import { DEFAULT_ARCHIVE_SETTINGS, formatOfName } from "../../format.js";
|
|
13
|
+
import { entryParts } from "../../paths.js";
|
|
14
|
+
import { createTar } from "../../tar.js";
|
|
15
|
+
import { folderNames, walk } from "../../walk.js";
|
|
16
|
+
import { writeZip } from "../../zip.js";
|
|
17
|
+
/** The base folder and the names of the input. Exactly one of `source` and `files`. */
|
|
18
|
+
async function inputNames(input) {
|
|
19
|
+
if ((input.source === undefined) === (input.files === undefined)) {
|
|
20
|
+
throw new ArchiveError("UNSAFE_PATH", "archive.create needs exactly one of source and files");
|
|
21
|
+
}
|
|
22
|
+
if (input.source !== undefined) {
|
|
23
|
+
const base = resolve(input.source);
|
|
24
|
+
const stats = await stat(base).catch(() => undefined);
|
|
25
|
+
if (!stats?.isDirectory())
|
|
26
|
+
throw new ArchiveError("UNSAFE_PATH", `The source ${base} is not a folder`);
|
|
27
|
+
return { base, names: await folderNames(base) };
|
|
28
|
+
}
|
|
29
|
+
const base = resolve(input.cwd ?? process.cwd());
|
|
30
|
+
const names = (input.files ?? []).map((name) => {
|
|
31
|
+
const parts = entryParts(name);
|
|
32
|
+
if (parts.length === 0)
|
|
33
|
+
throw new ArchiveError("UNSAFE_PATH", `The file ${JSON.stringify(name)} names the base folder: give source for a whole folder`, name);
|
|
34
|
+
return parts.join("/");
|
|
35
|
+
});
|
|
36
|
+
return { base, names };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Make a tar.gz or zip archive of a folder or of files
|
|
40
|
+
*/
|
|
41
|
+
export async function archiveCreate(input, settings = DEFAULT_ARCHIVE_SETTINGS) {
|
|
42
|
+
const format = formatOfName(input.output, input.format);
|
|
43
|
+
const output = resolve(input.output);
|
|
44
|
+
const existing = await stat(output).catch(() => undefined);
|
|
45
|
+
if (existing?.isDirectory())
|
|
46
|
+
throw new ArchiveError("EXISTS", `${output} is a folder`);
|
|
47
|
+
if (existing !== undefined && input.overwrite !== true)
|
|
48
|
+
throw new ArchiveError("EXISTS", `${output} exists: give overwrite: true to replace it`);
|
|
49
|
+
const { base, names } = await inputNames(input);
|
|
50
|
+
const temporary = `${output}.${randomBytes(6).toString("hex")}.tmp`;
|
|
51
|
+
const entries = await walk(base, names, { ...settings, exclude: new Set([output, temporary]) });
|
|
52
|
+
await mkdir(dirname(output), { recursive: true });
|
|
53
|
+
try {
|
|
54
|
+
if (format === "zip") {
|
|
55
|
+
const items = entries.map((entry) => ({
|
|
56
|
+
name: entry.name,
|
|
57
|
+
kind: entry.kind,
|
|
58
|
+
mode: entry.mode,
|
|
59
|
+
mtime: entry.mtime,
|
|
60
|
+
data: entry.kind === "file" ? () => readFile(entry.path) : entry.kind === "symlink" ? Buffer.from(entry.linkTarget ?? "", "utf8") : undefined,
|
|
61
|
+
}));
|
|
62
|
+
const handle = await open(temporary, "wx");
|
|
63
|
+
try {
|
|
64
|
+
await writeZip(handle, items);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await handle.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
await createTar(temporary, base, entries.map((entry) => entry.name), format === "tar.gz");
|
|
72
|
+
}
|
|
73
|
+
await rename(temporary, output);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
await rm(temporary, { force: true });
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
return { path: output, format, entries: entries.length, bytes: (await stat(output)).size };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/procedures/archive/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.extract procedure
|
|
3
|
+
*
|
|
4
|
+
* Extract a tar.gz, tar or zip archive into a folder. The procedure examines all the entries
|
|
5
|
+
* before it writes a file (`planExtract()`): an entry with `..` or an absolute path, a symbolic
|
|
6
|
+
* link out of the target, an entry through a link, a special file, an existing file without
|
|
7
|
+
* `overwrite`, or an archive over the limits changes nothing.
|
|
8
|
+
*
|
|
9
|
+
* A damaged entry (its size or its CRC-32) stops the extract, and the files before it stay.
|
|
10
|
+
*/
|
|
11
|
+
import { type ArchiveSettings } from "../../format.js";
|
|
12
|
+
import type { ArchiveExtractInput, ArchiveExtractOutput } from "../../types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Extract a tar.gz or zip archive into a folder, with a check of each path
|
|
15
|
+
*/
|
|
16
|
+
export declare function archiveExtract(input: ArchiveExtractInput, settings?: ArchiveSettings): Promise<ArchiveExtractOutput>;
|
|
17
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/procedures/archive/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAI/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAqDhF;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,GAAE,eAA0C,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA6BpJ"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.extract procedure
|
|
3
|
+
*
|
|
4
|
+
* Extract a tar.gz, tar or zip archive into a folder. The procedure examines all the entries
|
|
5
|
+
* before it writes a file (`planExtract()`): an entry with `..` or an absolute path, a symbolic
|
|
6
|
+
* link out of the target, an entry through a link, a special file, an existing file without
|
|
7
|
+
* `overwrite`, or an archive over the limits changes nothing.
|
|
8
|
+
*
|
|
9
|
+
* A damaged entry (its size or its CRC-32) stops the extract, and the files before it stay.
|
|
10
|
+
*/
|
|
11
|
+
import { lstat, mkdir, realpath, rm, symlink, unlink, utimes } from "node:fs/promises";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { DEFAULT_ARCHIVE_SETTINGS, formatOfFile } from "../../format.js";
|
|
14
|
+
import { planExtract } from "../../paths.js";
|
|
15
|
+
import { extractTar, readTarEntries } from "../../tar.js";
|
|
16
|
+
import { extractZipFile, readZipDirectory, readZipLink } from "../../zip.js";
|
|
17
|
+
/** This function removes an old file or link at a path, so a new write does not follow it. */
|
|
18
|
+
async function clear(path) {
|
|
19
|
+
const found = await lstat(path).catch(() => undefined);
|
|
20
|
+
if (found !== undefined && !found.isDirectory())
|
|
21
|
+
await unlink(path);
|
|
22
|
+
}
|
|
23
|
+
/** This function writes the checked entries of a zip, in the order of the archive. */
|
|
24
|
+
async function writeZipEntries(archive, target, planned, zipEntries) {
|
|
25
|
+
for (const entry of planned) {
|
|
26
|
+
const destination = join(target, ...entry.parts);
|
|
27
|
+
if (entry.kind === "directory") {
|
|
28
|
+
await mkdir(destination, { recursive: true });
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
32
|
+
await clear(destination);
|
|
33
|
+
if (entry.kind === "symlink") {
|
|
34
|
+
await symlink(entry.linkTarget ?? "", destination);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const zipEntry = zipEntries[entry.index];
|
|
38
|
+
try {
|
|
39
|
+
await extractZipFile(archive, zipEntry, destination, zipEntry.mode === undefined ? undefined : zipEntry.mode & 0o777);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
// A damaged entry leaves no part of its file
|
|
43
|
+
await rm(destination, { force: true });
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
await utimes(destination, zipEntry.mtime, zipEntry.mtime);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** The counts of the output. */
|
|
50
|
+
function countsOf(planned) {
|
|
51
|
+
let files = 0;
|
|
52
|
+
let directories = 0;
|
|
53
|
+
let links = 0;
|
|
54
|
+
let bytes = 0;
|
|
55
|
+
for (const entry of planned) {
|
|
56
|
+
if (entry.kind === "file") {
|
|
57
|
+
files++;
|
|
58
|
+
bytes += entry.size;
|
|
59
|
+
}
|
|
60
|
+
else if (entry.kind === "directory") {
|
|
61
|
+
directories++;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
links++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { files, directories, links, bytes };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Extract a tar.gz or zip archive into a folder, with a check of each path
|
|
71
|
+
*/
|
|
72
|
+
export async function archiveExtract(input, settings = DEFAULT_ARCHIVE_SETTINGS) {
|
|
73
|
+
const archive = resolve(input.archive);
|
|
74
|
+
const format = await formatOfFile(archive, input.format);
|
|
75
|
+
const overwrite = input.overwrite === true;
|
|
76
|
+
// The entries, before a write
|
|
77
|
+
let raw;
|
|
78
|
+
let zipEntries = [];
|
|
79
|
+
if (format === "zip") {
|
|
80
|
+
zipEntries = await readZipDirectory(archive, settings.maxEntries);
|
|
81
|
+
raw = [];
|
|
82
|
+
for (const [index, entry] of zipEntries.entries()) {
|
|
83
|
+
raw.push({ name: entry.name, kind: entry.kind, size: entry.size, index, ...(entry.kind === "symlink" ? { linkTarget: await readZipLink(archive, entry) } : {}) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
raw = (await readTarEntries(archive, settings)).map((entry) => ({ name: entry.name, kind: entry.kind, size: entry.size, linkTarget: entry.linkTarget }));
|
|
88
|
+
}
|
|
89
|
+
const folder = resolve(input.target);
|
|
90
|
+
await mkdir(folder, { recursive: true });
|
|
91
|
+
const target = await realpath(folder);
|
|
92
|
+
const planned = planExtract(raw, target, { overwrite, maxBytes: settings.maxBytes, maxEntries: settings.maxEntries });
|
|
93
|
+
if (format === "zip") {
|
|
94
|
+
await writeZipEntries(archive, target, planned, zipEntries);
|
|
95
|
+
}
|
|
96
|
+
else if (planned.length > 0) {
|
|
97
|
+
await extractTar(archive, target, new Set(planned.map((entry) => entry.name)));
|
|
98
|
+
}
|
|
99
|
+
return { target, format, ...countsOf(planned) };
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/procedures/archive/extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/procedures/archive/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/procedures/archive/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.list procedure
|
|
3
|
+
*
|
|
4
|
+
* The entries of a tar.gz, tar or zip archive: the path, the kind, the size, the mode, the time
|
|
5
|
+
* and the target of a link. The list reads the archive and writes nothing.
|
|
6
|
+
*/
|
|
7
|
+
import { type ArchiveSettings } from "../../format.js";
|
|
8
|
+
import type { ArchiveEntry, ArchiveFormat, ArchiveListInput, ArchiveListOutput } from "../../types.js";
|
|
9
|
+
/** The entries of an archive of a known format. `extract` uses them too. */
|
|
10
|
+
export declare function readEntries(path: string, format: ArchiveFormat, settings: ArchiveSettings): Promise<ArchiveEntry[]>;
|
|
11
|
+
/**
|
|
12
|
+
* The entries of a tar.gz or zip archive
|
|
13
|
+
*/
|
|
14
|
+
export declare function archiveList(input: ArchiveListInput, settings?: ArchiveSettings): Promise<ArchiveListOutput>;
|
|
15
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/procedures/archive/list.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG/F,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvG,4EAA4E;AAC5E,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAyBzH;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,QAAQ,GAAE,eAA0C,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAK3I"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* archive.list procedure
|
|
3
|
+
*
|
|
4
|
+
* The entries of a tar.gz, tar or zip archive: the path, the kind, the size, the mode, the time
|
|
5
|
+
* and the target of a link. The list reads the archive and writes nothing.
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { DEFAULT_ARCHIVE_SETTINGS, formatOfFile } from "../../format.js";
|
|
9
|
+
import { readTarEntries } from "../../tar.js";
|
|
10
|
+
import { readZipDirectory, readZipLink } from "../../zip.js";
|
|
11
|
+
/** The entries of an archive of a known format. `extract` uses them too. */
|
|
12
|
+
export async function readEntries(path, format, settings) {
|
|
13
|
+
if (format === "zip") {
|
|
14
|
+
const entries = await readZipDirectory(path, settings.maxEntries);
|
|
15
|
+
const result = [];
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
result.push({
|
|
18
|
+
path: entry.name,
|
|
19
|
+
kind: entry.kind,
|
|
20
|
+
size: entry.size,
|
|
21
|
+
...(entry.mode !== undefined ? { mode: entry.mode } : {}),
|
|
22
|
+
mtime: entry.mtime.toISOString(),
|
|
23
|
+
...(entry.kind === "symlink" ? { linkTarget: await readZipLink(path, entry) } : {}),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
const entries = await readTarEntries(path, settings);
|
|
29
|
+
return entries.map((entry) => ({
|
|
30
|
+
path: entry.name,
|
|
31
|
+
kind: entry.kind,
|
|
32
|
+
size: entry.size,
|
|
33
|
+
...(entry.mode !== undefined ? { mode: entry.mode & 0o7777 } : {}),
|
|
34
|
+
...(entry.mtime !== undefined ? { mtime: entry.mtime.toISOString() } : {}),
|
|
35
|
+
...(entry.linkTarget !== undefined ? { linkTarget: entry.linkTarget } : {}),
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The entries of a tar.gz or zip archive
|
|
40
|
+
*/
|
|
41
|
+
export async function archiveList(input, settings = DEFAULT_ARCHIVE_SETTINGS) {
|
|
42
|
+
const path = resolve(input.archive);
|
|
43
|
+
const format = await formatOfFile(path, input.format);
|
|
44
|
+
const entries = await readEntries(path, format, { ...settings, maxBytes: Number.MAX_SAFE_INTEGER });
|
|
45
|
+
return { format, entries, bytes: entries.reduce((sum, entry) => sum + (entry.kind === "file" ? entry.size : 0), 0) };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/procedures/archive/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,cAAc,aAAa,CAAC"}
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Importing this file registers the procedures of @procedures/archive in the global registry:
|
|
3
|
+
* `import "@procedures/archive/register"`. The package root registers nothing.
|
|
4
|
+
*/
|
|
5
|
+
import { registerArchiveProcedures } from "./module.js";
|
|
6
|
+
export * from "./module.js";
|
|
7
|
+
registerArchiveProcedures();
|
|
8
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD,cAAc,aAAa,CAAC;AAE5B,yBAAyB,EAAE,CAAC"}
|
package/dist/tar.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tar and tar.gz archives with `tar` (node-tar), the tar library of npm.
|
|
3
|
+
*
|
|
4
|
+
* - The list reads the headers with a `Parser`. It stops at the limit of entries or of bytes, so a
|
|
5
|
+
* compressed archive with a very large content stops early.
|
|
6
|
+
* - The extract runs after the check of `planExtract()`. It keeps the protections of node-tar:
|
|
7
|
+
* no absolute path, no `..`, and no write through a symbolic link. Its filter lets only the
|
|
8
|
+
* checked entries through, and `strict` makes each warning an error.
|
|
9
|
+
*/
|
|
10
|
+
import type { ArchiveEntryKind } from "./types.js";
|
|
11
|
+
/** One header of a tar archive. */
|
|
12
|
+
export interface TarEntryInfo {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly kind: ArchiveEntryKind;
|
|
15
|
+
readonly size: number;
|
|
16
|
+
readonly mode: number | undefined;
|
|
17
|
+
readonly mtime: Date | undefined;
|
|
18
|
+
readonly linkTarget: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* This function reads the headers of a tar or tar.gz archive. It throws `TOO_LARGE` at more than
|
|
22
|
+
* `maxEntries` entries or more than `maxBytes` bytes of files.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readTarEntries(path: string, limits: {
|
|
25
|
+
maxEntries: number;
|
|
26
|
+
maxBytes: number;
|
|
27
|
+
}): Promise<TarEntryInfo[]>;
|
|
28
|
+
/**
|
|
29
|
+
* This function extracts a tar or tar.gz archive into `target`, after the check. Only the names
|
|
30
|
+
* of `allowed` pass the filter. A name that the check did not see is an error at the end.
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractTar(path: string, target: string, allowed: ReadonlySet<string>): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* This function writes a tar or tar.gz archive of the `names` (relative to `cwd`) to `file`. The
|
|
35
|
+
* names are the full list: the walk of `archive.create` gives each file and folder.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createTar(file: string, cwd: string, names: readonly string[], gzip: boolean): Promise<void>;
|
|
38
|
+
//# sourceMappingURL=tar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar.d.ts","sourceRoot":"","sources":["../src/tar.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AA0DD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAO5H;AA6DD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1G;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjH"}
|