Skip to content

fix(shellEnv): scrub AppImage env from shell probe#1680

Open
raulpineda wants to merge 2 commits intogeneralaction:mainfrom
raulpineda:emdash/zsh-ilc-appimage-9z2
Open

fix(shellEnv): scrub AppImage env from shell probe#1680
raulpineda wants to merge 2 commits intogeneralaction:mainfrom
raulpineda:emdash/zsh-ilc-appimage-9z2

Conversation

@raulpineda
Copy link
Copy Markdown

@raulpineda raulpineda commented Apr 6, 2026

Summary

  • src/main/utils/shellEnv.ts spawned $SHELL -ilc '<probe>' with process.env passed wholesale, leaking APPIMAGE / APPDIR / /tmp/.mount_* PATH entries into the child shell on Linux AppImage installs. Any shell-init code that exec's a binary by name through PATH (starship version modules, mise activation hooks, oh-my-zsh completion plugins) could then re-enter the AppImage binary and fork-bomb Emdash. See [bug]: AppImage launch fork-bombs on Linux #1679 for the failure mode.
  • Route the probe env through buildExternalToolEnv() from src/main/utils/childProcessEnv.ts — the same helper already used by ptyManager.ts (introduced by Python scripts fail in Emdash worktree terminal when using virtual environments (PYTHONHOME conflict) #485) and appIpc.ts (Fix AppImage env leakage in Open In launches #872). shellEnv.ts was the remaining process.env-wholesale call site.
  • Adds a regression test that mocks execSync and asserts the spawned env has APPIMAGE / APPDIR / ARGV0 / OWD stripped and no /tmp/.mount_* entries left in PATH / LD_LIBRARY_PATH.

Fixes

Fixes #1679

Test plan

  • pnpm run format — clean
  • pnpm run lint — 0 errors
  • pnpm run type-check — clean
  • pnpm exec vitest run — 878 passing, 1 skipped, 82 files
  • Regression test verified by stash-and-rerun: fails without the fix (expected '/home/user/emdash.AppImage' to be undefined), passes with it
  • Manual end-to-end verification against a real AppImage build was not performed; a standalone sandboxed reproducer of the mechanism (systemd-run --scope + zsh -ilc + PATH-based version probe) confirmed the upstream cause and is available on request

Summary by CodeRabbit

  • Bug Fixes

    • Prevented AppImage-related environment variables and mount paths from leaking into probed shell environments during initialization.
  • Tests

    • Added a regression test that verifies AppImage env keys and mount entries are stripped when detecting the shell environment.

Route the env passed to `$SHELL -ilc` through `buildExternalToolEnv()`
so AppImage packaging vars (APPIMAGE, APPDIR, ARGV0, OWD, etc.) and
`/tmp/.mount_*` entries in PATH / LD_LIBRARY_PATH / XDG_DATA_DIRS are
stripped before the probed login shell inherits them.

Without this, a Linux AppImage install leaks those vars into the login
shell startup. If the user's `.zshrc` / `.bash_profile` exec's a binary
by name through PATH (starship version modules, mise activation hooks,
oh-my-zsh completion plugins all do some flavor of this), the exec can
resolve back into the AppImage mount and re-enter Emdash's main process,
which runs this probe again and spawns another shell — a fork bomb.

The same remediation helper is already used by ptyManager.ts (rationale
comment there introduced by generalaction#485) and appIpc.ts (generalaction#872). shellEnv.ts was
the remaining call site where process.env was passed wholesale to a
child shell.

Adds a regression test that mocks execSync and asserts the spawned env
has AppImage keys and `/tmp/.mount_*` PATH entries scrubbed. Verified by
stash-and-rerun: the test fails without the fix and passes with it.

Fixes generalaction#1679

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 6, 2026

@raulpineda is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7ddc2af8-bae9-4f5d-8438-5fba1a4dc924

📥 Commits

Reviewing files that changed from the base of the PR and between daa81e8 and daa4d2a.

📒 Files selected for processing (1)
  • src/main/utils/__tests__/shellEnv.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/utils/tests/shellEnv.test.ts

📝 Walkthrough

Walkthrough

Routes shell-probing child processes through buildExternalToolEnv(process.env) so AppImage-specific environment variables and mount paths are removed before executing probed login shells; adds a regression test that verifies AppImage keys and mount entries are scrubbed.

Changes

Cohort / File(s) Summary
Shell probing env sanitization
src/main/utils/shellEnv.ts
Import buildExternalToolEnv and use ...buildExternalToolEnv(process.env) for execSync in login-shell probes (getShellEnvVar, getShellLocaleVars) so AppImage env keys and mount paths are stripped before probing.
Regression test
src/main/utils/__tests__/shellEnv.test.ts
Add test should strip AppImage env leakage from the probed shell that injects AppImage vars and mount paths, mocks probe output, calls initializeShellEnvironment(), and asserts the probe was executed with the sanitized env (AppImage keys removed and mount paths absent).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant MainProcess as Main\nProcess
  participant EnvBuilder as buildExternalToolEnv
  participant ShellProbe as Login\nShell (execSync)
  participant ChildShell as Probed\nShell

  User->>MainProcess: trigger shell env initialization
  MainProcess->>EnvBuilder: buildExternalToolEnv(process.env)
  EnvBuilder-->>MainProcess: sanitizedEnv (no APPIMAGE, no mount paths)
  MainProcess->>ShellProbe: execSync("-ilc ...", { env: sanitizedEnv })
  ShellProbe->>ChildShell: start with sanitized environment
  ChildShell-->>ShellProbe: produce probe output (env/list)
  ShellProbe-->>MainProcess: probe output
  MainProcess->>MainProcess: parse and set application shell env
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

🐰
Soft hops through code where mount paths stray,
I scrub AppImage footprints clean away.
Shells wake up tidy, no ghostly trail—
A rabbit's fix: small, swift, and hale. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: scrubbing AppImage environment variables from shell probe execution.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/utils/__tests__/shellEnv.test.ts (1)

349-351: Add XDG_DATA_DIRS assertion to fully lock the sanitization contract.

buildExternalToolEnv() also strips AppImage mount entries from XDG_DATA_DIRS; this regression test currently checks only PATH and LD_LIBRARY_PATH.

🧪 Suggested test extension
       process.env.PATH = `/usr/local/bin:${appDir}/usr/bin:/usr/bin`;
       process.env.LD_LIBRARY_PATH = `${appDir}/usr/lib:/usr/local/cuda/lib64`;
+      process.env.XDG_DATA_DIRS = `/usr/local/share:${appDir}/usr/share:/usr/share`;
@@
         expect(env.LD_LIBRARY_PATH).toBeDefined();
         expect(env.LD_LIBRARY_PATH).not.toContain(appDir);
         expect(env.LD_LIBRARY_PATH).not.toContain('/tmp/.mount_');
+        expect(env.XDG_DATA_DIRS).toBeDefined();
+        expect(env.XDG_DATA_DIRS).not.toContain(appDir);
+        expect(env.XDG_DATA_DIRS).not.toContain('/tmp/.mount_');
       }

Also applies to: 378-384

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/utils/__tests__/shellEnv.test.ts` around lines 349 - 351, The test
currently only asserts PATH and LD_LIBRARY_PATH sanitization; extend it to also
assert that buildExternalToolEnv() removes AppImage mount entries from
XDG_DATA_DIRS by setting process.env.XDG_DATA_DIRS to include an AppImage-style
path and verifying the returned env.XDG_DATA_DIRS no longer contains that mount
entry; update both occurrences of the test (around the blocks that set
process.env.PATH / LD_LIBRARY_PATH at the given ranges) so they include the same
XDG_DATA_DIRS setup and corresponding assertion referencing
buildExternalToolEnv.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/utils/__tests__/shellEnv.test.ts`:
- Around line 369-373: The test currently asserts options.env for every
mockedExecSync.mock.calls entry which fails when detectSshAuthSock() triggers a
launchctl getenv call without an env option; update the loop in shellEnv.test.ts
to first filter calls to only those executing the shell probe (e.g., where
typeof call[0] === 'string' and the command string includes the shell-probe
probe command or a known probe substring), then perform the options/env
assertions on those filtered calls (reference mockedExecSync.mock.calls and
detectSshAuthSock to locate the relevant test code).

---

Nitpick comments:
In `@src/main/utils/__tests__/shellEnv.test.ts`:
- Around line 349-351: The test currently only asserts PATH and LD_LIBRARY_PATH
sanitization; extend it to also assert that buildExternalToolEnv() removes
AppImage mount entries from XDG_DATA_DIRS by setting process.env.XDG_DATA_DIRS
to include an AppImage-style path and verifying the returned env.XDG_DATA_DIRS
no longer contains that mount entry; update both occurrences of the test (around
the blocks that set process.env.PATH / LD_LIBRARY_PATH at the given ranges) so
they include the same XDG_DATA_DIRS setup and corresponding assertion
referencing buildExternalToolEnv.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d827b82a-2a73-4024-bac2-a42dde156e2d

📥 Commits

Reviewing files that changed from the base of the PR and between c797ed5 and daa81e8.

📒 Files selected for processing (2)
  • src/main/utils/__tests__/shellEnv.test.ts
  • src/main/utils/shellEnv.ts

The AppImage-env regression test iterated over every mockedExecSync
call and asserted `options.env` was defined. That breaks on macOS,
where detectSshAuthSock() calls `execSync('launchctl getenv ...')`
with no `env` option — the loop would trip the `expect(env).toBeDefined()`
assertion on the launchctl call before it ever reached the shell probe.

Filter the call list to entries whose command string contains `-ilc`
(the flag both getShellEnvVar and getShellLocaleVars use) so the
assertions only run against the actual probe calls. Behavior on Linux
is unchanged (launchctl branch is skipped there anyway).

Verified by reverting shellEnv.ts to HEAD~1 and re-running: the test
still fails on `expected '/home/user/emdash.AppImage' to be undefined`,
confirming the filter didn't weaken the regression guard.

Addresses CodeRabbit review on generalaction#1680.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
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.

[bug]: AppImage launch fork-bombs on Linux

1 participant