Skip to content

fix(cli): replace the legacy bare orca AppImage wrapper - #24389

Open
innocarpe wants to merge 2 commits into
stablyai:mainfrom
innocarpe:fix-18985-bare-orca-legacy-wrapper
Open

innocarpe wants to merge 2 commits into
stablyai:mainfrom
innocarpe:fix-18985-bare-orca-legacy-wrapper

Conversation

@innocarpe

Copy link
Copy Markdown
Contributor

Description

On Linux, ~/.local/bin/orca can still be the AppImage wrapper Orca wrote before the command was renamed to orca-ide. That wrapper execs the AppImage. AppRun then injects --no-sandbox into Electron's node mode, so every bare orca command fails with bad option: --no-sandbox while orca-ide works. Serve treated that wrapper as someone else's script and left it in place.

Focused fix

In scope: when installing the bare-orca dispatcher, replace a file only if it is the current dispatcher or an exact legacy AppImage wrapper.

Out of scope: AppRun's sandbox flag, the orca-ide installer, and any script the user edited.

Preserves

A user's own orca script, including a legacy wrapper they have edited, is still left untouched. The replacement execs the extracted orca-ide launcher and does not pass --no-sandbox.

Evidence

node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts --cache false src/main/cli/linux-bare-orca-dispatcher.test.ts — 13 passed.

The new cases cover replacing the exact legacy wrapper and keeping a hand-edited one. A restricted-userns Linux host was not rechecked.

User-regression-tradeoffs

An unedited wrapper Orca previously installed at ~/.local/bin/orca is overwritten on the next serve start. A script that is not that exact wrapper stays as it is.

Fixes #18985

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issue was established.

Summary

The PR allows Linux serve to replace an exact legacy bare-orca AppImage wrapper with a dispatcher that launches orca-ide, while leaving other scripts untouched.

  • The follow-up change compares the wrapper’s saved AppImage path with the running AppImage path before replacing it.
  • Tests cover a changed path and a matching path.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Existing bare orca file] --> B{Marked dispatcher?}
  B -->|Yes| E[Replace]
  B -->|No| C{Exact legacy wrapper?}
  C -->|No| F[Preserve]
  C -->|Yes| D{Trusted path absent or paths match?}
  D -->|Yes| E
  D -->|No| F
Loading

Reviews (3) · Last reviewed commit: "fix(cli): keep a legacy wrapper whose Ap..." · Reviewed by Greptile

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
Walkthrough

The Linux bare-orca dispatcher now resolves a trusted AppImage path. It can replace an app-owned dispatcher or a recognized legacy AppImage wrapper that matches the trusted path. It preserves unrecognized wrappers, wrappers with a different target, and other foreign files. Tests cover replacement and preservation cases.

Priority: Normal

Severity of issue fixed: Medium

Merge Risk: Low · previous review 84b88

A non-AppImage launch of the packaged app could overwrite an unedited legacy wrapper that points to a different AppImage. The impact is narrow and recoverable. Tighten the null-path check or document the behavior before merging.

Security architecture risk: Low · up to 84b88

Replacement remains limited to the user's Linux CLI command and does not execute the legacy wrapper's embedded path. However, when AppImage identity is unavailable, a wrapper edited only to target another AppImage can still be overwritten.

Retained concerns

  • Low · architecture · inferred: Without trusted AppImage identity, complete template matching becomes sufficient ownership proof. A user who changes only the wrapper's APPIMAGE target still leaves a matching template, so the new gate can overwrite that retargeted command. Trusted-path matching prevents this when identity exists, but not in the production-reachable null-identity case. This is a limited command-ownership gap, not an established code-execution or privilege-escalation vulnerability.
Security review details

Security Blast Radius

  • inferred — The demonstrated direct scope is the invoking account's bare-orca command and downstream invocations resolving it through PATH. An actor already able to modify that account's command directory can influence its contents, but the new recognizer does not grant that actor additional execution privileges or propagate the embedded legacy target into the replacement.

Trust Boundaries and Controls

  • observed — Recognition checks complete content rather than merely trusting an APPIMAGE assignment. Trusted-path equality protects mismatched targets when identity is available, and displaced-file revalidation limits replacement races. The null-identity branch removes the target constraint, leaving template shape as the ownership decision.

Resilience and Maintainability Implications

  • inferred — Termination after displacement but before publication or restoration can leave the public command absent and its original under a preserved filename. This recovery limitation predates the PR, but newly admitted legacy wrappers now enter that same transition. The inspected flow has no durable restart-recovery step; no evidence establishes security exploitation of this window.

