Severity
Medium
Summary
scripts/build-sea.mjs downloads the official Node binary from nodejs.org/dist/... over HTTPS but does not verify the tarball/zip against the published SHASUMS256.txt. The Node binary is the largest, most-privileged piece of every shipped release — a tampered Node = full RCE for everyone who runs the CLI.
Affected code
scripts/build-sea.mjs:106-115:
process.stderr.write(`Downloading Node ${DEFAULT_NODE_VERSION} for ${osTag}-${arch}…\n`);
const url = nodeDownloadUrl(DEFAULT_NODE_VERSION, platform, arch);
if (platform === "win32") {
const zipPath = resolve(cacheRoot, `${dirname}.zip`);
run("curl", ["-fsSL", "-o", zipPath, url]);
run("unzip", ["-q", "-o", zipPath, "-d", cacheRoot]);
} else {
const tarPath = resolve(cacheRoot, `${dirname}.tar.gz`);
run("curl", ["-fsSL", "-o", tarPath, url]);
run("tar", ["xzf", tarPath, "-C", cacheRoot]);
}
Threat model
- TLS protects against passive on-path attackers when the runner trusts the public-CA root store.
- TLS does not protect against:
- Compromise of nodejs.org's web infrastructure or CDN.
- A future scenario where a CA mis-issues a cert for nodejs.org.
- Local cache poisoning if
dist-sea/cache/ is shared across builds.
- A bad Node binary becomes part of every macOS/Linux/Windows release, signed by our Developer ID — users have no easy way to detect.
Suggested fix
Pin a known-good SHA-256 per (version, platform, arch) triple in the script (or fetch SHASUMS256.txt from the same nodejs.org URL and verify against it), then shasum -a 256 -c the downloaded archive before extracting.
Sketch:
async function fetchAndVerify(url, archivePath, expectedSha256) {
run("curl", ["-fsSL", "-o", archivePath, url]);
const actual = createHash("sha256")
.update(readFileSync(archivePath))
.digest("hex");
if (actual !== expectedSha256) {
throw new Error(`SHA-256 mismatch on ${url}: expected ${expectedSha256}, got ${actual}`);
}
}
Or fetch SHASUMS256.txt once per DEFAULT_NODE_VERSION and look up the row matching the archive filename. Either approach makes a tampered upstream archive cause a hard build failure rather than shipping silently.
Found by
Internal pre-0.1.1 security review.
Severity
Medium
Summary
scripts/build-sea.mjsdownloads the official Node binary fromnodejs.org/dist/...over HTTPS but does not verify the tarball/zip against the publishedSHASUMS256.txt. The Node binary is the largest, most-privileged piece of every shipped release — a tampered Node = full RCE for everyone who runs the CLI.Affected code
scripts/build-sea.mjs:106-115:Threat model
dist-sea/cache/is shared across builds.Suggested fix
Pin a known-good SHA-256 per
(version, platform, arch)triple in the script (or fetchSHASUMS256.txtfrom the same nodejs.org URL and verify against it), thenshasum -a 256 -cthe downloaded archive before extracting.Sketch:
Or fetch
SHASUMS256.txtonce perDEFAULT_NODE_VERSIONand look up the row matching the archive filename. Either approach makes a tampered upstream archive cause a hard build failure rather than shipping silently.Found by
Internal pre-0.1.1 security review.