@blamejs/blamejs-shop 0.0.83 → 0.0.85
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 +4 -0
- package/lib/email-campaigns.js +1 -1
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +4 -0
- package/lib/vendor/blamejs/README.md +1 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -0
- package/lib/vendor/blamejs/api-snapshot.json +151 -2
- package/lib/vendor/blamejs/fuzz/safe-archive.fuzz.js +37 -0
- package/lib/vendor/blamejs/index.js +15 -1
- package/lib/vendor/blamejs/lib/archive-adapters.js +629 -0
- package/lib/vendor/blamejs/lib/archive-read.js +781 -0
- package/lib/vendor/blamejs/lib/archive-tar-read.js +418 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +557 -0
- package/lib/vendor/blamejs/lib/archive.js +17 -0
- package/lib/vendor/blamejs/lib/audit.js +22 -7
- package/lib/vendor/blamejs/lib/backup/index.js +429 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +180 -0
- package/lib/vendor/blamejs/lib/guard-filename.js +205 -0
- package/lib/vendor/blamejs/lib/safe-archive.js +295 -0
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.7.json +86 -0
- package/lib/vendor/blamejs/release-notes/v0.12.8.json +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +247 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +127 -0
- package/package.json +2 -2
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* archive-tar-read — POSIX pax tar reader. Sibling of lib/archive-tar.js
|
|
4
|
+
* (write side) the way lib/archive-read.js is to lib/archive.js for ZIP.
|
|
5
|
+
* `b.archive.read.tar` lives here so the @module/@primitive validator
|
|
6
|
+
* can pair `function tar(adapter, opts)` cleanly with the @primitive
|
|
7
|
+
* comment block without colliding with the write-side `function
|
|
8
|
+
* tarBuilder()` (which is exported as `b.archive.tar`).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
var nodeFs = require("node:fs");
|
|
12
|
+
var nodePath = require("node:path");
|
|
13
|
+
var C = require("./constants");
|
|
14
|
+
var lazyRequire = require("./lazy-require");
|
|
15
|
+
var safeBuffer = require("./safe-buffer");
|
|
16
|
+
var archiveTar = require("./archive-tar");
|
|
17
|
+
|
|
18
|
+
var TarError = archiveTar.TarError;
|
|
19
|
+
var _parseHeader = archiveTar._parseHeader;
|
|
20
|
+
|
|
21
|
+
var guardFilename = lazyRequire(function () { return require("./guard-filename"); });
|
|
22
|
+
var guardArchive = lazyRequire(function () { return require("./guard-archive"); });
|
|
23
|
+
|
|
24
|
+
var BLOCK_SIZE = C.BYTES.bytes(512);
|
|
25
|
+
|
|
26
|
+
var TF_REGULAR = "0";
|
|
27
|
+
var TF_REGULAR_LEGACY = "\u0000";
|
|
28
|
+
var TF_HARDLINK = "1";
|
|
29
|
+
var TF_SYMLINK = "2";
|
|
30
|
+
var TF_CHARDEV = "3";
|
|
31
|
+
var TF_BLOCKDEV = "4";
|
|
32
|
+
var TF_DIRECTORY = "5";
|
|
33
|
+
var TF_FIFO = "6";
|
|
34
|
+
var TF_CONTIGUOUS = "7";
|
|
35
|
+
var TF_PAX_EXTENDED = "x";
|
|
36
|
+
var TF_PAX_GLOBAL = "g";
|
|
37
|
+
|
|
38
|
+
void TF_CHARDEV; void TF_BLOCKDEV; void TF_FIFO; void TF_CONTIGUOUS;
|
|
39
|
+
|
|
40
|
+
var DEFAULT_BOMB_POLICY = Object.freeze({
|
|
41
|
+
maxEntries: 65535, // allow:raw-byte-literal — operator-friendly default ceiling
|
|
42
|
+
maxEntryDecompressedBytes: C.BYTES.mib(128),
|
|
43
|
+
maxTotalDecompressedBytes: C.BYTES.gib(4),
|
|
44
|
+
maxExpansionRatio: 100, // allow:raw-byte-literal — tar has no compression-ratio concept, but keep field for orchestrator policy parity
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
var DEFAULT_ENTRY_TYPE_POLICY = Object.freeze({
|
|
48
|
+
symlinks: false,
|
|
49
|
+
hardlinks: false,
|
|
50
|
+
devices: false,
|
|
51
|
+
fifos: false,
|
|
52
|
+
sockets: false,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function _normalizeBombPolicy(p) {
|
|
56
|
+
if (!p) return DEFAULT_BOMB_POLICY;
|
|
57
|
+
return Object.freeze(Object.assign({}, DEFAULT_BOMB_POLICY, p));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function _normalizeEntryTypePolicy(p) {
|
|
61
|
+
if (!p) return DEFAULT_ENTRY_TYPE_POLICY;
|
|
62
|
+
return Object.freeze(Object.assign({}, DEFAULT_ENTRY_TYPE_POLICY, p));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _emitAudit(opts, action, outcome, metadata) {
|
|
66
|
+
if (!opts || !opts.audit || typeof opts.audit.safeEmit !== "function") return;
|
|
67
|
+
try { opts.audit.safeEmit({ action: action, outcome: outcome, metadata: metadata }); }
|
|
68
|
+
catch (_e) { /* drop-silent */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _isZeroBlock(buf) {
|
|
72
|
+
for (var i = 0; i < BLOCK_SIZE; i += 1) {
|
|
73
|
+
if (buf[i] !== 0) return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _parsePaxRecords(buf) {
|
|
79
|
+
var out = Object.create(null);
|
|
80
|
+
var pos = 0;
|
|
81
|
+
var s = buf.toString("utf8");
|
|
82
|
+
while (pos < s.length) {
|
|
83
|
+
var spaceIdx = s.indexOf(" ", pos);
|
|
84
|
+
if (spaceIdx < 0) {
|
|
85
|
+
throw new TarError("archive-tar/bad-pax-record",
|
|
86
|
+
"pax record at byte " + pos + " missing length-space delimiter");
|
|
87
|
+
}
|
|
88
|
+
var lenStr = s.slice(pos, spaceIdx);
|
|
89
|
+
var len = parseInt(lenStr, 10);
|
|
90
|
+
if (!Number.isFinite(len) || len <= 0) {
|
|
91
|
+
throw new TarError("archive-tar/bad-pax-record",
|
|
92
|
+
"pax record length " + JSON.stringify(lenStr) + " is not a positive integer");
|
|
93
|
+
}
|
|
94
|
+
var record = s.slice(pos, pos + len);
|
|
95
|
+
if (record[record.length - 1] !== "\n") {
|
|
96
|
+
throw new TarError("archive-tar/bad-pax-record",
|
|
97
|
+
"pax record at byte " + pos + " not newline-terminated");
|
|
98
|
+
}
|
|
99
|
+
var eqIdx = record.indexOf("=", spaceIdx - pos + 1);
|
|
100
|
+
if (eqIdx < 0) {
|
|
101
|
+
throw new TarError("archive-tar/bad-pax-record",
|
|
102
|
+
"pax record at byte " + pos + " missing key=value delimiter");
|
|
103
|
+
}
|
|
104
|
+
var key = record.slice(spaceIdx - pos + 1, eqIdx);
|
|
105
|
+
var value = record.slice(eqIdx + 1, record.length - 1);
|
|
106
|
+
out[key] = value;
|
|
107
|
+
pos += len;
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function _collectAdapterBytes(adapter) {
|
|
113
|
+
if (adapter.kind === "random-access") {
|
|
114
|
+
var size = adapter.size;
|
|
115
|
+
if (size == null && typeof adapter.resolveSize === "function") {
|
|
116
|
+
size = await adapter.resolveSize();
|
|
117
|
+
}
|
|
118
|
+
if (typeof size !== "number" || size === 0) return Buffer.alloc(0);
|
|
119
|
+
return adapter.range(0, size);
|
|
120
|
+
}
|
|
121
|
+
if (adapter.kind === "trusted-sequential") {
|
|
122
|
+
var collector = safeBuffer.boundedChunkCollector({
|
|
123
|
+
maxBytes: C.BYTES.gib(1),
|
|
124
|
+
errorClass: TarError,
|
|
125
|
+
sizeCode: "archive-tar/trusted-stream-too-large",
|
|
126
|
+
});
|
|
127
|
+
for await (var chunk of adapter.readable) {
|
|
128
|
+
collector.push(chunk);
|
|
129
|
+
}
|
|
130
|
+
return collector.result();
|
|
131
|
+
}
|
|
132
|
+
throw new TarError("archive-tar/bad-adapter",
|
|
133
|
+
"read.tar: adapter kind " + adapter.kind + " not supported");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function _classifyTypeflag(tf) {
|
|
137
|
+
if (tf === TF_REGULAR || tf === TF_REGULAR_LEGACY) return "file";
|
|
138
|
+
if (tf === TF_DIRECTORY) return "directory";
|
|
139
|
+
if (tf === TF_SYMLINK) return "symlink";
|
|
140
|
+
if (tf === TF_HARDLINK) return "hardlink";
|
|
141
|
+
if (tf === TF_CHARDEV || tf === TF_BLOCKDEV) return "device";
|
|
142
|
+
if (tf === TF_FIFO) return "fifo";
|
|
143
|
+
if (tf === TF_CONTIGUOUS) return "file";
|
|
144
|
+
return "unknown";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @primitive b.archive.read.tar
|
|
149
|
+
* @signature b.archive.read.tar(adapter, opts?)
|
|
150
|
+
* @since 0.12.8
|
|
151
|
+
* @status stable
|
|
152
|
+
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
153
|
+
* @related b.archive.read.zip, b.safeArchive.extract, b.guardArchive.tarEntryPolicy
|
|
154
|
+
*
|
|
155
|
+
* POSIX pax tar reader. Walks 512-byte header blocks sequentially +
|
|
156
|
+
* extracts via the same bomb-cap / path-traversal / entry-type policy
|
|
157
|
+
* surface as the v0.12.7 ZIP reader. Random-access and trusted-stream
|
|
158
|
+
* adapters are both first-class (tar has no central directory, so
|
|
159
|
+
* sequential header-by-header is the canonical adversarial-safe
|
|
160
|
+
* path).
|
|
161
|
+
*
|
|
162
|
+
* @opts
|
|
163
|
+
* bombPolicy: { maxEntries, maxEntryDecompressedBytes,
|
|
164
|
+
* maxTotalDecompressedBytes, maxExpansionRatio },
|
|
165
|
+
* entryTypePolicy: { symlinks, hardlinks, devices, fifos, sockets },
|
|
166
|
+
* allowDangerous: { symlinks, hardlinks },
|
|
167
|
+
* guardProfile: "strict" | "balanced" | "permissive",
|
|
168
|
+
* audit: b.audit,
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* var reader = b.archive.read.tar(b.archive.adapters.buffer(Buffer.alloc(0)));
|
|
172
|
+
* var entries = await reader.inspect();
|
|
173
|
+
* void entries;
|
|
174
|
+
*/
|
|
175
|
+
function tar(adapter, opts) {
|
|
176
|
+
if (!adapter || (adapter.kind !== "random-access" && adapter.kind !== "trusted-sequential")) {
|
|
177
|
+
throw new TarError("archive-tar/bad-adapter",
|
|
178
|
+
"read.tar(adapter): adapter must come from b.archive.adapters.*");
|
|
179
|
+
}
|
|
180
|
+
opts = opts || {};
|
|
181
|
+
var bombPolicy = _normalizeBombPolicy(opts.bombPolicy);
|
|
182
|
+
var entryTypePolicy = _normalizeEntryTypePolicy(opts.entryTypePolicy);
|
|
183
|
+
|
|
184
|
+
async function _walk() {
|
|
185
|
+
var bytes = await _collectAdapterBytes(adapter);
|
|
186
|
+
if (bytes.length === 0) return { entries: [], bytes: bytes };
|
|
187
|
+
var pos = 0;
|
|
188
|
+
var entries = [];
|
|
189
|
+
var pendingPax = null;
|
|
190
|
+
var globalPax = null;
|
|
191
|
+
var zeroBlockCount = 0;
|
|
192
|
+
while (pos + BLOCK_SIZE <= bytes.length) {
|
|
193
|
+
var block = bytes.slice(pos, pos + BLOCK_SIZE);
|
|
194
|
+
pos += BLOCK_SIZE;
|
|
195
|
+
if (_isZeroBlock(block)) {
|
|
196
|
+
zeroBlockCount += 1;
|
|
197
|
+
if (zeroBlockCount >= 2) break;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
zeroBlockCount = 0;
|
|
201
|
+
var hdr = _parseHeader(block);
|
|
202
|
+
if (hdr.typeflag === TF_PAX_EXTENDED || hdr.typeflag === TF_PAX_GLOBAL) {
|
|
203
|
+
var bodyEnd = pos + Math.ceil(hdr.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
204
|
+
if (bodyEnd > bytes.length) {
|
|
205
|
+
throw new TarError("archive-tar/truncated-entry",
|
|
206
|
+
"pax extended header declares size=" + hdr.size +
|
|
207
|
+
" bytes but only " + (bytes.length - pos) +
|
|
208
|
+
" bytes remain after header — archive truncated mid-stream");
|
|
209
|
+
}
|
|
210
|
+
var paxBody = bytes.slice(pos, pos + hdr.size);
|
|
211
|
+
var records = _parsePaxRecords(paxBody);
|
|
212
|
+
if (hdr.typeflag === TF_PAX_EXTENDED) pendingPax = records;
|
|
213
|
+
else globalPax = records;
|
|
214
|
+
pos = bodyEnd;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (globalPax) {
|
|
218
|
+
if (globalPax.path) hdr.name = globalPax.path;
|
|
219
|
+
if (globalPax.size) hdr.size = parseInt(globalPax.size, 10);
|
|
220
|
+
if (globalPax.linkpath) hdr.linkname = globalPax.linkpath;
|
|
221
|
+
}
|
|
222
|
+
if (pendingPax) {
|
|
223
|
+
if (pendingPax.path) hdr.name = pendingPax.path;
|
|
224
|
+
if (pendingPax.size) hdr.size = parseInt(pendingPax.size, 10);
|
|
225
|
+
if (pendingPax.linkpath) hdr.linkname = pendingPax.linkpath;
|
|
226
|
+
pendingPax = null;
|
|
227
|
+
}
|
|
228
|
+
if (entries.length >= bombPolicy.maxEntries) {
|
|
229
|
+
throw new TarError("archive-tar/too-many-entries",
|
|
230
|
+
"archive has more than " + bombPolicy.maxEntries + " entries");
|
|
231
|
+
}
|
|
232
|
+
if (hdr.size > bombPolicy.maxEntryDecompressedBytes) {
|
|
233
|
+
throw new TarError("archive-tar/entry-too-large",
|
|
234
|
+
"entry " + JSON.stringify(hdr.name) +
|
|
235
|
+
" size=" + hdr.size + " exceeds maxEntryDecompressedBytes=" +
|
|
236
|
+
bombPolicy.maxEntryDecompressedBytes);
|
|
237
|
+
}
|
|
238
|
+
var bodyStart = pos;
|
|
239
|
+
var paddedSize = Math.ceil(hdr.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
240
|
+
// Codex P1 on v0.12.8 PR #159 — refuse truncated archives upfront.
|
|
241
|
+
// The walker advances `pos` by the declared padded block size; if
|
|
242
|
+
// the buffer ends mid-body, extract() would silently slice a
|
|
243
|
+
// partial payload (header says 11 bytes, buffer holds 8 — without
|
|
244
|
+
// this check we'd write the 8-byte prefix as if it were the
|
|
245
|
+
// complete file). Detector: archive-tar-truncated-walker-without-bounds-check.
|
|
246
|
+
if (bodyStart + paddedSize > bytes.length) {
|
|
247
|
+
throw new TarError("archive-tar/truncated-entry",
|
|
248
|
+
"entry " + JSON.stringify(hdr.name) +
|
|
249
|
+
" declares size=" + hdr.size + " (padded=" + paddedSize +
|
|
250
|
+
") but only " + (bytes.length - bodyStart) +
|
|
251
|
+
" bytes remain after header — archive truncated mid-stream");
|
|
252
|
+
}
|
|
253
|
+
hdr._bodyStart = bodyStart;
|
|
254
|
+
hdr._paddedSize = paddedSize;
|
|
255
|
+
entries.push(hdr);
|
|
256
|
+
pos += paddedSize;
|
|
257
|
+
}
|
|
258
|
+
return { entries: entries, bytes: bytes };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function inspect() {
|
|
262
|
+
var walked = await _walk();
|
|
263
|
+
var entries = walked.entries;
|
|
264
|
+
return entries.map(function (e) {
|
|
265
|
+
return {
|
|
266
|
+
name: e.name,
|
|
267
|
+
size: e.size,
|
|
268
|
+
mtime: new Date(e.mtime * C.TIME.seconds(1)),
|
|
269
|
+
mode: e.mode,
|
|
270
|
+
typeflag: e.typeflag,
|
|
271
|
+
linkname: e.linkname,
|
|
272
|
+
uname: e.uname,
|
|
273
|
+
gname: e.gname,
|
|
274
|
+
entryType: _classifyTypeflag(e.typeflag),
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function extract(extractOpts) {
|
|
280
|
+
extractOpts = extractOpts || {};
|
|
281
|
+
if (typeof extractOpts.destination !== "string" || extractOpts.destination.length === 0) {
|
|
282
|
+
throw new TarError("archive-tar/no-destination",
|
|
283
|
+
"extract: opts.destination must be non-empty string");
|
|
284
|
+
}
|
|
285
|
+
var destination = nodePath.resolve(extractOpts.destination);
|
|
286
|
+
if (!nodeFs.existsSync(destination)) {
|
|
287
|
+
nodeFs.mkdirSync(destination, { recursive: true });
|
|
288
|
+
}
|
|
289
|
+
var allowDangerous = extractOpts.allowDangerous || {};
|
|
290
|
+
var walked = await _walk();
|
|
291
|
+
var entries = walked.entries;
|
|
292
|
+
var bytes = walked.bytes;
|
|
293
|
+
if (opts.guardProfile !== false) {
|
|
294
|
+
var profile = opts.guardProfile || "balanced";
|
|
295
|
+
var guardEntries = entries.map(function (e) {
|
|
296
|
+
return {
|
|
297
|
+
name: e.name,
|
|
298
|
+
size: e.size,
|
|
299
|
+
compressedSize: e.size,
|
|
300
|
+
isSymlink: e.typeflag === TF_SYMLINK,
|
|
301
|
+
isHardlink: e.typeflag === TF_HARDLINK,
|
|
302
|
+
linkTarget: e.linkname,
|
|
303
|
+
isDirectory: e.typeflag === TF_DIRECTORY,
|
|
304
|
+
isEncrypted: false,
|
|
305
|
+
attrs: { mode: e.mode },
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
var guardResult = guardArchive().validateEntries(guardEntries, { profile: profile });
|
|
309
|
+
if (guardResult && Array.isArray(guardResult.issues)) {
|
|
310
|
+
var critical = guardResult.issues.filter(function (i) { return i.severity === "critical"; });
|
|
311
|
+
if (critical.length > 0) {
|
|
312
|
+
_emitAudit(opts, "archive.read.tar.extract.refused", "refused", {
|
|
313
|
+
entries: entries.length,
|
|
314
|
+
issues: critical.map(function (i) { return i.ruleId; }),
|
|
315
|
+
});
|
|
316
|
+
throw new TarError("archive-tar/guard-refused",
|
|
317
|
+
"extract refused — " + critical.length + " critical guard issue(s)");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
var written = [];
|
|
322
|
+
var bytesExtracted = 0;
|
|
323
|
+
var totalDecompressed = 0;
|
|
324
|
+
try {
|
|
325
|
+
for (var i = 0; i < entries.length; i += 1) {
|
|
326
|
+
var entry = entries[i];
|
|
327
|
+
var type = _classifyTypeflag(entry.typeflag);
|
|
328
|
+
if (type === "device" || type === "fifo" || type === "socket") {
|
|
329
|
+
throw new TarError("archive-tar/entry-type-refused",
|
|
330
|
+
"entry " + JSON.stringify(entry.name) + " is a " + type +
|
|
331
|
+
" — refused unconditionally (no application use case)");
|
|
332
|
+
}
|
|
333
|
+
if (type === "symlink" && !(allowDangerous.symlinks || entryTypePolicy.symlinks)) {
|
|
334
|
+
throw new TarError("archive-tar/entry-type-refused",
|
|
335
|
+
"entry " + JSON.stringify(entry.name) + " is a symlink — refused by entryTypePolicy " +
|
|
336
|
+
"(opt in via allowDangerous: { symlinks: true })");
|
|
337
|
+
}
|
|
338
|
+
if (type === "hardlink" && !(allowDangerous.hardlinks || entryTypePolicy.hardlinks)) {
|
|
339
|
+
throw new TarError("archive-tar/entry-type-refused",
|
|
340
|
+
"entry " + JSON.stringify(entry.name) + " is a hardlink — refused by entryTypePolicy " +
|
|
341
|
+
"(opt in via allowDangerous: { hardlinks: true })");
|
|
342
|
+
}
|
|
343
|
+
var resolvedPath = guardFilename().verifyExtractionPath(entry.name, destination);
|
|
344
|
+
if (type === "directory") {
|
|
345
|
+
nodeFs.mkdirSync(resolvedPath, { recursive: true });
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (nodeFs.existsSync(resolvedPath)) {
|
|
349
|
+
throw new TarError("archive-tar/destination-exists",
|
|
350
|
+
"extract: destination file already exists at " +
|
|
351
|
+
JSON.stringify(resolvedPath) + " — refuse to overwrite");
|
|
352
|
+
}
|
|
353
|
+
if (type === "symlink" || type === "hardlink") {
|
|
354
|
+
guardFilename().verifyExtractionPath(entry.linkname, destination);
|
|
355
|
+
if (type === "symlink") {
|
|
356
|
+
nodeFs.symlinkSync(entry.linkname, resolvedPath);
|
|
357
|
+
} else {
|
|
358
|
+
var hardlinkTarget = nodePath.join(destination, entry.linkname);
|
|
359
|
+
nodeFs.linkSync(hardlinkTarget, resolvedPath);
|
|
360
|
+
}
|
|
361
|
+
written.push({ name: entry.name, bytesWritten: 0, path: resolvedPath });
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
var parentDir = nodePath.dirname(resolvedPath);
|
|
365
|
+
if (!nodeFs.existsSync(parentDir)) {
|
|
366
|
+
nodeFs.mkdirSync(parentDir, { recursive: true });
|
|
367
|
+
}
|
|
368
|
+
var body = bytes.slice(entry._bodyStart, entry._bodyStart + entry.size);
|
|
369
|
+
totalDecompressed += body.length;
|
|
370
|
+
if (totalDecompressed > bombPolicy.maxTotalDecompressedBytes) {
|
|
371
|
+
throw new TarError("archive-tar/total-too-large",
|
|
372
|
+
"cumulative uncompressed=" + totalDecompressed +
|
|
373
|
+
" exceeds maxTotalDecompressedBytes during extract");
|
|
374
|
+
}
|
|
375
|
+
var tmpPath = resolvedPath + ".__blamejs-archive-tar-tmp__";
|
|
376
|
+
nodeFs.writeFileSync(tmpPath, body);
|
|
377
|
+
nodeFs.renameSync(tmpPath, resolvedPath);
|
|
378
|
+
written.push({ name: entry.name, bytesWritten: body.length, path: resolvedPath });
|
|
379
|
+
bytesExtracted += body.length;
|
|
380
|
+
}
|
|
381
|
+
} catch (extractErr) {
|
|
382
|
+
try {
|
|
383
|
+
for (var w = 0; w < written.length; w += 1) {
|
|
384
|
+
if (nodeFs.existsSync(written[w].path)) {
|
|
385
|
+
nodeFs.rmSync(written[w].path);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} catch (_e) { /* drop-silent */ }
|
|
389
|
+
_emitAudit(opts, "archive.read.tar.extract.aborted", "failure", {
|
|
390
|
+
entries: entries.length,
|
|
391
|
+
written: written.length,
|
|
392
|
+
error: extractErr && (extractErr.code || extractErr.message),
|
|
393
|
+
});
|
|
394
|
+
throw extractErr;
|
|
395
|
+
}
|
|
396
|
+
_emitAudit(opts, "archive.read.tar.extract.completed", "success", {
|
|
397
|
+
entries: entries.length,
|
|
398
|
+
bytesExtracted: bytesExtracted,
|
|
399
|
+
});
|
|
400
|
+
return {
|
|
401
|
+
entries: written,
|
|
402
|
+
destinationRoot: destination,
|
|
403
|
+
bytesExtracted: bytesExtracted,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
kind: "tar-reader",
|
|
409
|
+
inspect: inspect,
|
|
410
|
+
extract: extract,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
module.exports = {
|
|
415
|
+
tar: tar,
|
|
416
|
+
DEFAULT_BOMB_POLICY: DEFAULT_BOMB_POLICY,
|
|
417
|
+
DEFAULT_ENTRY_TYPE_POLICY: DEFAULT_ENTRY_TYPE_POLICY,
|
|
418
|
+
};
|