fix(tools): Block MSYS coreutils under Windows sandbox#476
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
WalkthroughThis PR adds Windows MSYS/Cygwin sandbox detection, threads the new ChangesMSYS/Cygwin Sandbox Detection
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant bashTool.run
participant detectShellCommandIssue
participant msysGuardBypassed
participant execToolResult
bashTool.run->>detectShellCommandIssue: inspect command text
detectShellCommandIssue-->>bashTool.run: windows_msys_sandbox or nil
bashTool.run->>msysGuardBypassed: check issue + commandEngine
msysGuardBypassed-->>bashTool.run: allow or block
execToolResult->>execToolResult: detectShellOutputIssue on stdout + stderr
Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/tools/shell_runtime.go (1)
57-96: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
expris MSYS-prone but never actually detected.
MsysProneCommandName(Line 61) listsexpras an MSYS-prone command name, but neitherwindowsMsysPosixExecutablePattern(Line 31) norwindowsPosixUtilityPattern(Line 35) includeexprin their alternations. SodetectShellCommandIssuewill never flag a bareexpr ...invocation on Windows, even thoughknownSafeCommandSegment(incommand_prefix.go) will reject it as unsafe. The model gets no proactive guidance for this specific command even though the rest of the system treats it as MSYS-prone.🔧 Proposed fix
- windowsMsysPosixExecutablePattern = regexp.MustCompile(`(?i)(?:^|[&|;\s"])(?:[\w .:\\-]+\\)?(head|tail|grep|cat|cut|wc|nl|paste|tr|uniq|rev|seq|stat|uname|which|id|awk|sed|xargs)\.exe(?:\s+|$|")`) + windowsMsysPosixExecutablePattern = regexp.MustCompile(`(?i)(?:^|[&|;\s"])(?:[\w .:\\-]+\\)?(head|tail|grep|cat|cut|wc|nl|paste|tr|uniq|rev|seq|stat|uname|which|id|expr|awk|sed|xargs)\.exe(?:\s+|$|")`) windowsPosixUtilityPattern = regexp.MustCompile(`(?i)(^|[&|;]\s*)(head|tail|grep|cat|cut|wc|nl|paste|tr|uniq|rev|seq|stat|uname|which|id|awk|sed|xargs)(?:\s+|$)`) + windowsPosixUtilityPattern = regexp.MustCompile(`(?i)(^|[&|;]\s*)(head|tail|grep|cat|cut|wc|nl|paste|tr|uniq|rev|seq|stat|uname|which|id|expr|awk|sed|xargs)(?:\s+|$)`)🤖 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 `@internal/tools/shell_runtime.go` around lines 57 - 96, The Windows shell detection is missing bare expr even though MsysProneCommandName already treats it as MSYS-prone. Update the Windows command-issue detection in detectShellCommandIssue by adding expr to the same matching path used for other POSIX utilities, likely via the windowsPosixUtilityPattern or the MSYS-prone executable handling alongside windowsMsysPosixExecutablePattern. Keep the existing behavior and make sure a bare expr invocation now returns a windows_msys_sandbox issue with the same kind of guidance as the other MSYS-prone commands.
🤖 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.
Inline comments:
In `@internal/tools/shell_runtime.go`:
- Around line 119-134: The final fallback in msysRuntimeFailedInOutput is too
broad and can misclassify non-MSYS failures as windows_msys_sandbox. Tighten the
last return condition in msysRuntimeFailedInOutput by adding a stronger
MSYS-specific anchor, such as requiring usr\\bin, cygheap, or an MSYS-style log
prefix like [main], alongside the existing win32 error 5 and terminating checks.
Keep the earlier more-specific branches unchanged and make the fallback only
match genuine MSYS runtime failures.
---
Outside diff comments:
In `@internal/tools/shell_runtime.go`:
- Around line 57-96: The Windows shell detection is missing bare expr even
though MsysProneCommandName already treats it as MSYS-prone. Update the Windows
command-issue detection in detectShellCommandIssue by adding expr to the same
matching path used for other POSIX utilities, likely via the
windowsPosixUtilityPattern or the MSYS-prone executable handling alongside
windowsMsysPosixExecutablePattern. Keep the existing behavior and make sure a
bare expr invocation now returns a windows_msys_sandbox issue with the same kind
of guidance as the other MSYS-prone commands.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e6d23b5c-35e6-428c-a314-98b418ed13a3
📒 Files selected for processing (8)
internal/agent/command_prefix.gointernal/agent/command_prefix_test.gointernal/agent/loop_test.gointernal/agent/system_prompt.gointernal/tools/bash_tool_test.gointernal/tools/exec_command.gointernal/tools/shell_runtime.gointernal/tools/shell_runtime_test.go
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Approve — genuine Windows-sandbox hardening; two edge-case false-positives worth a fast-follow
Worth merging: yes. This fixes a real, documented failure (#458): Git-for-Windows MSYS coreutils fail under Zero's write-restricted sandbox token and strand the agent in retry loops. The layered approach — pre-exec block + reactive stderr detection + allowlist tightening + prompt guidance — is well-tested and correct. Thanks @euxaristia.
Reviewed as part of the 6-PR Windows-sandbox cluster, with the detection logic ground-truthed against the actual code and independently re-verified. Note the CHANGES_REQUESTED here is the CodeRabbit bot, not a human — not a merge gate.
Two real but minor edge cases I'd fold in as a fast-follow (non-blocking):
- The
require_escalatedescape hatch it advertises is self-blocked.detectShellCommandIssuefires unconditionally (bash.go:87/exec_command.go:520) beforesandboxPermissionsis consulted, so re-running the same command withrequire_escalated— exactly what the new message suggests — gets hard-blocked identically and never reaches the escalated path that would actually succeed. The advertised recovery isn't actionable through the same tool. - The
.exeregex over-matches.windowsMsysPosixExecutablePattern's leading class[&|;\s"]matches a coreutil.exetoken appearing anywhere as an argument or inside a quoted string — sogit commit -m "fix head.exe crash"is hard-blocked as a false MSYS invocation. Uncommon, but plausible in a repo that literally discusseshead.exe/grep.exe. Anchor it to the invoked-program position the way the barewindowsPosixUtilityPatternalready is. (Also:expris missing from the util set.)
Neither blocks — both are recoverable via native tools and the core fix is correct. Merge as-is and file one polish issue, or fold them in.
Sequencing: land after #465 — both touch bash_tool_test.go with different test functions, so keep a single copy of the echo-arg helper on rebase.
Vasanthdev2004 asked for this PR to be split since it bundled several independent static-analysis fixes. Remove the specialist depth cap, bash output OOM cap, and persistence error logging: each now has its own PR (Gitlawb#491, Gitlawb#492, Gitlawb#493). Also drop the "cat" addition to the Windows POSIX-utility detection in shell_runtime.go. PR Gitlawb#476 already covers cat detection comprehensively under the MSYS/sandbox angle, so keeping it here would duplicate that work. What remains: the Windows cmd.exe quoting-guidance rewrite, the Windows-specific interactive-command suggestions (steering away from Unix head/tail/ps toward native or PowerShell alternatives), the clipboard PowerShell -Command fix, and the CI test-slack change in exec_command_test.go. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jatmn
left a comment
There was a problem hiding this comment.
I found a few issues that need to be addressed before this is ready.
Findings
-
[P2] Make the advertised escalation path actually bypass the MSYS block
internal/tools/bash.go:83
The new MSYS hint tells the model to rerun withsandbox_permissions: "require_escalated"when host-level execution is truly required, but bothbashandexec_commandcalldetectShellCommandIssuebefore the parsedsandboxPermissionsis used to choose the unsandboxed command engine. That means rerunning the exact blocked command withrequire_escalatedis still hard-blocked by the same preflight result and never reaches the escalation path. Please either let approvedrequire_escalatedcalls bypass this MSYS preflight check or remove the escalation suggestion so the recovery guidance matches the actual behavior. -
[P2] Use one MSYS-prone command set for every detection path
internal/tools/shell_runtime.go:31
CodeRabbit's outside-diff request is still valid, and it points at a broader drift problem: this PR now has a centralMsysProneCommandNamelist, butdetectShellCommandIssuestill maintains separate regex alternations.expris listed as MSYS-prone andknownSafeCommandSegmentrejects it on Windows, yet bareexpr ...andexpr.exemiss the proactivewindows_msys_sandboxguidance because the regexes omit it.lshas the opposite inconsistency: it is listed as MSYS-prone, but it hits the oldwindows_shell_syntaxbranch before the MSYS branch, so it gets different metadata and recovery guidance from the rest of the shared set. Please make the preflight detection derive from the same MSYS-prone command set, or otherwise keep the lists and result kinds explicitly synchronized, and add regression coverage forexprandls. -
[P2] Make the MSYS command detection quote-aware
internal/tools/shell_runtime.go:31
The new MSYS preflight regexes do not understand shell quoting, so they can treat harmless argument text as an executable command.windowsMsysPosixExecutablePatternmatcheshead.exe/grep.exeafter any whitespace or quote, which hard-blocks commands likegit commit -m "fix head.exe crash"orgh pr comment --body "grep.exe fails under MSYS". The bare utility regex has the same boundary problem around quoted operators, soecho "try git log | head"can be blocked as ifheadwere actually being invoked. This repo now explicitly discusses those command names, so the false positive is likely during follow-up commits, PR comments, or docs edits. Please anchor detection to parsed command positions or explicit MSYS paths, and add regression tests for quoted argument text.
…uote-aware Addresses jatmn's review on PR Gitlawb#476: - bash and exec_command called detectShellCommandIssue before resolving sandbox_permissions into a command engine, so a require_escalated retry of a blocked command was still hard-blocked by the same preflight check. Resolve the command engine first and only bypass the windows_msys_sandbox issue kind when that resolves to true unsandboxed execution; other issue kinds (real cmd.exe syntax problems) still block regardless of escalation. - Unify all MSYS-prone command name checks (MsysProneCommandName, the preflight scan, and transitively command_prefix.go's known-safe guard) behind one windowsMsysProneNames set. This fixes expr (in the name set but missing from the old regex alternations, so never proactively flagged) and ls (hit the older windows_shell_syntax branch first, so it got different metadata than every other MSYS-prone name). - Make the preflight scan quote-aware: split the command into cmd.exe- operator-separated segments respecting double-quote grouping, and only check the first word of each segment instead of scanning raw text anywhere. Fixes false positives on quoted text like commit messages or PR comment bodies that merely mention a coreutil name.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@internal/tools/exec_command_test.go`:
- Around line 164-176: The Windows-only exec command test is using a
PATH-dependent command in the RunWithOptions call, so the result can be polluted
by a shell_issue even when the sandbox bypass works. Update the test in
exec_command_test.go to use a guaranteed native command instead of the current
cat invocation, or assert the require_escalated bypass directly from
RunWithOptions/ExecCommandToolName behavior so the check does not depend on
shell availability.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 88ad9884-a6d4-4623-9219-de8285ddd2ba
📒 Files selected for processing (6)
internal/tools/bash.gointernal/tools/bash_tool_test.gointernal/tools/exec_command.gointernal/tools/exec_command_test.gointernal/tools/shell_runtime.gointernal/tools/shell_runtime_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/tools/shell_runtime_test.go
|
Status update on the open findings: Already fixed on this branch before this push:
Fixed in 304a5a5 (this push): CodeRabbit's last finding, that TestExecCommandRequireEscalatedBypassesMsysGuardAfterApproval asserted shell_issue == "" after running the real go build/vet clean on both linux and windows targets, go test ./internal/tools/... ./internal/agent/... green. @jatmn ready for re-review. |
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the changed paths and found a couple of issues that still need to be addressed.
Findings
-
[P2] Apply cmd.exe command-position parsing to every MSYS preflight path
internal/tools/shell_runtime.go:151
The quote-aware fix only covers the bare-name scan, but the explicit MSYS path detector still runs against the entire raw command beforewindowsCommandSegments/firstCommandWordis used. That means commands such asgit commit -m "C:\Program Files\Git\usr\bin\head.exe fails"orgh pr comment --body "C:\Git\usr\bin\grep.exe is blocked"are still hard-blocked even though the MSYS path is just quoted argument text, which is the same class of false positive the latest parser was meant to avoid. The segment splitter also does not match cmd.exe tokenization: it treats caret-escaped operators and;as command separators, soecho ^| headandecho foo; headare classified as actualheadinvocations even though cmd.exe prints those as argument text. Please route the explicit path check through a real command-position parser, or otherwise make the parser handle quoted/escaped argument text consistently, and add regressions for quoted MSYS paths, caret-escaped operators, and semicolon text. -
[P2] Do not offer a prefix approval that leaves an MSYS-prone tail uncovered
internal/agent/command_prefix.go:31
This PR removes MSYS-prone commands fromknownSafeCommandSegmenton Windows, butproposedCommandPrefixstill returns the first non-safe valid segment before proving the remaining segments are safe. For a command likeps aux | head -5, the prompt can still offer onlyps auxas the reusable prefix; if the user chooses that option,shellExecutionArgsForApprovaladdssandbox_permissions: "require_escalated"to the entire command and the first execution runs the uncoveredheadsegment unsandboxed. That undercuts the new guard because the current command can still bypass the sandbox through a prefix that does not cover the MSYS-prone segment. Please only offer prefix approval when every other segment is genuinely known-safe for the current OS, or include the unsafe segment in the approval flow instead of escalating the whole command under the earlier prefix.
…x approval gap Address jatmn's latest review on PR Gitlawb#476: - shell_runtime.go: the explicit MSYS binary-path check (usr\bin\, mingw64\bin\, msys-2.0.dll, cygwin1.dll) ran against the raw command string, so a quoted mention (e.g. a commit message describing a usr\bin\head.exe failure) was still hard-blocked. It now runs against the first word of each operator-separated segment, same as the coreutil-name check. windowsCommandSegments also stopped treating ; as a command separator (cmd.exe does not split on ;, unlike bash) and now respects cmd.exe's ^ escape character, so escaped operators like echo ^| head are no longer misread as a real head invocation. - command_prefix.go: proposedCommandPrefix could offer to approve a single segment (e.g. "ps aux" in "ps aux | head -5") without checking that every other segment was independently safe. Since approving a prefix escalates the whole command to bypass the sandbox, an MSYS-prone or otherwise unsafe segment could ride along uncovered. It now only proposes a segment as the prefix when every other segment in the command is known-safe on its own. Added regression tests for quoted MSYS path mentions, caret-escaped operators, semicolon text, and the prefix-approval gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the changed paths and found a couple of issues that still need to be addressed.
Findings
-
[P2] Treat cmd redirection as a command-name boundary
internal/tools/shell_runtime.go:177
The new command-position scan only checks the first whitespace-delimited word of each cmd.exe segment. That still misses real MSYS-prone invocations when cmd redirection is attached directly to the command name, which cmd.exe accepts. For example,some-command | head>out.txt,cat<in.txt, andgrep>matches.txtall invoke the same MSYS-prone utilities, butfirstCommandWordreturnshead>out.txt/cat<in.txt/grep>matches.txt, somsysProneCommandWordnever matches the bare command name. Please strip or split cmd redirection operators when extracting the invoked command word, and add regressions for attached</>redirection. -
[P2] Keep post-execution MSYS detection from matching quoted command text
internal/tools/shell_runtime.go:193
The preflight path now avoids quoted MSYS-path mentions, but the output heuristic still lowercases and scanscommand + "\n" + output. That means a failed command whose arguments merely contain an MSYS failure string, such as agh pr comment --bodyor commit command quotingC:\Git\usr\bin\head.exe: *** fatal error ..., can be mislabeled aswindows_msys_sandboxeven when the actual failure output is unrelated. This reintroduces the same false-positive class the command-position parser just fixed, only after execution. Please base the MSYS runtime heuristic on the command's actual output/stderr, or otherwise apply the same command-position/quote-aware treatment before using command text as evidence.
Process note: the linked issue #458 still does not have the issue-approved label, while CONTRIBUTING.md says community PRs require an approved parent issue. Please wait for maintainer confirmation on whether this PR can proceed under that policy.
…nd text as output evidence Addresses jatmn's latest review on PR Gitlawb#476: - firstCommandWord only split on whitespace, so cmd.exe redirection attached directly to a command name (head>out.txt, cat<in.txt, grep>matches.txt) was read as one word and never matched against the MSYS-prone name set. It now also stops at < and >. - detectShellOutputIssue scanned command+output together, so a command whose own argument text merely quoted an MSYS failure string (e.g. a gh pr comment --body describing the bug) could be mislabeled after running even though the real stdout/stderr was unrelated. Removed the command parameter entirely from detectShellOutputIssue and both call sites (bash.go, exec_command.go), so the command line can never be used as post-execution evidence again.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Re-approving. My earlier approval predates the hardening you and jatmn worked through, so I read the delta since then rather than let a stale approval ride on a sandbox PR.
The part that matters most is the escalation hole in proposedCommandPrefix, and it's closed correctly: since approving a prefix escalates the whole command past the sandbox, it now refuses to propose one unless every other segment is independently known-safe, and knownSafeCommandSegment no longer treats MSYS-prone coreutils as safe on Windows. That's the actual security boundary and it holds.
The rest is advisory-hint quality, and it's careful too — detection is anchored to the first word of each cmd.exe segment (so head.exe quoted in a commit message or PR body doesn't false-trigger), the output scan reads output only instead of the command line, the win32-error-5 fallback is anchored to MSYS markers so an unrelated ACCESS_DENIED isn't mislabeled, and the prone-name set is one source of truth shared by every path so they can't drift. Test coverage across all of it is good.
Solid. Over to kevin.
|
please rebase from main and fix conflicts @euxaristia |
Git for Windows usr\bin coreutils (head, grep, cat, ...) are MSYS PE binaries that fail with ACCESS_DENIED and cygheap/signal-pipe errors when Zero runs them in the write-restricted Windows sandbox. Detect and block these commands before execution, surface MSYS runtime failures from stderr, steer agents toward native Zero tools or require_escalated, and exclude MSYS-prone names from known-safe command tails. Refs Gitlawb#458
CodeRabbit review feedback: the win32-error-5 plus terminating check had no MSYS-specific anchor, so unrelated access-denied failures could be mislabeled as windows_msys_sandbox. Require an MSYS marker like the neighboring checks do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uote-aware Addresses jatmn's review on PR Gitlawb#476: - bash and exec_command called detectShellCommandIssue before resolving sandbox_permissions into a command engine, so a require_escalated retry of a blocked command was still hard-blocked by the same preflight check. Resolve the command engine first and only bypass the windows_msys_sandbox issue kind when that resolves to true unsandboxed execution; other issue kinds (real cmd.exe syntax problems) still block regardless of escalation. - Unify all MSYS-prone command name checks (MsysProneCommandName, the preflight scan, and transitively command_prefix.go's known-safe guard) behind one windowsMsysProneNames set. This fixes expr (in the name set but missing from the old regex alternations, so never proactively flagged) and ls (hit the older windows_shell_syntax branch first, so it got different metadata than every other MSYS-prone name). - Make the preflight scan quote-aware: split the command into cmd.exe- operator-separated segments respecting double-quote grouping, and only check the first word of each segment instead of scanning raw text anywhere. Fixes false positives on quoted text like commit messages or PR comment bodies that merely mention a coreutil name.
TestBashToolRequireEscalatedMsysGuard and TestExecCommandRequireEscalatedBypassesMsysGuardAfterApproval asserted shell_issue == "" to prove the require_escalated bypass worked. Once bypassed, "cat somefile.txt" actually runs unsandboxed, and its real, PATH-dependent output could trip the unrelated post-execution detectShellOutputIssue heuristic, failing the assertion for reasons unrelated to the preflight guard under test. Assert on the exit_code "-1" sentinel (set only by shellIssueBlockResult) instead, which directly reflects whether the preflight guard blocked the command.
…x approval gap Address jatmn's latest review on PR Gitlawb#476: - shell_runtime.go: the explicit MSYS binary-path check (usr\bin\, mingw64\bin\, msys-2.0.dll, cygwin1.dll) ran against the raw command string, so a quoted mention (e.g. a commit message describing a usr\bin\head.exe failure) was still hard-blocked. It now runs against the first word of each operator-separated segment, same as the coreutil-name check. windowsCommandSegments also stopped treating ; as a command separator (cmd.exe does not split on ;, unlike bash) and now respects cmd.exe's ^ escape character, so escaped operators like echo ^| head are no longer misread as a real head invocation. - command_prefix.go: proposedCommandPrefix could offer to approve a single segment (e.g. "ps aux" in "ps aux | head -5") without checking that every other segment was independently safe. Since approving a prefix escalates the whole command to bypass the sandbox, an MSYS-prone or otherwise unsafe segment could ride along uncovered. It now only proposes a segment as the prefix when every other segment in the command is known-safe on its own. Added regression tests for quoted MSYS path mentions, caret-escaped operators, semicolon text, and the prefix-approval gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nd text as output evidence Addresses jatmn's latest review on PR Gitlawb#476: - firstCommandWord only split on whitespace, so cmd.exe redirection attached directly to a command name (head>out.txt, cat<in.txt, grep>matches.txt) was read as one word and never matched against the MSYS-prone name set. It now also stops at < and >. - detectShellOutputIssue scanned command+output together, so a command whose own argument text merely quoted an MSYS failure string (e.g. a gh pr comment --body describing the bug) could be mislabeled after running even though the real stdout/stderr was unrelated. Removed the command parameter entirely from detectShellOutputIssue and both call sites (bash.go, exec_command.go), so the command line can never be used as post-execution evidence again.
136d720 to
91700b8
Compare
|
@kevincodex1 Done, rebased onto main and resolved the conflict in bash.go (main had already merged a stdout/stderr budget-capture rewrite that overlapped with this PR's changes). Force-pushed. GitHub now shows this as mergeable with no conflicts. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/tools/bash.go (1)
539-545: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDead code:
truncateHeadTailis unused and trips the linter.golangci-lint flags this as an
unusederror, so it'll fail CI. Nothing calls the wrapper —truncateHeadTailWithTotalis used directly. Delete it.🧹 Proposed removal
-// truncateHeadTail keeps the first and last halves of value when it exceeds -// maxBytes, dropping the middle behind a marker. Returns the possibly-truncated -// text, the raw byte length, and whether truncation happened. -func truncateHeadTail(value string, maxBytes int) (string, int, bool) { - // value holds the whole stream, so its length is the true raw size. - return truncateHeadTailWithTotal(value, len(value), maxBytes) -} -Confirm it's truly unreferenced (incl. tests / exec_command.go) before removing:
#!/bin/bash rg -nP '\btruncateHeadTail\b\s*\(' --type=go🤖 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 `@internal/tools/bash.go` around lines 539 - 545, `truncateHeadTail` is dead code and only wraps `truncateHeadTailWithTotal`, so it should be removed if it is truly unreferenced. Confirm there are no callers in the Go tree, including tests and `exec_command.go` (use the `truncateHeadTail` symbol to verify), then delete the unused `truncateHeadTail` function and keep `truncateHeadTailWithTotal` as the single implementation.Source: Linters/SAST tools
🤖 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 `@internal/tools/bash.go`:
- Around line 539-545: `truncateHeadTail` is dead code and only wraps
`truncateHeadTailWithTotal`, so it should be removed if it is truly
unreferenced. Confirm there are no callers in the Go tree, including tests and
`exec_command.go` (use the `truncateHeadTail` symbol to verify), then delete the
unused `truncateHeadTail` function and keep `truncateHeadTailWithTotal` as the
single implementation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 34b4ec18-8d1a-4f5e-90e0-ed7b864925ff
📒 Files selected for processing (10)
internal/agent/command_prefix.gointernal/agent/command_prefix_test.gointernal/agent/loop_test.gointernal/agent/system_prompt.gointernal/tools/bash.gointernal/tools/bash_tool_test.gointernal/tools/exec_command.gointernal/tools/exec_command_test.gointernal/tools/shell_runtime.gointernal/tools/shell_runtime_test.go
✅ Files skipped from review due to trivial changes (1)
- internal/agent/system_prompt.go
🚧 Files skipped from review as they are similar to previous changes (8)
- internal/tools/exec_command_test.go
- internal/tools/exec_command.go
- internal/agent/loop_test.go
- internal/tools/shell_runtime_test.go
- internal/tools/bash_tool_test.go
- internal/agent/command_prefix_test.go
- internal/tools/shell_runtime.go
- internal/agent/command_prefix.go
CodeRabbit nitpick: truncateHeadTailWithTotal is called directly everywhere, so this wrapper is dead code that trips golangci-lint's unused check and fails CI.
…itlawb#476 Per Vasanthdev2004's review: this PR and Gitlawb#476 (already approved) both reworked shell_runtime.go's Windows POSIX/MSYS detection independently and incompatibly. Gitlawb#476 is the more thorough of the two (segment/word-anchored detection via windowsCommandSegments + firstCommandWord + a single windowsMsysProneNames set, plus MSYS binary-path detection, the require_escalated escalation-bypass fix, and redirection-boundary handling that this branch's regex-plus-stripping approach didn't cover). Once Gitlawb#476 merges, this branch would no longer compile against main (it still references windowsLSCommandPattern, which Gitlawb#476 deletes). Revert shell_runtime.go and the corresponding bash_tool_test.go additions to match main exactly, letting Gitlawb#476 own that surface and close Gitlawb#467. This PR keeps only its non-overlapping pieces: the quoting guidance in system_prompt.go, the clipboard escaping fix, and the CI jitter tolerance increase.
go vet, gofmt, govulncheck, and deadcode don't catch unused private functions/assignments reachable within a package but never called, or staticcheck-style correctness/readability issues. deadcode's whole-program reachability analysis specifically missed several of these; golangci-lint's truncateHeadTail nitpick on #476 assumed it would fail CI, but nothing in this repo actually ran it. Scoped to unused, ineffassign, and staticcheck rather than the full default battery, and continue-on-error like deadcode, so this surfaces the existing findings across the repo (being cleaned up incrementally per #527) without blocking in-flight PRs on a big-bang adoption.
's MSYS text Gitlawb#476 merged into main and rewrote the same "Shell syntax: Windows cmd.exe..." guidance string in system_prompt.go and shellGuidanceForGOOS in shell_runtime.go that this branch also edits (this PR's fix for the broken single-quoted quoting examples). Combined both into one coherent string in each spot: the double-quote/single-quote metachar rule with correct examples, plus Gitlawb#476's MSYS coreutils warning and require_escalated mention. Merged the corresponding test assertions in loop_test.go the same way. internal/imageinput/clipboard.go, internal/tools/bash_tool_test.go, and internal/tools/exec_command_test.go applied cleanly with no overlap.
Rebasing fix/bug-hunt-findings onto main pulled in Gitlawb#476, which replaced this file's regex-based windows_shell_syntax detection with a segment/word-anchored check and deliberately stopped treating ; as a command separator on Windows (cmd.exe doesn't split on ; the way bash does). detectShellCommandIssue itself came out of the rebase unchanged from main (our quote-masking version was fully superseded by Gitlawb#476's more thorough segment-based approach), but the quoted-content test this PR added still asserted the old bash-style semantics for `echo "foo" ; ls -la`, which now correctly returns no issue since it's a single echo invocation with literal arguments under cmd.exe, not a real ls invocation.
* fix: address bugs found in a multi-agent codebase audit Five parallel review agents each swept a different area (self-update, TUI keybindings, agent/session handling, sandbox/shell safety, and auth/credential storage) and surfaced nine concrete defects, all fixed here with regression tests: - sandbox: grant_scope path comparisons were case-sensitive, letting a persisted deny grant be bypassed by spelling a Windows path with different case - securefile/oauth: createSecretFile's Windows ERROR_ACCESS_DENIED handling was only applied to the oauth token store, not the shared credstore backend; both also masked the real lock-contention error behind a misleading "file does not exist" timeout - cli: firstUsableProvider ignored OAuth logins when picking a fallback provider, forcing unnecessary re-onboarding for an already-authenticated user - sessions: exec prompt summarization truncated on a raw byte offset, which could split a multi-byte UTF-8 rune and embed invalid UTF-8 into a resumed session's prompt - tui: configurable keybindings had no collision detection, so a remapped chord could silently make a hardcoded shortcut (or another configured binding) permanently unreachable - tools: the Windows shell-syntax pre-flight check wasn't quote-aware and could block harmless commands whose quoted arguments merely contained operator-like text - update: checksum verification didn't cross-check the archive filename it verified against the one being extracted; extractZip didn't reject symlink-mode entries like extractTarGz already did; replaceBinary's restore-on-failure rename had no retry, risking a permanently missing binary on a transient Windows file lock * fix: close remaining PR 481 review findings * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(tools): drop stale ; test case after rebasing onto main's MSYS check Rebasing fix/bug-hunt-findings onto main pulled in #476, which replaced this file's regex-based windows_shell_syntax detection with a segment/word-anchored check and deliberately stopped treating ; as a command separator on Windows (cmd.exe doesn't split on ; the way bash does). detectShellCommandIssue itself came out of the rebase unchanged from main (our quote-masking version was fully superseded by #476's more thorough segment-based approach), but the quoted-content test this PR added still asserted the old bash-style semantics for `echo "foo" ; ls -la`, which now correctly returns no issue since it's a single echo invocation with literal arguments under cmd.exe, not a real ls invocation. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: address bugs found in a multi-agent codebase audit Five parallel review agents each swept a different area (self-update, TUI keybindings, agent/session handling, sandbox/shell safety, and auth/credential storage) and surfaced nine concrete defects, all fixed here with regression tests: - sandbox: grant_scope path comparisons were case-sensitive, letting a persisted deny grant be bypassed by spelling a Windows path with different case - securefile/oauth: createSecretFile's Windows ERROR_ACCESS_DENIED handling was only applied to the oauth token store, not the shared credstore backend; both also masked the real lock-contention error behind a misleading "file does not exist" timeout - cli: firstUsableProvider ignored OAuth logins when picking a fallback provider, forcing unnecessary re-onboarding for an already-authenticated user - sessions: exec prompt summarization truncated on a raw byte offset, which could split a multi-byte UTF-8 rune and embed invalid UTF-8 into a resumed session's prompt - tui: configurable keybindings had no collision detection, so a remapped chord could silently make a hardcoded shortcut (or another configured binding) permanently unreachable - tools: the Windows shell-syntax pre-flight check wasn't quote-aware and could block harmless commands whose quoted arguments merely contained operator-like text - update: checksum verification didn't cross-check the archive filename it verified against the one being extracted; extractZip didn't reject symlink-mode entries like extractTarGz already did; replaceBinary's restore-on-failure rename had no retry, risking a permanently missing binary on a transient Windows file lock * fix: close remaining PR 481 review findings * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(tools): drop stale ; test case after rebasing onto main's MSYS check Rebasing fix/bug-hunt-findings onto main pulled in Gitlawb#476, which replaced this file's regex-based windows_shell_syntax detection with a segment/word-anchored check and deliberately stopped treating ; as a command separator on Windows (cmd.exe doesn't split on ; the way bash does). detectShellCommandIssue itself came out of the rebase unchanged from main (our quote-masking version was fully superseded by Gitlawb#476's more thorough segment-based approach), but the quoted-content test this PR added still asserted the old bash-style semantics for `echo "foo" ; ls -la`, which now correctly returns no issue since it's a single echo invocation with literal arguments under cmd.exe, not a real ls invocation. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(tools): block MSYS and WSL shells under the Windows sandbox Git-for-Windows bash.exe and sh.exe are MSYS binaries: the MSYS runtime secures its signal pipe and shared-memory sections with explicit DACLs granting only the user, Administrators, and SYSTEM (sigproc.cc sigproc_init -> sec_user_nih), so under the WRITE_RESTRICTED token the second access check can never pass and startup dies with "couldn't create signal pipe" or "CreateFileMapping <SID>.1", Win32 error 5. The System32 WSL bash launcher fails equivalently: the restricted token cannot connect to the WSL service (Bash/Service/CreateInstance/ E_ACCESSDENIED). Every executable a bare bash can resolve to fails, so block bash and sh preflight like the MSYS coreutils from #476, mention the indirect spawn paths (git hooks, git/gh credential helpers) in the shared suggestion, detect the WSL launcher's UTF-16LE service-denied stderr, and document the root cause in the runner's KNOWN LIMITATION comment beside the Schannel note. * fix(tui): align picker_test switchProviderModel call with 4-value return * test(tools): cover sh.exe in Windows shell blocking test
…ix (#468) * merge: rebase onto main, combine Windows shell guidance with #476's MSYS text #476 merged into main and rewrote the same "Shell syntax: Windows cmd.exe..." guidance string in system_prompt.go and shellGuidanceForGOOS in shell_runtime.go that this branch also edits (this PR's fix for the broken single-quoted quoting examples). Combined both into one coherent string in each spot: the double-quote/single-quote metachar rule with correct examples, plus #476's MSYS coreutils warning and require_escalated mention. Merged the corresponding test assertions in loop_test.go the same way. internal/imageinput/clipboard.go, internal/tools/bash_tool_test.go, and internal/tools/exec_command_test.go applied cleanly with no overlap. * test(tools): add host shell syntax description test for exec_command * chore: retrigger CI after infra flake * style(tools): gofmt exec_command_test.go --------- Co-authored-by: Vasanthdev2004 <vasanth.dev2004@gmail.com>
Fixes #458
Summary
Git for Windows ships POSIX coreutils (head, grep, cat, and similar) as MSYS PE binaries on PATH. Under Zero's write-restricted Windows sandbox they fail at startup with ACCESS_DENIED, CreateFileMapping, signal-pipe, and cygheap errors instead of running the command. This leaves agents retrying broken shell pipelines.
Detect MSYS-prone commands before execution, surface MSYS runtime failures from command output, steer the agent toward native Zero tools or cmd/PowerShell alternatives, and exclude MSYS-prone command names from known-safe segmented-command tails on Windows.
Note: #458 may still need the
issue-approvedlabel per CONTRIBUTING.md before merge.Changes
internal/tools/shell_runtime.go.exe, or directory-prefixed).MsysProneCommandName()for shared guard logic, now backed by a single canonical name set shared by every detection path.windows_msys_sandboxissues fromdetectShellCommandIssue()with guidance toward native tools orrequire_escalated.detectShellOutputIssue()(CreateFileMapping, signal pipe, cygheap).internal/tools/bash.go,internal/tools/exec_command.gointernal/agent/command_prefix.gointernal/agent/system_prompt.gointernal/tools/shell_runtime_test.goexpr/lsconsistency.internal/tools/bash_tool_test.go,internal/tools/exec_command_test.go,internal/agent/command_prefix_test.go,internal/agent/loop_test.goUpdates addressing jatmn's review
bashandexec_commandwere callingdetectShellCommandIssue()before resolvingsandboxPermissionsinto a command engine, so rerunning a blocked command withsandbox_permissions: "require_escalated"was still hard-blocked by the same preflight check it was supposed to escalate past. Both tools now resolve the command engine first and only bypass thewindows_msys_sandboxissue kind when that resolves to true unsandboxed execution (an approved escalation). Any other issue kind (a real cmd.exe syntax problem) still blocks regardless of escalation, since running outside the sandbox does not fix a syntax error. Covered byTestBashToolRequireEscalatedMsysGuardandTestExecCommandRequireEscalatedBypassesMsysGuardAfterApproval.windowsMsysProneNamesis now the single source of truth:MsysProneCommandName(), the preflight command scan, and (transitively) the known-safe-segment guard incommand_prefix.goall derive from it. This fixes two drift bugs:exprwas in the name set but missing from the old regex alternations (so it was never proactively flagged), andlshit the olderwindows_shell_syntaxbranch before ever reaching the MSYS check (so it got different metadata/guidance than every other MSYS-prone name). Covered byTestDetectShellCommandIssueFlagsExprAndLsConsistently.echo "log | head is broken") could be misdetected as an actual invocation. Detection now splits the command into cmd.exe-operator-separated segments (respecting double-quote grouping) and only checks the first word of each segment, the actual command position, not the raw text anywhere. Covered byTestDetectShellCommandIssueIgnoresQuotedMsysMentions.usr\bin\,mingw64\bin\, ...) still scanned raw command text, so a quoted path mention had the same false-positive risk item 3 fixed for coreutil names; it now runs against the same per-segment first word. The segment splitter also stopped treating;as a separator (cmd.exe does not split on it, unlike bash) and now respects the^escape character, soecho ^| headis no longer misread as invokinghead. Separately,command_prefix.go'sproposedCommandPrefixcould offer to approve a prefix (e.g.ps auxinps aux | head -5) without checking that the remaining segments were independently safe, letting an MSYS-prone tail ride along uncovered once the whole command was escalated. It now only proposes a prefix when every other segment is known-safe on its own.firstCommandWordsplit only on whitespace, sohead>out.txtorcat<in.txt(cmd.exe accepts redirection with no separating space) were read as one word and missed entirely; it now also stops at</>. Separately,detectShellOutputIssue()scannedcommand + outputtogether, so a command whose own argument text merely quoted an MSYS failure string (e.g. agh pr comment --bodydescribing the bug) could be mislabeled after running, even when the real stdout/stderr was unrelated. It now takes onlyoutput(thecommandparameter is removed from its signature and both call sites), so the command line can never be used as evidence again. Covered byTestDetectShellCommandIssueFlagsRedirectionAttachedToCommandandTestBashToolIgnoresMsysMarkersInCommandArgumentsAfterFailure.Test plan
go build ./...go test ./internal/tools/...(pass)go test ./internal/agent/...(pass)go vet ./internal/tools/... ./internal/agent/...(clean)head.exe/git log | headblocked or surfaced with MSYS sandbox guidance on WindowsSummary by CodeRabbit