Skip to content

fix(desktop): strip group/world write bits from Linux package files - #352

Merged
shuv1337 merged 1 commit into
mainfrom
fix/linux-deb-write-bits
Nov 3, 2026
Merged

shuv1337 merged 1 commit into
mainfrom
fix/linux-deb-write-bits

Conversation

@shuv1337

@shuv1337 shuv1337 commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Electron's Linux zips ship world-writable files (0777 binaries, 0666 .paks), and electron-builder copies those modes verbatim into the .deb and AppImage. The current nightly .deb (v0.1.5-nightly.20261101.79, amd64) installs 14 world-writable files under /opt/S2Code (Nightly), including the main executable and chrome-sandbox.

Ports upstream pingdotgg/t3code#28351 (bfa1ff6):

  • scripts/linux-after-pack.ts: an electron-builder afterPack hook that walks appOutDir with lstat, skips symlinks (so it never follows a link out of the app folder), and runs chmod(path, mode & 0o7755) on any entry with group/other write bits set.
  • Registered as buildConfig.afterPack in the Linux branch of scripts/build-desktop-artifact.ts.
  • Ported upstream's test (scripts/linux-after-pack.test.ts), which covers 0777, 0666, setuid 0o4777, and 0777 directories, and checks that a file reached through a symlinked directory outside the app keeps its mode.
  • Added the hook to the scripts entries in knip.jsonc, since electron-builder loads it by path.

Deviation from upstream: upstream's hook also calls prepare-server-compile-cache.cjs. This fork doesn't have that file, so that call is left out.

Verification

  • vp test run scripts/linux-after-pack.test.ts: 1 file, 1 test passed.
  • vp run knip:check: passes. Plain vp run knip exits 1, but its findings are identical to unmodified HEAD (5bbc455) and none of them involve linux-after-pack.
  • vp run --filter t3 --filter @t3tools/contracts --filter @t3tools/shared --filter @t3tools/web --filter @t3tools/scripts typecheck: 5/5 successful.
  • vp run check:changed: format, lint, knip 1/2 and 2/2, typecheck (40 packages), and tests (3 files, 68 tests) all pass.

Packaging proof

build-desktop-artifact.ts refuses to package Linux on a non-Linux host (LinuxBrowserSecretHostError: the libsecret helper must be built on Linux), so this was done on macOS against the published nightly instead. I extracted data.tar.xz from S2Code-0.1.5-nightly.20261101.79-amd64.deb with bsdtar (tar -xpf, which preserves modes) and ran the hook's default export with appOutDir set to the extracted opt/S2Code (Nightly) directory.

  • Before: find "$OPT" -perm -o+w ! -type l listed 14 files (chrome-sandbox, s2code-nightly, chrome_crashpad_handler, libffmpeg.so, libvk_swiftshader.so, libvulkan.so.1, resources.pak, chrome_100_percent.pak, chrome_200_percent.pak, locales/en-US.pak, LICENSES.chromium.html, snapshot_blob.bin, v8_context_snapshot.bin, vk_swiftshader_icd.json). These match the archive's own tar -tvf listing.
  • After: the same find prints nothing (0 world-writable and 0 group-writable entries). The executables are now -rwxr-xr-x and the data files -rw-r--r--.

Ported with Claude Opus via Cursor in T3 Code.

Electron's Linux zips ship world-writable files (0777 binaries, 0666 paks),
and electron-builder copies those modes verbatim into the .deb and AppImage.
Register a Linux afterPack hook that walks the packed app with lstat, skips
symlinks, and clears group/other write bits.

Ports pingdotgg#28351 (bfa1ff6) without its compile-cache step.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shuv1337
shuv1337 merged commit 2de863c into main Nov 3, 2026
4 checks passed
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