mirror of
https://github.com/wgtunnel/Nucleus.git
synced 2026-10-09 14:40:54 -07:00
Packaging invoked `npx --yes electron-builder@26.15.5`, which pinned only the top-level version: the ~275 transitive packages were re-resolved from the registry on every build, with no lock state to check them against and with install scripts enabled. That runs on the machine holding the code-signing certificates and notarization credentials, so a compromise anywhere in that tree executed with those. The plugin now embeds a package.json / package-lock.json pair pinning 277 packages (all from registry.npmjs.org, each with a sha512 integrity hash), stages it into a build-local directory, installs with `npm ci --ignore-scripts`, and runs the CLI directly through `node node_modules/electron-builder/cli.js`. A tarball whose bytes do not match the recorded hash now fails the build (EINTEGRITY), and no pre/postinstall hook from that tree runs at all. node and npm still come from the build machine; only the dependency tree is pinned. electron-builder's own run-time downloads (app-builder, 7-Zip, NSIS, snap/AppImage templates) remain outside any npm lock file — they are verified by its own checksums. Also in this change: - NodeJsDetector.detectNpm(), plus a shared detectSibling() helper for the nodePath override; the task now requires both node and npm and says what each is for when one is missing. - MacDmgLzma.locateAppBuilder searched the npx cache, which no longer exists, and never looked in ELECTRON_BUILDER_CACHE where electron-builder 26 actually downloads app-builder. It now checks that first, so the differential-update blockmap is no longer silently dropped after the DMG is recompressed with LZMA. - scripts/update-electron-builder-lock.sh regenerates the lock file, resolving against the public registry explicitly and refusing to run when certificate verification has been switched off — a lock file resolved over an unverified connection would record hashes for bytes nothing authenticated. - ElectronBuilderToolchainLockTest guards the boundary offline: version pinned in step with the Kotlin constant, lockfileVersion 3, every package from the public registry, every one carrying a sha512 hash. Verified end to end against a minimal consumer project (TargetFormat.Zip): the nominal path provisions the toolchain and produces the signed .app zip plus its blockmap, and tampering with a single integrity hash in the embedded lock file fails the build as intended.
89 lines
4.0 KiB
Bash
Executable File
89 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regenerates the pinned electron-builder toolchain lock file shipped inside the Gradle plugin.
|
|
#
|
|
# The plugin provisions electron-builder with `npm ci --ignore-scripts` against this lock file, so
|
|
# the lock file is the integrity boundary for ~275 npm packages pulled onto the machine that holds
|
|
# the code-signing certificates. Run this ONLY on a trusted machine with a clean npm config, and
|
|
# review the diff before committing.
|
|
#
|
|
# Usage:
|
|
# scripts/update-electron-builder-lock.sh # re-resolve the currently pinned version
|
|
# scripts/update-electron-builder-lock.sh 26.16.0 # bump to a new version
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
resource_dir="$repo_root/plugin-build/plugin/src/main/resources/nucleus/electron-builder"
|
|
tool_manager="$repo_root/plugin-build/plugin/src/main/kotlin/dev/nucleusframework/desktop/application/internal/electronbuilder/ElectronBuilderToolManager.kt"
|
|
|
|
pinned_version="$(sed -nE 's/.*ELECTRON_BUILDER_VERSION = "([^"]+)".*/\1/p' "$tool_manager")"
|
|
version="${1:-$pinned_version}"
|
|
|
|
if [[ -z "$version" ]]; then
|
|
echo "Could not read ELECTRON_BUILDER_VERSION from $tool_manager" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# This refuses only when certificate verification has been *switched off* — not merely because a
|
|
# corporate proxy terminates TLS. A proxy with its CA properly installed leaves verification on
|
|
# (NODE_TLS_REJECT_UNAUTHORIZED unset) and is fine: the hashes below cover tarball bytes, which such
|
|
# a proxy does not rewrite. With verification off there is nothing left to tell a scanning proxy from
|
|
# an attacker, and these hashes are what every later `npm ci` trusts.
|
|
if [[ -n "${NODE_TLS_REJECT_UNAUTHORIZED:-}" && "${NODE_TLS_REJECT_UNAUTHORIZED}" != "1" ]]; then
|
|
echo "Refusing to resolve the tree with certificate verification disabled" >&2
|
|
echo "(NODE_TLS_REJECT_UNAUTHORIZED=${NODE_TLS_REJECT_UNAUTHORIZED}). The integrity hashes would" >&2
|
|
echo "record whatever the connection served, unverified. Unset it — installing your proxy's CA" >&2
|
|
echo "certificate (NODE_EXTRA_CA_CERTS) keeps the proxy working with verification on." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Resolve against the public registry regardless of local npm config: the lock file must record
|
|
# registry.npmjs.org tarballs, so an internal mirror must not leak into `resolved` URLs. A mirror
|
|
# stays usable at install time — npm rewrites the host and still checks the recorded hash.
|
|
registry="https://registry.npmjs.org/"
|
|
configured_registry="$(npm config get registry)"
|
|
if [[ "$configured_registry" != "$registry" ]]; then
|
|
echo "note: npm is configured for $configured_registry; resolving against $registry for this run."
|
|
fi
|
|
|
|
workdir="$(mktemp -d)"
|
|
trap 'rm -rf "$workdir"' EXIT
|
|
|
|
cat > "$workdir/package.json" <<JSON
|
|
{
|
|
"name": "nucleus-electron-builder-toolchain",
|
|
"version": "1.0.0",
|
|
"private": true,
|
|
"description": "Pinned electron-builder toolchain used by the Nucleus Gradle plugin.",
|
|
"license": "MIT",
|
|
"dependencies": {
|
|
"electron-builder": "$version"
|
|
}
|
|
}
|
|
JSON
|
|
|
|
echo "Resolving electron-builder $version against $registry ..."
|
|
(cd "$workdir" && npm install --package-lock-only --ignore-scripts --no-audit --no-fund --registry="$registry")
|
|
|
|
echo "Verifying the resolved tree installs and runs ..."
|
|
(cd "$workdir" && npm ci --ignore-scripts --no-audit --no-fund --no-progress --registry="$registry" >/dev/null)
|
|
installed="$(cd "$workdir" && node node_modules/electron-builder/cli.js --version)"
|
|
if [[ "$installed" != "$version" ]]; then
|
|
echo "Installed CLI reports $installed, expected $version" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$resource_dir"
|
|
cp "$workdir/package.json" "$workdir/package-lock.json" "$resource_dir/"
|
|
|
|
echo
|
|
echo "Updated $resource_dir"
|
|
echo " electron-builder: $version ($(grep -c '"integrity":' "$resource_dir/package-lock.json") locked packages)"
|
|
if [[ "$version" != "$pinned_version" ]]; then
|
|
echo
|
|
echo "Now set ELECTRON_BUILDER_VERSION = \"$version\" in:"
|
|
echo " $tool_manager"
|
|
echo "(ElectronBuilderToolchainLockTest fails until it matches.)"
|
|
fi
|
|
echo
|
|
echo "Review the diff before committing: git diff -- $resource_dir"
|