fix(windows): reject WSL bash.exe in findGitBashPath detection - #1329
Conversation
Windows 启用 WSL 时,`where.exe bash` 返回 C:\Windows\System32\bash.exe(WSL 启动器)和 %LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe(WSL App Execution Alias),位置常在 Git for Windows 的 bash 之前。原 findExecutableWithDeps 直接返回首个非 cwd 结果,CCB 因此把 WSL 启动器误认为 Git Bash,污染 process.env.SHELL 与 CLAUDE_CODE_GIT_BASH_PATH 整个会话,所有 hook / BashTool 调用都会拉起 wsl.exe 弹窗。 在 findExecutableWithDeps 内、当 executable='bash' 时,过滤 System32 与 WindowsApps 的 bash.exe 候选。continue 到下一个 where.exe 命中,让 PATH 后续的合法 Git Bash 仍能胜出。下游消费者(setShellIfWindows env 传播、hooks.ts spawn 点、Shell.ts SHELL 分支)通过 env 缓存自动受益。 同时添加用户可见的提示: - Shell.ts findSuitableShell:检测到 WSL bash 但无 Git for Windows 且未设 CLAUDE_CODE_GIT_BASH_PATH override 时,启动一次性 warning。 - doctorDiagnostic.ts:相同条件的 `claude doctor` 持久诊断项,方便用户随时复查。 3 个新单元测试覆盖该过滤器: - 拒绝 WSL System32 bash → null - 拒绝 WSL WindowsApps bash → null - 跳过 WSL bash,fall through 到下一个 where.exe 命中 Co-Authored-By: glm-5.2[1m] <zai-org@claude-code-best.win>
📝 WalkthroughWalkthroughWindows Bash discovery now rejects WSL launcher aliases under ChangesWindows Bash handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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: 2
🤖 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 `@src/utils/Shell.ts`:
- Around line 80-103: Replace the PATH-only Git Bash availability logic in
src/utils/Shell.ts lines 80-103 with the shared resolver from
src/utils/windowsPaths.ts, while preserving the WSL launcher check; use the
resolver result so valid overrides and Git for Windows found in standard
locations suppress the warning, but invalid overrides do not. Apply the same
resolver-based decision in src/utils/doctorDiagnostic.ts lines 369-386 so the
diagnostic does not warn when a valid override or discoverable Git Bash exists.
Add regression coverage for both a valid CLAUDE_CODE_GIT_BASH_PATH override and
Git for Windows unavailable from where.exe bash.
- Around line 85-88: Update the where.exe lookup inside findSuitableShell() to
use asynchronous Bun subprocess execution instead of execFileSync, while
preserving ignored failures and the existing PATH-detection behavior. Apply a
bounded timeout to the subprocess so shell discovery cannot hang.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8d157026-299e-46c4-8f5a-3fcf94bf2c0c
📒 Files selected for processing (4)
src/utils/Shell.tssrc/utils/__tests__/windowsPaths.test.tssrc/utils/doctorDiagnostic.tssrc/utils/windowsPaths.ts
| getPlatform() === 'windows' && | ||
| !process.env.CLAUDE_CODE_GIT_BASH_PATH && | ||
| !process.env.CLAUDE_CODE_GIT_BASH_PATH_WARNED | ||
| ) { | ||
| try { | ||
| const whereResult = execFileSync('where.exe', ['bash'], { | ||
| stdio: ['ignore', 'pipe', 'ignore'], | ||
| encoding: 'utf8', | ||
| }) | ||
| const lines = whereResult | ||
| .split(/\r?\n/) | ||
| .map(l => l.trim().toLowerCase()) | ||
| .filter(Boolean) | ||
| const hasWslBash = lines.some(l => | ||
| /(?:system32|windowsapps)\\bash\.exe$/.test(l), | ||
| ) | ||
| const hasGitBash = lines.some(l => /\\git\\.*bash\.exe$/.test(l)) | ||
| if (hasWslBash && !hasGitBash) { | ||
| process.env.CLAUDE_CODE_GIT_BASH_PATH_WARNED = '1' | ||
| console.warn( | ||
| '[CCB] Detected WSL bash on PATH without Git for Windows. ' + | ||
| 'Hooks and BashTool will not work correctly. ' + | ||
| 'Install Git for Windows (https://git-scm.com/download/windows) ' + | ||
| 'or set CLAUDE_CODE_GIT_BASH_PATH to your bash.exe.', |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use the Git Bash resolver for the availability check.
where.exe bash only reports Bash executables on PATH. It does not determine whether a usable Git Bash exists. The resolver in src/utils/windowsPaths.ts also validates CLAUDE_CODE_GIT_BASH_PATH, derives Bash from Git, and searches standard locations.
src/utils/Shell.ts#L80-L103: Keep the WSL launcher check, but use the resolver result to determine whether a usable Git Bash or valid override exists. The current nonempty override check suppresses warnings for invalid paths.src/utils/doctorDiagnostic.ts#L369-L386: Use the same resolver result. The current diagnostic warns even when a validCLAUDE_CODE_GIT_BASH_PATHoverride exists.
Add regression coverage for a valid override and for Git for Windows that is discoverable without appearing in where.exe bash. The PR objective requires Git Bash-or-override detection.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync, spawn } from 'child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
📍 Affects 2 files
src/utils/Shell.ts#L80-L103(this comment)src/utils/doctorDiagnostic.ts#L369-L386
🤖 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 `@src/utils/Shell.ts` around lines 80 - 103, Replace the PATH-only Git Bash
availability logic in src/utils/Shell.ts lines 80-103 with the shared resolver
from src/utils/windowsPaths.ts, while preserving the WSL launcher check; use the
resolver result so valid overrides and Git for Windows found in standard
locations suppress the warning, but invalid overrides do not. Apply the same
resolver-based decision in src/utils/doctorDiagnostic.ts lines 369-386 so the
diagnostic does not warn when a valid override or discoverable Git Bash exists.
Add regression coverage for both a valid CLAUDE_CODE_GIT_BASH_PATH override and
Git for Windows unavailable from where.exe bash.
| const whereResult = execFileSync('where.exe', ['bash'], { | ||
| stdio: ['ignore', 'pipe', 'ignore'], | ||
| encoding: 'utf8', | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate Shell.ts =="
fd -a 'Shell\.ts$' . || true
echo "== git diff stat/name-status =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat 2>/dev/null || true
git diff --name-only HEAD~1..HEAD 2>/dev/null || git diff --name-only 2>/dev/null || true
echo "== inspect Shell.ts outline/sections =="
if [ -f src/utils/Shell.ts ]; then
wc -l src/utils/Shell.ts
sed -n '1,160p' src/utils/Shell.ts
fi
echo "== search execFileSync/spawn usage =="
rg -n "execFileSync|execFileSync|spawn\(|Bun\.spawn|where\.exe|bash" src package.json bun.lock 2>/dev/null || trueRepository: claude-code-best/claude-code
Length of output: 50385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== inspect Shell.ts outline/sections =="
if [ -f src/utils/Shell.ts ]; then
wc -l src/utils/Shell.ts
sed -n '1,160p' src/utils/Shell.ts
fi
echo "== search process execution and Shell findSuitableShell callers =="
rg -n "findSuitableShell|execFileSync|execFileSync|spawn\(|Bun\.spawn|where\.exe|bash" src package.json bun.lock 2>/dev/null || true
echo "== deterministic source shape check using Python =="
python3 - <<'PY'
from pathlib import Path
p = Path('src/utils/Shell.ts')
if not p.exists():
raise SystemExit('src/utils/Shell.ts not found')
text = p.read_text()
checks = {
'has_async_findSuitableShell': 'async function findSuitableShell' in text or 'findSuitableShell(' in text and 'async' in text[:text.find('findSuitableShell(') + 200],
'has_execFileSync': 'execFiles ync' in text or 'execFileSync' in text,
'has_where_exe': 'where.exe' in text,
'has_Bun_spawn_in_Shell': 'Bun.spawn' in text,
}
print(checks)
# Print exact lines with symbols.
for i, line in enumerate(text.splitlines(), 1):
if any(s in line for s in ['findSuitableShell', 'execFileSync', 'where.exe', 'Bun.spawn', 'timeout']):
print(f'{i}: {line}')
PYRepository: claude-code-best/claude-code
Length of output: 50387
Use asynchronous Bun process execution for the PATH lookup.
findSuitableShell() is async, so execFileSync('where.exe', ['bash']) blocks shell discovery until where.exe returns and can hang without a timeout. Replace this lookup with Bun subprocess execution, keep failures ignored, and add a bounded timeout for the lookup.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync, spawn } from 'child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
🤖 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 `@src/utils/Shell.ts` around lines 85 - 88, Update the where.exe lookup inside
findSuitableShell() to use asynchronous Bun subprocess execution instead of
execFileSync, while preserving ignored failures and the existing PATH-detection
behavior. Apply a bounded timeout to the subprocess so shell discovery cannot
hang.
Source: Coding guidelines
问题
Windows 启用 WSL 时,
where.exe bash返回C:\Windows\System32\bash.exe(WSL 启动器)和%LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe(WSL App Execution Alias),位置常在 Git for Windows 的 bash 之前。原findExecutableWithDeps直接返回首个非 cwd 结果,CCB 因此把 WSL 启动器误认为 Git Bash,污染process.env.SHELL与CLAUDE_CODE_GIT_BASH_PATH整个会话,所有 hook / BashTool 调用都会拉起wsl.exe弹窗。修改
src/utils/windowsPaths.ts— 在findExecutableWithDeps内、当executable='bash'时,过滤 System32 与 WindowsApps 的 bash.exe 候选。continue到下一个where.exe命中,让 PATH 后续的合法 Git Bash 仍能胜出。下游消费者(setShellIfWindowsenv 传播、hooks.tsspawn 点、Shell.tsSHELL 分支)通过 env 缓存自动受益。src/utils/Shell.ts—findSuitableShell启动入口:检测到 WSL bash 但无 Git for Windows 且未设CLAUDE_CODE_GIT_BASH_PATHoverride 时,输出一次性 warning。src/utils/doctorDiagnostic.ts— 相同条件的claude doctor持久诊断项,方便用户随时复查。Test plan
src/utils/__tests__/windowsPaths.test.ts):nullnullwhere.exe命中(Git Bash later in PATH)bun run precheck通过:typecheck 0 errors / biome 0 fixes / 5987 pass / 0 fail / 10 skipwsl.exe弹窗claude doctor输出对应 warning 项🤖 Generated with Claude Code Best
Summary by CodeRabbit