fix(tui): harden PowerShell invocation for safe Windows execution - #4593
Conversation
Prefer structured PowerShell 7 flags (-NoLogo -NoProfile -NonInteractive), capture native \$LASTEXITCODE, and route multiline/heavily-quoted scripts through a temporary -File .ps1 instead of nested -Command strings. Keep cmd.exe raw-arg quoting and Job Object cleanup unchanged. Signed-off-by: Hunter B <hmbown@gmail.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
The shell-hardening slice tripped two `-D warnings` clippy lints that broke both the Lint job and test-target compilation in CI: - clippy::needless_character_iteration on the ASCII guard; `str::is_ascii()` replaces the char iterator. - dead_code on `ShellKind::needs_command_flag`, which is only exercised by the shell-flag unit tests; gate it with `#[cfg(test)]` like the original `is_powershell` helper. Verified locally: `cargo clippy -p codewhale-tui --all-targets --all-features --locked -- -D warnings` is clean, and all 13 shell_dispatcher unit tests pass (including the new multiline temp-`-File` invocation test). No behavior change to PowerShell/cmd/sh dispatch. Signed-off-by: Hunter B <hmbown@gmail.com>
The Windows CI failures on this branch (test_command_spec_shell, test_command_spec_shell_quoted_arg_not_split, test_prepare_unsandboxed, issue_1691_quoted_commit_message_round_trips) all shared one root cause: the tests and display_command() re-derived the dispatcher's argv shape — a second copy of the invocation contract. The hardened PowerShell path (-NoLogo/-NoProfile/-NonInteractive, $LASTEXITCODE capture, temp -File scripts) changed that shape, and every duplicate went stale at once. Fix at the boundary instead of re-syncing the copies: - CommandSpec now carries requested_command — the shell command exactly as requested, recorded at construction. display_command() returns it directly; arg parsing survives only as a fallback for specs built from raw program + args. Future dispatcher wrapping changes can no longer corrupt display. - The three shape-coupled tests now assert intent, not argv layout: the quoted #1691 message must ride in exactly one argv slot with no tokenized fragments, unsandboxed prepare must pass program + args through untouched, and display must round-trip the original command. These hold on sh/bash, cmd, Windows PowerShell, and pwsh alike. Verified locally: 23 sandbox tests, issue_1691 round-trip, 13 shell_dispatcher tests, strict all-target/all-feature locked clippy, fmt. Signed-off-by: Hunter B <hmbown@gmail.com>
|
Claude finished @Hmbown's task in 4m 10s —— View job Claude's Review — PowerShell shell hardeningScope reviewed:
Note: Findings (posted as inline comments)
What's solid
No provider/model routing code is touched by this PR, so the v0.8.65 EPIC #2608 invariant doesn't apply here. |
…p scripts Repairs both exact-head review findings: - A trailing unquoted '#' in a single-line payload commented out the ';'-joined $LASTEXITCODE check, so a failing native command silently reported success. The check now starts on its own line inside the -Command argv string (PowerShell accepts embedded newlines there), and a regression proves the capture survives a '# recent' comment. - Temp -File scripts leaked one file per multiline/non-ASCII invocation. Each script now captures the native exit code, removes itself (PowerShell reads the whole file before executing), and only then propagates the exit code — ordered so a nonzero exit cannot skip the removal. A best-effort sweep also removes codewhale-shell-*.ps1 older than one hour (covers killed processes) without racing fresh scripts. Verified locally: 15 shell_dispatcher tests (three new), 74 sandbox/shell tests, strict all-target/all-feature locked clippy, fmt. Signed-off-by: Hunter B <hmbown@gmail.com>
|
Claude finished @Hmbown's task in 1m 50s —— View job Claude's re-review — PowerShell shell hardening (post-fix)
Note: as in the prior pass, Both prior findings are fixed
Follow-up commits also check out
No new issues found in the fix commits. The PR looks ready from a correctness standpoint; final call on merge is Hunter's per the maintainer test-plan checklist (cargo test / cargo fmt / gitleaks already checked off in the PR description). |
Summary
Translates applicable PowerShell safe-invocation invariants into Codewhale's existing
ShellDispatcher(no skill bundling/copy):pwshdetection order already present-NoLogo -NoProfile -NonInteractivefor all PowerShell spawns$LASTEXITCODEafter the payload.ps1+-Filefor multiline / heavily quoted / non-ASCII scriptsTest plan
cargo test -p codewhale-tui --bin codewhale-tui --locked shell_dispatchercargo fmt --all -- --check