mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
f352b6b08d9028ba8b577d6ac8a8247c2a6c9c92
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cff202c16a |
fix(windows): drop EDR-flagged -ExecutionPolicy Bypass from encoded PowerShell (#17880)
* fix(windows): drop EDR-flagged -ExecutionPolicy Bypass from encoded PowerShell
MDE flags `-ExecutionPolicy Bypass` paired with base64 `-EncodedCommand` as a
behavioural signal. Measured on Windows 11: neither `-Command` nor
`-EncodedCommand` is execution-policy gated (both run under an explicit
`-ExecutionPolicy Restricted` and `AllSigned`; only `-File` fails), so the
switch was a pure no-op on every one of these command lines.
Removes the switch from all four sites that spelled it, and de-encodes the one
site whose payload never passes through a re-parsing shell:
- ssh-remote-powershell: one chokepoint for ~40 remote-Windows call sites.
Base64 kept — the remote sshd DefaultShell re-parses this string.
- setup-agent-sequencing / windows-cmd-runner-delayed-launch: base64 kept —
these strings are typed into a terminal pane.
- windows-interactive-login-spawn: base64 kept — `cmd.exe /c start` re-parses,
and the cmd-safe-token guard rejects the `&` and `"` in the raw relay script.
- windows-mobile-firewall local runner: `-EncodedCommand` -> `-Command`, since
execFile reaches CreateProcess with no shell in between.
The setup startup gate keeps execution-policy relief in-payload (process scope),
because it evals a user-authored startup command that may invoke a `.ps1`, and a
`.ps1` IS gated. Caught by the real-process suite; mirrors the agent-hooks
launcher's trade.
The elevated firewall child deliberately stays encoded: `Start-Process
-ArgumentList` joins its array into one ShellExecuteEx string without quoting
and PowerShell re-splits on whitespace, measured to collapse `C:\My App\...`
to `C:\My App\...` — a firewall rule for the wrong program.
* test(ssh): enforce the no-script-file invariant remote payloads rely on
Dropping `-ExecutionPolicy Bypass` from `powerShellCommand` is a no-op only
while no remote payload loads a PowerShell script file — execution policy has
never gated anything else. That invariant held by inspection and was guarded by
nothing, so a future payload that dot-sourced, used `-File`, or imported a
`.psm1` would break only on a remote host with a Restricted/AllSigned
LocalMachine policy and no GPO: a failure on someone else's machine.
States the invariant at the wrapper, and adds a ratchet that scans every module
importing it for `.ps1`/`.psm1`, `Import-Module`, `-File`, and dot-sourcing.
The scan discovers importers itself (13 today) so new ones are covered, and
asserts it found some, so an emptied list cannot pass vacuously.
Mutation-checked: injecting each construct into a real importer fails the
matching case and names the file. The first dot-source pattern passed a
`;`-prefixed sample but missed `powerShellCommand(". '$x'")` — the likelier
shape — so the pattern now accepts a string-literal start and the self-test
samples carry their surrounding quotes.
* test(ssh): close two blind spots in the remote-payload ratchet
Both found by independent mutation testing of the ratchet itself, and both let
a real violation pass while the guard reported green.
`-File` was matched case-sensitively, so `-file $scriptVar` slipped through —
PowerShell switches are case-insensitive, and with a variable path the `.ps1`
pattern does not cover for it, so that shape escaped both nets. The naive fix
is wrong: bare /-File\b/i matches `--credential-file`, `--log-file` and
`--body-file`, which occur in three of these importers. Anchoring to a token
boundary catches the lowercase, odd-spacing and argv-element forms with zero
offenders across all 14.
Comment stripping paired a `/*` appearing inside a string (a glob such as
'src/*.ts') with any later comment close and deleted everything between, hiding
violations in the gap. Anchoring the block strip to line start, as the `//`
strip already was, fixes it — verified by injecting an `Import-Module` after a
glob string: the unanchored form misses it, the anchored form catches it.
Extends the same case-insensitivity to `.ps1`/`.psm1` and `Import-Module`,
which had the identical flaw (`import-module`, `DEPLOY.PS1` are legitimate
spellings); measured to add no false positive.
Each construct now carries the fixtures it must catch AND the near-misses it
must not, so a future tightening cannot quietly trade one for the other — the
negative fixtures are what would have caught the naive `-File` fix. Non-vacuity
bound tightened to >10 against 14 importers.
* docs(ssh): state what the remote-payload ratchet cannot see
The scan matches source text, so a script file reached only through a variable
(`& $scriptPath`) never appears in source and no pattern can catch it. The
ratchet narrows the hole; the invariant note on `powerShellCommand` covers the
remainder.
Recorded because a guard that reads as complete coverage when it is not is
worse than one that states its edge: the next author trusts it further than it
deserves, and should learn this limit from the test rather than an incident.
* test(ssh): scan remote payloads with the shared source walk
The ratchet had its own tree walk and comment stripper. The walk skipped
neither node_modules/dist/.git nor dot-directories and excluded tests by
`.test.ts` alone, so its importer count -- the guard's own goalpost -- could
be wrong about what it scanned. The stripper was anchored to line start to
dodge a `/*` inside a glob string, which silently skipped trailing comments;
`stripComments` tracks quote state and handles both.
Importer set re-derived against the shared walk: 15, floor unchanged at 10.
* fix(setup): report a failed execution-policy relief instead of swallowing it
The in-payload Set-ExecutionPolicy carried -ErrorAction SilentlyContinue and
an empty catch, so any failure vanished. A Windows PowerShell 5.1 install with
duplicate extended type data fails every cmdlet in Microsoft.PowerShell.Security
-- autoload, not policy -- and the user then saw only their own .ps1 being
refused, with no trace that the relief had been attempted or why.
-ErrorAction Stop is what routes a non-terminating failure into the catch at
all; the catch reports the FullyQualifiedErrorId to stderr and deliberately
does not rethrow, so a broken policy cmdlet cannot take down the startup this
gate exists to run. Success path is unchanged and stays stderr-clean.
Verified by execution on a clean child environment: success -> policy=Bypass,
stderr empty; shadowed failing cmdlet -> diagnostic on stderr and the gate
still continues; the old empty catch -> silent.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
|
||
|
|
ed7849eb7b |
fix(worktrees): stop silently switching existing Windows setup scripts to Git Bash (#12406)
* fix(worktrees): stop silently switching existing Windows setup scripts to Git Bash #6967 derived the Windows setup-runner shell from `terminalWindowsShell`. On upgrade, any Windows user whose terminal preference resolved to Git Bash had their existing `orca.yaml` setup script (and issue command) handed to bash instead of cmd.exe. Scripts authored against the cmd runner — `copy`, `xcopy`, `set VAR=value`, `if errorlevel 1`, `%VAR%`, backslash paths — broke with no migration and no warning, and the failure looked like Orca broke the project. The conflation is also wrong in the steady state: a terminal preference is per-user, so two people on the same repo got different interpreters for the same orca.yaml and no project could write a setup script that worked for all of its Windows contributors. The interpreter is now a property of the script, declared the standard way: a leading `#!` line. Native Windows keeps the historical `.cmd` runner unless the script declares a POSIX shell, so no existing script changes behavior. `resolveSetupRunnerShell` keeps its role as the feasibility gate — a bash runner still requires the terminal to resolve to Git Bash, since the launch command is typed into that shell and uses MSYS `/c/...` paths. `buildWindowsRunnerScript` now drops a leading `#!` line rather than `call`ing it, so a declared-bash script that falls back to cmd (Git Bash missing) fails on a real setup line instead of aborting on errorlevel at line one. WSL worktrees, POSIX platforms, and SSH hosts are untouched. * fix(worktrees): keep the cmd setup runner launchable from a Git Bash pane Adversarial review of this PR found that pinning the runner format per script reopened issue #6896 one layer down. - `WorktreeSetupLaunch.shell` had been redefined to mean "the format the runner file was written in". `resolveSetupRunnerCommand` consumes it as "the shell that types the launch command", so a Git Bash terminal with a batch setup script produced `cmd.exe /c "C:\...\setup-runner.cmd"` typed into a bash pane, where MSYS rewrites the `/c` switch into a drive path: cmd opens interactively and setup never runs. `shell` is the terminal's family again; the runner file's .cmd/.sh extension carries the format, and a batch runner launched from a POSIX pane reuses the existing PowerShell ProcessStartInfo launcher. - The cmd runner dropped a leading `#!` line and ran the rest as batch, so a bash script reaching cmd (PowerShell/cmd terminal, or any SSH-to-Windows host) got its interpreter-agnostic prefix executed before failing mid-way. It now prints why and exits 1 without running anything. - A `#!` line's option flags were discarded: `#!/usr/bin/env -S bash -euo pipefail` lost pipefail because the runner is launched as `bash <path>`. The generated posix runner now replays declared flags via `set` and drops the duplicate interpreter line. - Docs cover the per-user setup command in repository hook settings, which goes through the same `#!` rule, and describe what the `#!` line does and does not select. Tests: composed launch command for a POSIX pane + cmd runner (hooks, shared runner command, setup sequencing gate, observed-setup signal), the cmd runner's shebang refusal, and shebang flag replay. Each fails with the source reverted. * fix(worktrees): replay only real `set` flags and keep the gate in the pane's shell Two round-2 review findings: - `#!/bin/bash -l` replayed `set -l`, which exits 2 and aborted the runner under its own `set -e` before a single setup line ran (all platforms). Only the flags `set` documents are replayed now; a bare `-o` with no option name is dropped instead of dumping the shell-option table. - The wait-for-setup gate picked its language from the runner file, so a batch runner launched from a Git Bash pane got the PowerShell gate while the agent startup command was already POSIX-quoted — `Invoke-Expression` cannot parse `'\''`. The gate now follows the pane; the runner still launches through the ProcessStartInfo launcher, never through bash. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
8c5371ebad |
fix(worktrees): respect Windows shell for setup runners (#6967)
* Honor configured shells during worktree setup
* Align setup launch paths with selected Windows shells
* Carry setup shell selection through deferred launches
* Prove Windows setup shell routing at its real adapters
* Ground remote PowerShell proof in the real writer
* Preserve Git Bash across deferred setup launches
* Harden Windows setup runner shell selection
- Resolve remote PowerShell binary without local pwsh probe: for SSH/remote
Windows worktrees, isPwshAvailable() reflects only the LOCAL host, so an
'auto' implementation could route the remote runner to a pwsh.exe the remote
lacks. Add resolveSetupRunnerShell(..., { probeLocalPwsh: false }) so remote
auto keeps the always-present powershell.exe; explicit pwsh.exe still honored.
- Preserve native exit codes in the PowerShell runner by checking
$LASTEXITCODE before $?, so a failing native command surfaces its real code
instead of a generic exit 1; $? still catches cmdlet soft-failures.
- Write the PowerShell runner with a UTF-8 BOM so Windows PowerShell 5.1 (the
new default powershell.exe) reads it as UTF-8 instead of ANSI, preventing
non-ASCII setup-script corruption.
- Add unit tests for the remote-probe behavior.
* Restore setup-shell scope narrowing over the rebase
The force-pushed rebase dropped five review-fix commits that were already
on this branch; this reapplies their combined effect on top of the new
base and the hardening commit:
- Keep SSH setup shell selection remote-owned (no local terminalWindowsShell
or pwsh routing for remote hosts; supersedes the probeLocalPwsh guard)
- Preserve cmd setup compatibility outside POSIX shells (no .ps1 runner
family, so the BOM/exit-code hardening is no longer applicable)
- Route WSL setup runners from the project runtime
- Avoid blocking PowerShell probes during setup creation
- Correct SSH and WSL background setup fixtures
* Satisfy the changed-code gates for the setup-shell runner
- createWorktreeRunnerScript took 7 positional parameters, tripping the
changed-code max-params gate; move it to a single options object.
- hooks-runner.test.ts deep-equals the createSetupRunnerScript result, so
assert the cmd shell now returned for native Windows worktrees.
* Carry the setup launch shell through observed and issue runners
- buildObservedSetupCommand takes the runner's launch shell so WSL-routed
Windows-drive setup replays use /mnt/c instead of Git Bash /c
- resolveSetupRunnerShell gates the posix runner on the same Git Bash
resolution the PTY uses, so a missing or non-MSYS bash keeps the cmd runner
- issue-command runners carry their launch shell, and the renderer passes it
when building the queued command
- treat a bare `bash` shell setting as POSIX like `bash.exe`
Co-authored-by: Orca <help@stably.ai>
* fix(worktrees): close counsel P1 gaps for Windows setup shells
Route windowless/headless creates through the shell-aware setup runner when a
PTY controller is available, existence-check explicit Git Bash paths before
committing to .sh runners, thread the resolved shell into issue-command
runners, and document the intentional Git Bash interpreter flip with a narrow
scope table.
* Convert setup env to MSYS form and harden the bare cmd runner launch
C3: a Git Bash setup runner now receives ORCA_*/CONDUCTOR_*/GHOSTX_* path
values in /c/... form, matching the runner path and the shell's own HOME/PWD.
C5: extension-less `bash` resolves to Git Bash everywhere, matching how
resolveWindowsShellStartupFamily already classifies it.
C7: runner paths carrying characters that cannot be quoted on a cmd command
line launch through a delayed-expansion PowerShell shim instead, and the batch
runner disables inherited delayed expansion so `!` in setup lines survives.
Co-authored-by: Orca <help@stably.ai>
* docs: note MSYS ORCA_* paths and bare bash Git Bash resolution
Keep the setup-shell release note aligned with C3 env conversion and C5 bare
bash resolution so the published claim matches runtime behavior.
* revert: drop windows-setup-shell doc allowlist and AGENTS link
Keep the counsel P1/P2 product fixes without expanding the docs allowlist
or AGENTS.md guidance surface.
* fix(plugins): contain Parcel unsubscribe rejections under Vitest
Dev plugin watchers fire-and-forget unsubscribe, and in-process Parcel
can reject when temp watch roots are already deleted. Catch those
rejections so they cannot fail the suite as unhandled errors.
* fix(plugins): keep in-process unsubscribe rejection surface
Swallowing Parcel unsubscribe errors broke mocked unsubscribe tests
that return non-Promises and expect rejections. Contain failures only
in PluginDevWatcher fire-and-forget paths.
---------
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
a278a30b1c |
Let agents wait for setup when requested (#6298)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
0fd484c75d | Fix WSL UNC setup runner casing (#4139) |