fix(theseus/updater): verify manifest SHA-256 before arming install
The in-app updater fetched the setup .exe via
session.defaultSession.downloadURL and marked updateDownloadState="ready"
on any DownloadItem `done` with state === "completed", then handed
that path to install-update-now to spawn. No hash check against the
manifest — the same manifest that already carries a SHA-256 per file
and that the Ariadne addon updater verifies at ariadneDownloadInstaller
in this same file.
Consequence: a mid-stream truncation the runtime swallowed as
"completed" (a wrong Content-Length, a CDN cache truncation, an
interrupted TLS session, a corrupted mirror) armed install of a
half-file. install-update-now then ran the corrupt setup silently,
NSIS integrity check failed, uninstaller wiped the app first, and
Theseus was gone with nothing to click.
Now the completion handler streams the saved file through
crypto.createHash("sha256"), compares against updateAvailable.setupHash
from the manifest (already captured in checkForUpdate), and refuses to
arm install on mismatch — deletes the corrupt file and marks the
download failed so the retry loop can pick a fresh one up.
Companion fix to 0.3.32's --updated /S --force-run flags. Both
symptoms landed users in the same "browser vanished" state; 0.3.32
covered the spawn-side, this covers the download-side.
This commit is contained in:
parent
3c75abd1a6
commit
3e3b2c2b8b
2 changed files with 43 additions and 7 deletions
48
main.js
48
main.js
|
|
@ -1984,15 +1984,51 @@ function installDownloadTracker() {
|
|||
emitUpdateAvailable();
|
||||
});
|
||||
item.once("done", (_ev, state) => {
|
||||
if (state === "completed") {
|
||||
updateDownloadPath = item.getSavePath() || dst;
|
||||
updateDownloadState = "ready";
|
||||
console.log(`[update] silent fetch complete: ${updateDownloadPath}`);
|
||||
} else {
|
||||
if (state !== "completed") {
|
||||
updateDownloadState = "failed";
|
||||
console.warn(`[update] silent fetch ${state}`);
|
||||
emitUpdateAvailable();
|
||||
return;
|
||||
}
|
||||
emitUpdateAvailable();
|
||||
// NEVER mark "ready" without verifying the file hashes to what the
|
||||
// manifest promised. Electron's DownloadItem has been observed to
|
||||
// fire done/completed on truncated payloads (bad Content-Length,
|
||||
// CDN cache truncation, mid-stream TLS reset the runtime swallowed),
|
||||
// and 0.3.31's in-app updater then spawned a half-file as setup —
|
||||
// NSIS integrity check failed silently and the browser was gone.
|
||||
const savedPath = item.getSavePath() || dst;
|
||||
const expected = String(updateAvailable && updateAvailable.setupHash || "").toLowerCase();
|
||||
if (!expected) {
|
||||
updateDownloadState = "failed";
|
||||
console.warn(`[update] no manifest hash for ${savedPath} — refusing to arm install`);
|
||||
try { fs.unlinkSync(savedPath); } catch {}
|
||||
emitUpdateAvailable();
|
||||
return;
|
||||
}
|
||||
const crypto = require("node:crypto");
|
||||
const hash = crypto.createHash("sha256");
|
||||
const rs = fs.createReadStream(savedPath);
|
||||
rs.on("data", (c) => hash.update(c));
|
||||
rs.once("error", (e) => {
|
||||
updateDownloadState = "failed";
|
||||
console.warn(`[update] hash read failed: ${e.message}`);
|
||||
try { fs.unlinkSync(savedPath); } catch {}
|
||||
emitUpdateAvailable();
|
||||
});
|
||||
rs.once("end", () => {
|
||||
const got = hash.digest("hex").toLowerCase();
|
||||
if (got !== expected) {
|
||||
updateDownloadState = "failed";
|
||||
console.warn(`[update] SHA-256 mismatch: got ${got}, want ${expected} — refusing to arm install`);
|
||||
try { fs.unlinkSync(savedPath); } catch {}
|
||||
emitUpdateAvailable();
|
||||
return;
|
||||
}
|
||||
updateDownloadPath = savedPath;
|
||||
updateDownloadState = "ready";
|
||||
console.log(`[update] silent fetch complete + verified: ${savedPath}`);
|
||||
emitUpdateAvailable();
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "theseus-navigator",
|
||||
"version": "0.3.32",
|
||||
"version": "0.3.33",
|
||||
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
||||
"author": "Silent Mode",
|
||||
"main": "main.js",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue