Skip to content

Resolve spawned system executables by absolute path (#509) - #596

Open
rahul188 wants to merge 3 commits into
wonderwhy-er:mainfrom
rahul188:fix/spawn-absolute-paths
Open

Resolve spawned system executables by absolute path (#509)#596
rahul188 wants to merge 3 commits into
wonderwhy-er:mainfrom
rahul188:fix/spawn-absolute-paths

Conversation

@rahul188

@rahul188 rahul188 commented Jul 18, 2026

Copy link
Copy Markdown

Summary

Several spawn/exec calls resolved their executable through PATH, which is inherited from the parent process and can be reordered by shell startup files, direnv, or any tool that prepends a shims directory. This hardens the two spots called out in #509 to use fixed, unambiguous binaries.

Changes

src/remote-device/remote-channel.ts — the blocking offline-update script was launched with spawnSync('node', …). The code already logs process.execPath on the line above ("Using node executable:"), so this switches the spawn to process.execPath. That's the exact Node binary already running the server, so the child runs on the same runtime and can't be shadowed by a different node on PATH.

setup-claude-server.js — the macOS/Linux restart path shelled out to bare killall, pkill, and open. These now resolve through a small resolveSystemBinary(candidates, fallback) helper that returns the first absolute path that exists on disk (/usr/bin/killall, /usr/bin/pkill or /bin/pkill, /usr/bin/open) and falls back to the bare name if the tool is installed elsewhere, so uncommon distro layouts keep working. Paths are quoted to tolerate spaces.

Left as-is intentionally:

  • Windows taskkill and the cmd.exe wrapper — resolved from the protected System32 directory; out of scope for this issue.
  • The Linux claude launch — that's the user's own CLI on PATH, not a fixed system utility, so it has no stable absolute location to prefer.

Testing

  • npm run build (tsc) passes; the compiled dist/remote-device/remote-channel.js shows spawnSync(process.execPath, …).
  • node --check setup-claude-server.js passes.
  • Verified resolveSystemBinary returns the absolute path when present and falls back to the bare name otherwise.

Fixes #509

Summary by CodeRabbit

  • Security & Reliability
    • Improved how system utilities are launched by using trusted, absolute executable paths on macOS and Linux.
    • Updated the Claude restart/shutdown flow to invoke resolved binaries (including killall/pkill and macOS open) instead of relying on PATH.
    • Ensured offline update subprocesses run using the same Node.js runtime as the server, reducing runtime/path inconsistencies.

Several spawn/exec calls used bare executable names and relied on PATH to
resolve them at runtime, so a foreign entry prepended to PATH (shell rc
files, direnv, tool shims) could cause a different binary to run.

- remote-channel.ts: spawn the blocking update script with
  process.execPath — the exact Node binary already running the server —
  instead of the bare name 'node'. The path was already being logged one
  line above, so there is no ambiguity about which runtime to use.
- setup-claude-server.js: resolve killall/pkill/open to their standard
  absolute locations via a small resolveSystemBinary() helper that prefers
  an on-disk absolute path and falls back to the bare name when the tool
  lives elsewhere, so distros that install it in a different directory keep
  working.

Fixes wonderwhy-er#509
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79416338-52ff-4536-bcd0-0f6bb1ebb226

📥 Commits

Reviewing files that changed from the base of the PR and between 8967dce and 5359908.

📒 Files selected for processing (1)
  • uninstall-claude-server.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • uninstall-claude-server.js

📝 Walkthrough

Walkthrough

Executable invocations now avoid PATH-based resolution. Restart and uninstall commands resolve system utilities from candidate absolute paths, while remote-device offline updates use the currently running Node binary.

Changes

Executable path hardening

Layer / File(s) Summary
Current Node runtime execution
src/remote-device/remote-channel.ts
Offline-update subprocesses use process.execPath instead of the bare node command.
Setup restart binary resolution
setup-claude-server.js
Claude restart operations resolve killall, pkill, and macOS open before invoking them.
Uninstall termination binary resolution
uninstall-claude-server.js
Claude termination and macOS restart operations resolve killall, pkill, and open before invoking them.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: wonderwhy-er

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: resolving spawned executables by absolute path.
Linked Issues check ✅ Passed The changes match #509 by using process.execPath in remote-channel.ts and absolute-path resolution for killall, pkill, and open in the Claude setup flow.
Out of Scope Changes check ✅ Passed The uninstall flow changes are consistent with the same PATH-shadowing fix and do not introduce unrelated behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
setup-claude-server.js (1)

536-548: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Apply the same binary resolution in uninstall-claude-server.js (lines 493-519). killall, pkill, and open still run by bare name here, so PATH shadowing protection is inconsistent with the setup flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@setup-claude-server.js` around lines 536 - 548, Apply the setup flow’s
resolveSystemBinary behavior to uninstall-claude-server.js, adding or reusing
the corresponding resolver and resolving killall, pkill, and open before
execution. Ensure each command uses the resolved absolute-path candidate with
the existing bare-name fallback, matching the protection already implemented in
resolveSystemBinary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@setup-claude-server.js`:
- Around line 536-548: Apply the setup flow’s resolveSystemBinary behavior to
uninstall-claude-server.js, adding or reusing the corresponding resolver and
resolving killall, pkill, and open before execution. Ensure each command uses
the resolved absolute-path candidate with the existing bare-name fallback,
matching the protection already implemented in resolveSystemBinary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fad91fc-408c-4b21-bb87-71cadee384bb

📥 Commits

Reviewing files that changed from the base of the PR and between 78f8f4b and 3ac4742.

📒 Files selected for processing (2)
  • setup-claude-server.js
  • src/remote-device/remote-channel.ts

The uninstall flow still spawned these by bare name, so the PATH
shadowing protection only covered setup.
@rahul188

Copy link
Copy Markdown
Author

Good catch — applied. uninstall-claude-server.js now uses the same
resolveSystemBinary() approach for killall and pkill, so the PATH-shadowing
protection covers both flows instead of just setup.

One small correction to the finding: open isn't used in the uninstall script (it
only kills the process, it doesn't restart Claude), so there was nothing to resolve
there. taskkill on the win32 branch is left as-is, matching setup.

Both scripts pass node --check.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
uninstall-claude-server.js (1)

531-533: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Resolve open using absolute paths to prevent PATH shadowing.

As per the PR objectives and comments summary, the requirement is to use absolute paths for macOS/Linux open. However, it was omitted here under the incorrect assumption that the uninstall script does not restart Claude. The restartClaude function clearly attempts to restart the application, so open must be protected against PATH shadowing just like killall and pkill.

🔒️ Proposed fix
             } else if (platform === "darwin") {
-                await execAsync(`open -a "Claude"`);
+                const openBin = resolveSystemBinary(['/usr/bin/open'], 'open');
+                await execAsync(`"${openBin}" -a "Claude"`);
                 updateUninstallStep(startStep, 'completed');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@uninstall-claude-server.js` around lines 531 - 533, Update the macOS branch
of restartClaude to invoke open via its required absolute path instead of
resolving it through PATH, while preserving the existing Claude application
launch and uninstall-step completion behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@uninstall-claude-server.js`:
- Around line 531-533: Update the macOS branch of restartClaude to invoke open
via its required absolute path instead of resolving it through PATH, while
preserving the existing Claude application launch and uninstall-step completion
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16bfb041-abf3-408d-8d07-e9454e270c88

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac4742 and 8967dce.

📒 Files selected for processing (1)
  • uninstall-claude-server.js

@rahul188

Copy link
Copy Markdown
Author

Right — restartClaude in the uninstall script does relaunch the app, so the macOS open call needed the same treatment. Applied resolveSystemBinary(['/usr/bin/open'], 'open') there, matching the setup script.

@rahul188

Copy link
Copy Markdown
Author

Hi 👋 Gentle nudge on this PR — it's mergeable and the required checks are passing (one non-required/optional check shows as unstable, which shouldn't block merge). Would appreciate a review whenever you have a moment, and I'm happy to address anything needed. Thanks for your time and for maintaining this project! 🙏

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.

Resolve spawned executables by absolute path

1 participant