Complete third-party attribution and pin the electron-builder toolchain - #574
Merged
kdroidFilter merged 2 commits intoAug 18, 2026
Merged
Conversation
The repository is MIT-licensed but redistributes Apache-2.0 code whose
attribution was not carried:
- 127 Kotlin files under plugin-build/plugin/src retain the
"Copyright 20xx JetBrains s.r.o. ... Apache 2.0" header — the Gradle
plugin is a fork of the Compose Multiplatform Gradle plugin — yet no
Apache-2.0 text accompanied them and the notices did not mention it.
- The vendored tao fork (Apache-2.0) and the three AccessKit crates
(MIT OR Apache-2.0) compile into libnucleus_tao, which ships inside
the decorated-window-tao JAR. accesskit_windows was also the only
vendored crate not keeping its upstream LICENSE-APACHE/LICENSE-MIT.
- LICENSE still carried the unfilled template line
"Copyright (c) 2020 <Source Code Author>".
THIRD_PARTY_NOTICES.md now covers all four components plus the build-time
tools and a pointer to Cargo.lock for the transitive Rust tree, with the
license texts collected once under licenses/. The LICENSE year follows the
git history (first commit 2026-02-08); the 2020 came from the JetBrains
headers, not from this project.
Apache-2.0 §4(a) requires the license copy to travel with binary
distributions too, so the jar tasks of plugin-build/plugin and
decorated-window-tao now stage the notices and licenses/ into META-INF/.
… 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent fixes on the redistribution and release paths, one commit each.
1. Third-party attribution (
chore:commit)The repository is MIT-licensed but redistributes Apache-2.0 code whose attribution was not carried:
plugin-build/plugin/srcretain theCopyright 20xx JetBrains s.r.o. … Apache 2.0header — the Gradle plugin is a fork of the Compose Multiplatform Gradle plugin — yet no Apache-2.0 text accompanied them, andTHIRD_PARTY_NOTICES.mddid not mention it.taofork (Apache-2.0) and the three AccessKit crates (MIT OR Apache-2.0) compile intolibnucleus_tao, which ships inside thedecorated-window-taoJAR.accesskit_windowswas also the only vendored crate not keeping its upstreamLICENSE-APACHE/LICENSE-MIT.LICENSEstill carried the unfilled template lineCopyright (c) 2020 <Source Code Author>.THIRD_PARTY_NOTICES.mdnow covers all four components, plus the build-time tools and a pointer toCargo.lockfor the transitive Rust tree. License texts are collected once underlicenses/. Apache-2.0 §4(a) requires the license copy to travel with binary distributions, so thejartasks ofplugin-build/pluginanddecorated-window-taostage the notices andlicenses/intoMETA-INF/.The
LICENSEyear follows the git history (first commit 2026-02-08,Initial commitimmediately followed by the plugin fork). The 2020 came from the JetBrains headers, not from this project.2. Pinned electron-builder toolchain (
fix(plugin):commit)Packaging invoked
npx --yes electron-builder@26.15.5. That 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. It 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.jsonpair pinning 277 packages (all fromregistry.npmjs.org, each with asha512integrity hash), stages it into a build-local directory, installs withnpm ci --ignore-scripts, and runs the CLI directly vianode node_modules/electron-builder/cli.js. A tarball whose bytes do not match the recorded hash fails the build, and nopre/postinstallhook from that tree runs at all.nodeandnpmstill 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) stay outside any npm lock file; they are verified by its own checksums.Also in this commit:
NodeJsDetector.detectNpm(), plus a shareddetectSibling()helper for thenodePathoverride. The task requires bothnodeandnpmand says what each is for when one is missing.MacDmgLzma.locateAppBuildersearched the npx cache, which no longer exists, and never looked inELECTRON_BUILDER_CACHEwhere electron-builder 26 actually downloadsapp-builder. It checks that first now, so the differential-update blockmap is no longer silently dropped after the DMG is recompressed with LZMA.scripts/update-electron-builder-lock.shregenerates 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.ElectronBuilderToolchainLockTestguards the boundary offline: version pinned in step with the Kotlin constant,lockfileVersion 3, every package from the public registry, every one carrying asha512hash.Verification
plugin-1.0.0.jaranddecorated-window-tao.jarrebuilt; both containMETA-INF/THIRD_PARTY_NOTICES.mdand the three files underMETA-INF/licenses/.TargetFormat.Zip:packageZipprovisions the toolchain and produces a signed 24 MB.appzip plus its.blockmap; build temporaries are cleaned up.abbrev, a single character), plugin republished, task rerun: the build fails withnpm error code EINTEGRITY. Lock file restored, nominal path revalidated.scripts/update-electron-builder-lock.shon a trusted network; result identical to the committed one.:plugin:check, 331 tests, all six suites touching the changed code green. Two failures are pre-existing and environment-specific (LinuxSignerTestneeds a usablegpg;RenameMacAppBundleTestdepends on APFS Unicode normalization) — both reproduce identically on a clean worktree at the base commit.Notes for review
LICENSEare the two judgement calls here; everything else is mechanical.npmonPATHin addition tonode. Node 18+ ships npm 9, so the existing Node version check covers thelockfileVersion 3floor (npm 7+) in practice. Say the word if you want an explicit npm version check for a cleaner error message.