molt-cli 1.0.2 → 1.0.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/cli.js +33 -1
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -338,13 +338,45 @@ function extractTarGz(tarGzPath, destDir) {
|
|
|
338
338
|
const extract = tar.extract();
|
|
339
339
|
const gunzip = zlib.createGunzip();
|
|
340
340
|
|
|
341
|
+
const MAX_EXTRACTED_SIZE = 50 * 1024 * 1024; // 50MB max decompressed
|
|
342
|
+
const MAX_FILES = 500; // max files in package
|
|
343
|
+
let totalSize = 0;
|
|
344
|
+
let fileCount = 0;
|
|
345
|
+
const resolvedDest = path.resolve(destDir);
|
|
346
|
+
|
|
341
347
|
extract.on('entry', (header, stream, next) => {
|
|
342
|
-
|
|
348
|
+
fileCount++;
|
|
349
|
+
if (fileCount > MAX_FILES) {
|
|
350
|
+
stream.destroy();
|
|
351
|
+
return reject(new Error(`Too many files in package (max ${MAX_FILES}). Aborting.`));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Path traversal protection: resolve and verify within destDir
|
|
355
|
+
const filePath = path.resolve(destDir, header.name);
|
|
356
|
+
if (!filePath.startsWith(resolvedDest + path.sep) && filePath !== resolvedDest) {
|
|
357
|
+
stream.resume(); // skip malicious entry
|
|
358
|
+
console.log(` ${C.red}⚠ Skipping suspicious path: ${header.name}${C.reset}`);
|
|
359
|
+
return next();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Skip symlinks (potential attack vector)
|
|
363
|
+
if (header.type === 'symlink' || header.type === 'link') {
|
|
364
|
+
stream.resume();
|
|
365
|
+
return next();
|
|
366
|
+
}
|
|
367
|
+
|
|
343
368
|
if (header.type === 'directory') {
|
|
344
369
|
fs.mkdirSync(filePath, { recursive: true });
|
|
345
370
|
stream.resume();
|
|
346
371
|
next();
|
|
347
372
|
} else {
|
|
373
|
+
// Tar bomb protection: track decompressed size
|
|
374
|
+
totalSize += header.size || 0;
|
|
375
|
+
if (totalSize > MAX_EXTRACTED_SIZE) {
|
|
376
|
+
stream.destroy();
|
|
377
|
+
return reject(new Error(`Package too large when extracted (>${MAX_EXTRACTED_SIZE / 1024 / 1024}MB). Possible tar bomb.`));
|
|
378
|
+
}
|
|
379
|
+
|
|
348
380
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
349
381
|
const out = fs.createWriteStream(filePath);
|
|
350
382
|
stream.pipe(out);
|