Skip to content

fix(build): ship world-readable Linux packages when built with npm run build - #281

Open
miadisabelle wants to merge 1 commit into
johannesjo:mainfrom
miadisabelle:contrib/world-readable-packages
Open

miadisabelle wants to merge 1 commit into
johannesjo:mainfrom
miadisabelle:contrib/world-readable-packages

Conversation

@miadisabelle

Copy link
Copy Markdown
Contributor

A .deb built under a restrictive umask installs an app the desktop user can't open. Files installed or generated under that umask keep modes without world-read — node_modules as npm installed them, build output, and the directories and files electron-builder creates — and dpkg installs them verbatim. electron-builder adds world-read to files it copies itself, but not to asar-unpacked files, directories it creates, or files it generates.

Built with npm run build under umask 0007, 213 of the .deb's 298 entries have no world-read bit, including:

drwxrwx---  /opt/Parallel Code/
-rw-rw----  /opt/Parallel Code/resources/app.asar
-rw-rw----  /usr/share/applications/parallel-code.desktop

so the install directory can't be entered and the app is missing from the menu. CI is unaffected — the published v2.0.0 .deb has 293 entries, none without world-read — but a local or shared-machine build is not.

Two parts, both needed

199 of the 213 are asar-unpacked files under node_modules, whose modes npm set at install time, so a build-time umask alone can't fix them.

  • An afterPack hook normalises the packed app and the build resources directory: directories 755, files 644, or 755 where the owner could already execute them. A mode that's already right isn't rewritten. fpm reads the menu icon from build/ directly, so the hook also changes modes in the checkout's build/ — git records only the executable bit, which is kept, so no diff results.
  • umask 022 in the build script, for the five entries written after the hook runs: the .desktop entry, the changelog, resources/package-type, resources/apparmor-profile, and the package root.

This is electron-userland/electron-builder#2682, closed as not planned; neither electron-builder nor fpm has an option for file modes (--deb-use-file-permissions only affects owner and group).

Measured

Same checkout and machine, inspecting the .deb with dpkg-deb -c:

build entries without world-read
unmodified 213 of 298
afterPack hook only 5
hook + umask 022 0
hook without the build/ pass, build/icon.png at 0660 menu icon ships -rw-rw----
hook + umask 022, build/icon.png at 0660 0, icon -rw-r--r--

The set of executable files is identical before and after. The hook clears setuid/setgid/sticky bits; the packed tree has none — chrome-sandbox is packed 0755 and the deb's postinst sets 4755 only on systems without user namespaces. It returns early on macOS.

This only applies through npm run build, which every Linux path in release.yml and install.sh uses; a direct npx electron-builder still gets the hook but not the umask. knip is told umask is a shell builtin.

npm run check and check:static pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SK5d1Qh4JcJVF39HEkFBxX

…n build

A package built under a restrictive umask installs an app the desktop user
cannot open. Files installed or generated under that umask keep modes without
world-read — node_modules as npm installed them, build output, and the
directories and files electron-builder creates — and dpkg installs them
verbatim. electron-builder adds world-read to files it copies itself, but not
to asar-unpacked files, to directories it creates, or to files it generates.

Built with `npm run build` under umask 0007, 213 of the .deb's 298 entries
have no world-read bit, including:

  drwxrwx---  /opt/Parallel Code/
  -rw-rw----  /opt/Parallel Code/resources/app.asar
  -rw-rw----  /usr/share/applications/parallel-code.desktop

so the install directory cannot be entered and the app is missing from the
menu. 199 of the 213 are asar-unpacked files under node_modules, so a build-time
umask alone cannot fix them.

Two parts, both needed:

- An afterPack hook normalises the packed app and the build resources
  directory: directories 755, files 644, or 755 where the owner could already
  execute them, so no file becomes executable that its owner could not already
  execute. A mode that is already right is not rewritten. fpm reads the menu
  icon from build/ directly, so the hook also changes modes in the checkout's
  build/; git records only the executable bit, which is kept, so no diff
  results.
- `umask 022` in the build script, for the five entries written after the hook
  runs: the .desktop entry, the changelog, `resources/package-type`,
  `resources/apparmor-profile`, and the package root.

Measured on the .deb from the same checkout and machine:

  upstream                                213 of 298 without world-read
  afterPack hook only                       5
  hook + umask 022                          0
  hook disabled for build/, icon at 0660    icon ships -rw-rw----
  hook + umask 022, icon at 0660            0, icon -rw-r--r--

The set of executable files is identical before and after. The hook clears
setuid, setgid and sticky bits; the packed tree has none (chrome-sandbox is
packed 0755, and the deb's postinst sets 4755 only on systems without user
namespaces). CI builds are unaffected: the published v2.0.0 .deb has 293
entries and none without world-read.

knip is told `umask` is a shell builtin rather than an unlisted binary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant