A preload belongs to the WebContents, not the page: a website loaded into the Settings tab kept window.cfg and could read every vault password, flip settings and install extensions without consent. Settings and add-on tabs now never load web content, and the channels behind settings-preload check their sender. `navigate` no longer accepts calls from web pages. Add-on updates trusted any publisherSig, whatever name it carried, even for bundled add-ons. The trust root is now the installed addon.json (publisher, or the operator key when there is none); versions must be plain dotted numbers; a community install can't take over a bundled or foreign id. Also: - autofill matches and fills against the live URL, not a stale prov.host - bns:// forwards the raw request path (..%2F escaped the name's bucket) - clipboard-read denied, openExternal asks; forged collision choices ignored - web pages can't window.open file:/chrome:/theseus:; data:/blob: no longer go to the search engine; the quick-links panel loses home-preload - clear-history-on-quit is awaited and removes history.json too - update helper takes its paths from the environment (non-ASCII profiles) - electrum poll has a deadline; misses wait at most 2.5 s - p records go through Tor; add-on proxy credentials are actually used - whole-folder require-cache bust on add-on version change; failed activate() no longer leaks its request filter - approvals released when the window closes; web-app ids stay on-origin
78 lines
4.4 KiB
JavaScript
78 lines
4.4 KiB
JavaScript
// Builds the batch script that installs a downloaded Theseus update AFTER
|
|
// the browser has exited. Pure function, no Electron — main.js writes the
|
|
// result next to its user data and starts it detached from will-quit; the
|
|
// tests run it against a dummy process and a fake setup.
|
|
//
|
|
// Why a helper at all: on 2026-09-11 an update ran while the app was still
|
|
// shutting down. The NSIS installer's uninstall-old-version step had moved
|
|
// the whole old install into its temp folder when both NSIS processes died
|
|
// ~8 s in, and the install step never wrote a file — the user was left with
|
|
// a folder missing app.asar and ffmpeg.dll. The exact killer was never
|
|
// identified, so this removes every overlap with our own lifetime:
|
|
//
|
|
// 1. poll until our PID is gone (tasklist), then a grace period for
|
|
// Chromium's child processes to follow;
|
|
// 2. start the installer from a process that is not in any job of ours
|
|
// (a detached cmd.exe survives the app exiting — verified — whereas a
|
|
// detached powershell.exe silently does nothing without a console, and
|
|
// a non-detached child is killed with the app);
|
|
// 3. wait for the installer and, if resources\app.asar is missing from the
|
|
// install dir afterwards, run it once more — the installer is
|
|
// idempotent and a second pass repairs a torn install.
|
|
//
|
|
// Batch specifics: `timeout` refuses to run without a console, so sleeps are
|
|
// `ping -n <n+1> 127.0.0.1`; the setup is launched with `start "" /wait`
|
|
// so the script blocks until the installer exits. The script deletes itself.
|
|
const ENV_SETUP = "THESEUS_UPDATE_SETUP";
|
|
const ENV_DIR = "THESEUS_UPDATE_DIR";
|
|
function buildUpdateHelperCmd({ pid, setupPath, installDir, args = ["/S", "--force-run"], graceSec = 2, maxWaitSec = 120 }) {
|
|
if (!Number.isInteger(pid) || pid <= 0) throw new Error("pid required");
|
|
if (typeof setupPath !== "string" || !setupPath || /["\r\n%]/.test(setupPath)) throw new Error("setupPath required (no quotes, percent signs or newlines)");
|
|
if (typeof installDir !== "string" || !installDir || /["\r\n%]/.test(installDir)) throw new Error("installDir required (no quotes, percent signs or newlines)");
|
|
const argStr = args.map(String).join(" ");
|
|
if (/["\r\n%]/.test(argStr)) throw new Error("installer args must not contain quotes, percent signs or newlines");
|
|
const grace = Math.max(0, graceSec | 0) + 1;
|
|
const maxIter = Math.max(1, maxWaitSec | 0);
|
|
// Everything runs inside a console-less cmd.exe, which has two traps
|
|
// (both hit while writing this): child console programs' redirected
|
|
// stdout comes back EMPTY (tasklist, wmic, even powershell — so no
|
|
// "tasklist | find" style probing), and `start /wait` is unreliable. What
|
|
// does work there: exit codes, timing, cmd-internal redirection, and a
|
|
// child powershell.exe blocking in Wait-Process. So the wait is a child
|
|
// PowerShell that returns once our PID is gone (or after maxWaitSec), the
|
|
// installer is invoked directly (from a batch file cmd.exe waits for it,
|
|
// and no ShellExecute means no mark-of-the-web prompt), and tools are
|
|
// addressed by full path so a Unix toolchain on PATH can't shadow them.
|
|
const S32 = "%SystemRoot%\\System32";
|
|
const PS = `${S32}\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
return [
|
|
"@echo off",
|
|
"setlocal",
|
|
// The paths arrive through the environment (updateHelperEnv), not as text
|
|
// in this file: cmd.exe reads a batch file in the OEM code page, so a
|
|
// UTF-8 "C:\Users\Иван\…" written here would point nowhere. The
|
|
// environment block is UTF-16 and survives intact.
|
|
`set "SETUP=%${ENV_SETUP}%"`,
|
|
`set "ASAR=%${ENV_DIR}%\\resources\\app.asar"`,
|
|
`"${PS}" -NoProfile -NonInteractive -Command "Wait-Process -Id ${pid} -Timeout ${maxIter} -ErrorAction SilentlyContinue"`,
|
|
`"${S32}\\ping.exe" -n ${grace} 127.0.0.1 >nul`,
|
|
`"%SETUP%" ${argStr}`,
|
|
`if not exist "%ASAR%" (`,
|
|
` "${S32}\\ping.exe" -n 4 127.0.0.1 >nul`,
|
|
` "%SETUP%" ${argStr}`,
|
|
")",
|
|
"endlocal",
|
|
// Self-delete without cmd.exe's "The batch file cannot be found" when it
|
|
// tries to read the next line: the (goto) 2>nul idiom ends the batch
|
|
// context first, then del runs on the same line.
|
|
`(goto) 2>nul & del "%~f0"`,
|
|
"",
|
|
].join("\r\n");
|
|
}
|
|
|
|
// Environment for the cmd.exe that runs the script above.
|
|
function updateHelperEnv({ setupPath, installDir }) {
|
|
return { [ENV_SETUP]: setupPath, [ENV_DIR]: installDir };
|
|
}
|
|
|
|
module.exports = { buildUpdateHelperCmd, updateHelperEnv };
|