Skip to content

Commit 5cdce16

Browse files
committed
[APS-22106] replace decompress@4.2.1 with adm-zip@0.6.1
decompress@4.2.1 has an unpatched CVSS-9.1 Zip Slip vulnerability (GHSA-mp2f-45pm-3cg9 + two related advisories). The package is unmaintained (last release Feb 2020); no upstream fix is coming. Replacement: adm-zip@0.6.1. Why adm-zip@0.6.1: - Zero known CVEs (verified via `npm audit` — see below). - CJS package; direct `require()` works, no ESM incompat. - Node engine >= 14.0 — safe for the CLI's practical Node 14+ floor. (The @xhmikosr/decompress fork was rejected because it is ESM-only from 5.0.0 and requires Node 20+ at 11.x — either blocker breaks existing customers.) - No transitive dependencies (self-contained; supply-chain surface limited to adm-zip itself). - 19M+ weekly downloads. - Actively maintained: 0.6.1 was published 2026-09-11 specifically to close two prior advisories (GHSA-xcpc-8h2w-3j85 memory exhaustion and GHSA-vwc7-r8mq-g2x9 symlink Zip Slip). The fix commits are real code work — `eaa35fa7` ("Blocked extraction from writing through symlinks inside the target"), plus stripped setuid/setgid/sticky bits, rejected duplicate entry names, enforced decompression size caps. Also considered and rejected: - `extract-zip@2.0.1`: two unpatched HIGH symlink Zip Slip advisories (GHSA-jmr9-qjv8-65gv, GHSA-7pqw-9j4j-h8q3), last publish June 2020, fixAvailable:false. Same class of unmaintained-with-open-CVEs problem as decompress. - `@xhmikosr/decompress@11.1.3`: ESM-only across all versions (`type: module`) and requires Node >= 20; either breaks CJS require or breaks existing customers on Node 14/16/18. Flow preserved: both call sites keep the existing "primary + unzipper.Extract fallback" pattern. Only the primary lib changes. API swap: - `decompress(zipPath, targetDir)` -> Promise<Files[]> + `new AdmZip(zipPath).extractAllToAsync(targetDir, /*overwrite*/ true)` -> Promise<void> Both call sites already discarded the `Files[]` return value, so the shape difference is a no-op. Local verification: - `node --check` on both changed source files: OK - `npm ls adm-zip`: adm-zip@0.6.1 present - `npm ls decompress`: empty (vulnerable pkg gone; the remaining `decompress-response` is an unrelated HTTP-body decompressor). - `grep decompress` in source (excl. lockfile/node_modules): 0 hits - `npm audit` — adm-zip subtree: 0 vulnerabilities. Other pre-existing tree vulns unchanged: 10 (identical to master). - `npm test`: 723 passing / 2 pending / 16 failing — byte-identical to master baseline (the 16 failures are pre-existing flakes, unrelated to this PR).
1 parent 5a5b816 commit 5cdce16

6 files changed

Lines changed: 31 additions & 421 deletions

File tree

bin/helpers/buildArtifacts.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const logger = require('./logger').winstonLogger,
1111
const { default: axios } = require('axios');
1212
const { HttpsProxyAgent = require('https-proxy-agent') } = require('https-proxy-agent');
1313
const FormData = require('form-data');
14-
const decompress = require('decompress');
14+
const AdmZip = require('adm-zip');
1515
const unzipper = require("unzipper");
1616
const { setAxiosProxy } = require('./helper');
1717

@@ -154,10 +154,11 @@ const downloadAndUnzip = async (filePath, fileName, url) => {
154154
const unzipFile = async (filePath, fileName) => {
155155
return new Promise( async (resolve, reject) => {
156156
try {
157-
await decompress(path.join(filePath, fileName), filePath);
157+
const zip = new AdmZip(path.join(filePath, fileName));
158+
await zip.extractAllToAsync(filePath, /* overwrite */ true);
158159
resolve();
159160
} catch (error) {
160-
logger.debug(`Error unzipping with decompress, trying with unzipper. Stacktrace: ${error}.`);
161+
logger.debug(`Error unzipping with adm-zip, trying with unzipper. Stacktrace: ${error}.`);
161162
try {
162163
fs.createReadStream(path.join(filePath, fileName))
163164
.pipe(unzipper.Extract({ path: filePath }))

bin/helpers/reporterHTML.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const fs = require('fs'),
66
utils = require("./utils"),
77
Constants = require('./constants'),
88
config = require("./config"),
9-
decompress = require('decompress');
9+
AdmZip = require('adm-zip');
1010
const { isTurboScaleSession } = require('../helpers/atsHelper');
1111

1212
const { setAxiosProxy } = require('./helper');
@@ -171,15 +171,14 @@ function getReportResponse(filePath, fileName, reportJsonUrl) {
171171

172172
const unzipFile = async (filePath, fileName) => {
173173
return new Promise( async (resolve, reject) => {
174-
await decompress(path.join(filePath, fileName), filePath)
175-
.then((files) => {
176-
let message = "Unzipped the json and html successfully."
177-
resolve(message);
178-
})
179-
.catch((error) => {
174+
try {
175+
const zip = new AdmZip(path.join(filePath, fileName));
176+
await zip.extractAllToAsync(filePath, /* overwrite */ true);
177+
resolve("Unzipped the json and html successfully.");
178+
} catch (error) {
180179
reject(error);
181180
process.exitCode = Constants.ERROR_EXIT_CODE;
182-
});
181+
}
183182
});
184183
}
185184

0 commit comments

Comments
 (0)