Hardening Proposals

  • proposed — For identity-unavailable migration, consider requiring independent ownership evidence or explicit user authorization before replacing a matching wrapper. This would strengthen preservation of path-retargeted commands at the cost of less automatic migration.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem, scope, behavior, trade-offs, linked issue, and test evidence. However, it does not follow the repository template and omits required sections such as ELI5, What C… Rewrite the description using all required template headings. Include the missing information, mark Visual Proof as N/A with a reason if applicable, complete the testing and checklist items, and state the AI Disclosure and Agent skill upstr…
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: replacing the legacy bare Orca AppImage wrapper.
Linked Issues check ✅ Passed The implementation addresses issue #18985. installLinuxBareOrcaDispatcher replaces an owned dispatcher or an exact legacy AppImage wrapper. The replacement executes the bundled or extracted `orca-id…
Out of Scope Changes check ✅ Passed The changes stay within issue #18985. Production changes only expand bare-orca dispatcher ownership checks for the legacy AppImage wrapper and preserve user-owned files. The added tests verify this …
Full details: Description check

Explanation

The description explains the problem, scope, behavior, trade-offs, linked issue, and test evidence. However, it does not follow the repository template and omits required sections such as ELI5, What Changed, Why, Visual Proof or N/A, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist.

Resolution

Rewrite the description using all required template headings. Include the missing information, mark Visual Proof as N/A with a reason if applicable, complete the testing and checklist items, and state the AI Disclosure and Agent skill upstream boundary status.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.

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

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — reviewed the full diff at f2b35055 (2 files): the bare-orca dispatcher's replacement predicate and its new regression tests.

  • Serve now reclaims the legacy wrapper — the pre-check and the displaced-file branch of publishDispatcher call a new isReplaceableDispatcher, which accepts our own dispatcher (line-2 marker) or an exact legacy AppImage wrapper, so an unedited ~/.local/bin/orca no longer survives serve and no longer routes through AppRun's --no-sandbox.
  • User scripts stay safe — any other file, including a hand-edited legacy wrapper, still returns skipped-foreign.
  • Tests pin both directions — replaces the legacy AppImage wrapper fails pre-fix (pre-fix returned skipped-foreign), and leaves a hand-edited legacy wrapper in place guards the new clobber risk.

I verified buildLegacyAppImageCliWrapper is byte-faithful to the wrapper first shipped in 3647b53e00 (identical APPIMAGE_CLI_SCRIPT, shell body, and quoteShell), so the exact-match criterion should match real users' on-disk wrappers. Ran the two touched test files: 15 passed.

ℹ️ Serve replacement is broader than the installer's legacy-command removal

installLinuxBareOrcaDispatcher replaces any exact legacy wrapper regardless of which AppImage it names — the new test even asserts replacement when the caller passes appImagePath: null and the wrapper points at a different file. The desktop installer deliberately scopes the same cleanup to the current AppImage (cli-command-installation.ts:215, resolve(resolvedTarget) === resolve(this.appImagePath)), and cli-installer.test.ts encodes that ("only when it names the current AppImage"). This is likely intentional — the serve dispatcher is single-slot and always points at the running install, and the wrapper is byte-identical Orca output — but it is worth confirming that overwriting a wrapper naming a different Orca install is the desired behavior rather than matching the installer's stricter scope.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@innocarpe
innocarpe force-pushed the fix-18985-bare-orca-legacy-wrapper branch from f2b3505 to 84b88b0 Compare October 1, 2026 15:03
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (84b88b03ca)

A packaged serve now reclaims a legacy wrapper only when its AppImage path is the one this process is running. A wrapper whose APPIMAGE= line was edited to somewhere else stays in place.

@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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 3b4e6ba0-4f46-43f4-a0ac-3032f5b4d043

📥 Commits

Reviewing files that changed from the base of the PR and between f2b35055c12d6c08b49b56c19d9794a14b3812d2 and 84b88b03caa19b4b6037bc504cc68d50504b7261.

📒 Files selected for processing (2)
  • src/main/cli/linux-bare-orca-dispatcher.test.ts
  • src/main/cli/linux-bare-orca-dispatcher.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

if (!target) {
return false
}
return trustedAppImagePath === null || target === trustedAppImagePath

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
# Check callers that pass appImagePath: null or run without AppImage identity.
rg -nP --type=ts -C3 'installLinuxBareOrcaDispatcher\s*\(' -g '!**/*.test.ts'

Repository: stablyai/orca

