Per kdroidFilter's review on #664:
- Drop jdk.jlink filtering from AbstractJLinkTask (JEP 493 fix) — unrelated
to this PR, belongs in its own change.
- Drop nativeDistributions.appContent / jpackage --app-content — incomplete
API (no KDoc, no tests, JVM-only, flattens subdirectories) and redundant
with appResourcesRootDir + afterInstall for the systemd-unit use case this
was meant for.
- Expand KDoc on the four linux { afterInstall/afterRemove/beforeInstall/
beforeRemove } properties to document, on the properties themselves
rather than only in CLAUDE.md: electron-builder's single-quote-only macro
substitution for afterInstall/afterRemove, that beforeInstall/beforeRemove
get no macro substitution at all (raw fpm args), and the Pacman pre_remove
upgrade-detection caveat.
Wire the after-install/after-remove/before-install/before-remove scripts
for the GraalVM native-image packaging path too, matching the JVM/jpackage
path (previously only JVM was wired).
Follow-up to #629. The NSIS targets already expose most installer
settings, but not menuCategory / shortcutName, so a start menu group
could not be declared at all: the shortcut always landed in the start
menu root. This blocked projects that are migrating their MSI packaging
to the recommended NSIS format while keeping the start menu folder
they had under jpackage.
Add the two remaining CommonWindowsInstallerConfiguration options to
NsisSettings and emit them for nsis and nsis-web. Both default to null
(nothing emitted), so generated installers are unchanged unless a
project opts in - with the same intended exception as the MSI target:
windows.menuGroup, the jpackage-era name for the same concept, acts as
the default for nsis.menuCategory.
The "nsis target is unaffected by the msi settings" test asserted that
menuCategory never appears for NSIS; it now asserts the actual concern,
that msi {} values do not leak into the nsis block.
The MSI target only passed upgradeCode and perMachine to
electron-builder, so every other Windows installer setting fell back to
an electron-builder default: windows.menuGroup was ignored and the
shortcut landed in the start menu root, oneClick defaulted to true so
the installer ran without showing any UI, and runAfterFinish defaulted
to true so the app was launched as soon as the install finished.
Projects migrating from jpackage-based packaging lost all three
silently, with no way to set them from a build script.
Add the remaining CommonWindowsInstallerConfiguration options to
MsiSettings and emit them. Each defaults to the electron-builder
default so generated MSIs are unchanged unless a project opts in,
except that windows.menuGroup - the jpackage-era name for the same
concept - now acts as the default for msi.menuCategory, restoring the
start menu folder for projects that already declare one.
windows.menu / windows.shortcut are deliberately not mapped: their
jpackage defaults (false) are the opposite of electron-builder's
(true), so mapping them would silently drop shortcuts from MSIs that
are fine today. msi.createStartMenuShortcut / createDesktopShortcut
give explicit control instead.
generateWindowsConfig becomes internal so the rendered YAML can be
asserted in unit tests, matching generateLinuxConfig which is already
internal for the same reason.
buildAotJavaArgs now injects -Dnucleus.tao.fatalErrorDialog=false so a
training crash cannot hang in the #622 native dialog. Update the
argument-order assertions to match.
Show a modal GtkMessageDialog after the loop exits, install a shared
coroutine exception handler on every scene, and recheck pending fatals
before exitProcess(0) so a composition-side crash cannot quit with 0.
Unattended runs (headful tests, AOT training) disable the dialog via
-Dnucleus.tao.fatalErrorDialog=false.
gpg --import/--verify daemonize a gpg-agent for the throwaway homedir; with
no idle timeout it inherits the app's systemd scope and keeps it reported as
running forever, and the trailing exec bypassed the EXIT trap so the
root-owned work dir leaked too.
Verify with gpgv (never starts an agent) against a gpg --dearmor'd keyring
(pure filter), and drop the execs so the EXIT trap cleans the work dir —
set -e still propagates dpkg/rpm exit codes unchanged.
Fixes#567
The passwordless self-update helper runs as root via pkexec but takes a
caller-supplied package path that is writable by the unprivileged invoking
user. It verified the detached signature and then re-opened the same path
for `dpkg -i` / `rpm -U` — two separate opens, so the bytes could be swapped
between verify and install, giving a local user root code execution.
The helper now copies the package and its signature into a root-owned 0700
working dir first, then verifies and installs the copies, so the verified
bytes cannot be replaced. The deb path additionally refuses anything that is
not a strict version increase (`dpkg --compare-versions ... gt`), blocking a
rollback to an older, still-validly-signed, vulnerable release; `rpm -U`
already refuses downgrades.
The bash is exercised on real distros in packaging; a new unit test locks the
security-relevant structure (copy-before-verify, verify-the-copy, refuse
non-upgrade) so a refactor cannot silently drop it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
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/.
sun.misc.Unsafe is required by JNA, Jewel and many common libraries;
without it packaged distributions crash at runtime. jewel-demo no longer
needs its --add-opens workaround.
Rebased PR #354 on main and bumped Compose from 1.12.0-beta02 to the
latest RC. Scene create/render/dispose lives in TaoSceneBundle so the
1.12 FrameRecomposer + measureAndLayout + draw split is in one place.
Material3 moves to 1.12.0-alpha03 (Expressive APIs) and hot-reload to
1.2.0 to match the RC plugin bundle.
prepareAppResources was only wired for JVM run/jpackage. Native packaging
and runWithNativeAgent never set compose.application.resources.dir, so
sidecars such as Dawn's dxil.dll were missing.
Introduce msi { perMachine } mirroring nsis { perMachine } (1:1 mapping
to electron-builder msi.perMachine, no inversion). The deprecated
windows.perUserInstall keeps working when msi.perMachine is not set
explicitly; defaults are unchanged (MSI installs per-machine).
Closes#225
Wire orphan/project-class detection to the application target's
compilation outputs (e.g. jvm("desktop") → desktopMainClasses) instead
of hardcoded jvm/main paths, and ignore nest-internal reference edges so
real Room AppDatabase_Impl classes with nested types are still detected.