Files
Elie Gambache e7407ca4cc fix(plugin): install electron-builder from a pinned lock file instead of npx
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.
2026-08-18 12:43:17 +03:00
..