Length of output: 1660


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- dispatcher source ---'
cat -n src/main/cli/linux-bare-orca-dispatcher.ts | sed -n '1,290p'
printf '%s\n' '--- production caller context ---'
cat -n src/main/startup/main-process-runtime-launch.ts | sed -n '1,230p'
printf '%s\n' '--- affected tests ---'
cat -n src/main/cli/linux-bare-orca-dispatcher.test.ts | sed -n '210,285p'
printf '%s\n' '--- relevant PR diff ---'
git diff --unified=30 dc08ffeba9603c5b48b9b100d0a7f89b021e6d1b 84b88b03caa19b4b6037bc504cc68d50504b7261 -- src/main/cli/linux-bare-orca-dispatcher.ts src/main/startup/main-process-runtime-launch.ts

Repository: stablyai/orca

Length of output: 38118


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- runtime identity files ---'
fd -t f 'appimage-runtime-identity' src
printf '%s\n' '--- runtime identity implementation ---'
identity=$(fd -t f 'appimage-runtime-identity' src | head -n 1)
cat -n "$identity" | sed -n '1,260p'
printf '%s\n' '--- runtime identity references ---'
rg -n -C4 'resolveAppImageRuntimeIdentity|hasAppImagePathEnvironment|appImagePath: null' src --type ts

Repository: stablyai/orca

Length of output: 43654


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- runtime identity implementation ---'
cat -n src/main/appimage-runtime-identity.ts | sed -n '1,230p'
printf '%s\n' '--- packaged AppImage contract ---'
cat -n src/main/cli/cli-install-location.ts | sed -n '80,135p'

Repository: stablyai/orca

Length of output: 9713


Require a trusted AppImage path before replacing a legacy wrapper.

isReplaceableDispatcher accepts any exact legacy wrapper when trustedAppImagePath is null. The production caller passes no appImagePath. Outside an AppImage, runtime identity is null, and the bundled launcher fallback still runs. A wrapper that points to another AppImage can then be replaced.

🐛 Suggested fix
-    return trustedAppImagePath === null || target === trustedAppImagePath
+    return trustedAppImagePath !== null && target === trustedAppImagePath

Update the test at src/main/cli/linux-bare-orca-dispatcher.test.ts:246-268 to pass a trusted AppImage path, or document the null behavior as intentional.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return trustedAppImagePath === null || target === trustedAppImagePath
return trustedAppImagePath !== null && target === trustedAppImagePath

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — re-reviewed the delta since the prior pullfrog review at f2b35055 (2 files, 2 commits): the legacy-wrapper replacement predicate was narrowed to the AppImage this process is running, plus two new regression tests.

  • Replacement scoped to the trusted AppImage — isReplaceableDispatcher now takes trustedAppImagePath and reclaims an exact legacy wrapper only when its APPIMAGE= target equals the running AppImage (or no identity is available), so a wrapper edited to name a different AppImage is left as skipped-foreign.
  • New trustedLegacyAppImagePath — prefers a caller-supplied appImagePath (including null) and otherwise derives it from resolveAppImageRuntimeIdentity; the value is threaded through both the pre-check and the displaced-file branch of publishDispatcher.
  • Tests pin the new boundary — a wrapper whose APPIMAGE= path was edited stays in place, while one naming the running AppImage is still replaced.

This addresses greptile's original P1 (a hand-edited APPIMAGE path being overwritten). I ran src/main/cli/linux-bare-orca-dispatcher.test.ts and src/main/cli/legacy-appimage-cli-wrapper.test.ts: 17 passed. The null trusted-path wildcard applies only to byte-exact Orca-written wrappers and matches the documented overwrite behavior for a non-AppImage packaged serve.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@innocarpe
innocarpe force-pushed the fix-18985-bare-orca-legacy-wrapper branch from 84b88b0 to ea3d921 Compare October 22, 2026 01:42
innocarpe and others added 2 commits October 29, 2026 19:19
The pre-rename wrapper execs the AppImage, and AppRun injects --no-sandbox
into Electron's node mode. Serve now overwrites that exact wrapper with the
extracted launcher and still leaves a user's own orca script alone.

Fixes stablyai#18985
A packaged serve was rebuilding the expected wrapper from the APPIMAGE= line inside the file, so a path the user changed still looked like one Orca wrote and was replaced.

Co-authored-by: Cursor <cursoragent@cursor.com>
@innocarpe
innocarpe force-pushed the fix-18985-bare-orca-legacy-wrapper branch from ea3d921 to 8b381c7 Compare October 29, 2026 10:20

This branch has not been deployed

No deployments
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]: v1.4.194 bare orca still fails with "bad option: --no-sandbox" after #17964/#18100; orca-ide works

1 participant