fix(updater): verify download integrity (retry x3, byte-count, full gunzip) — truncated updates now error instead of silently no-oping

This commit is contained in:
avi 2026-10-01 14:52:42 -05:00
commit d052c3abc9
3 changed files with 105 additions and 29 deletions

View file

@ -118,6 +118,32 @@ function writeScript(box, spec) {
fs.rmSync(box, { recursive: true, force: true });
}
// --- archive integrity: verifyArchiveGzip + looksTruncated ---
{
const box = fs.mkdtempSync(path.join(os.tmpdir(), "folio-upd-gz-"));
const good = path.join(box, "good.tar.gz");
const zlib = await import("zlib");
fs.writeFileSync(good, zlib.gzipSync(Buffer.from("hello folio updater ".repeat(1000))));
ok(await u.verifyArchiveGzip(good), "complete gzip verifies");
// Truncate the good archive mid-stream: magic bytes present, payload short.
const full = fs.readFileSync(good);
const trunc = path.join(box, "trunc.tar.gz");
fs.writeFileSync(trunc, full.subarray(0, full.length - 50));
ok(!(await u.verifyArchiveGzip(trunc)), "truncated gzip REJECTED (the 0.1.2/0.1.3 bug)");
const notgz = path.join(box, "notgz.tar.gz");
fs.writeFileSync(notgz, "<html>error page</html>");
ok(!(await u.verifyArchiveGzip(notgz)), "non-gzip content rejected");
ok(!(await u.verifyArchiveGzip(path.join(box, "missing.tar.gz"))), "missing file rejected");
ok(u.looksTruncated(50, 100), "short byte count flagged");
ok(!u.looksTruncated(100, 100), "exact byte count passes");
ok(!u.looksTruncated(120, 100), "over-long passes");
ok(!u.looksTruncated(7, 0), "unknown total not flagged");
fs.rmSync(box, { recursive: true, force: true });
}
cleanupBundle();
if (fails) { console.error(`${fails} installer check(s) FAILED`); process.exit(1); }
console.log("All installer checks passed.");