sync: auto-sync from GURU-5070 at 2026-06-02 20:40:54
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-06-02 20:40:54
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
# PreToolUse(Bash) hook: block bash commands that REDIRECT/WRITE to a backslashed
|
||||
# Windows drive path (e.g. `echo x > D:\claudetools\.claude\current-mode`).
|
||||
#
|
||||
# Dual-driver: works when invoked from Claude Code (.claude/settings.json) or
|
||||
# from Grok (.grok/hooks/*.json PreToolUse). The caller normalizes stdin JSON
|
||||
# shape (tool_input vs toolInput) and the script emits Grok decision JSON when
|
||||
# it detects a Grok-shaped event and needs to deny.
|
||||
#
|
||||
# Why: under Git Bash / MSYS, a backslash is an escape char. `> D:\foo\bar`
|
||||
# strips the backslashes and substitutes the illegal ':' with the Unicode
|
||||
# Private-Use char U+F03A, creating a garbled junk file in the CWD instead of
|
||||
@@ -12,7 +17,25 @@
|
||||
# `icacls "D:\Homes"` or `pwsh -File C:\x.ps1` are NOT redirects, so they pass.
|
||||
|
||||
input=$(cat)
|
||||
cmd=$(echo "$input" | jq -r '.tool_input.command // ""' 2>/dev/null)
|
||||
# Support both Claude (tool_input / tool_input.command) and Grok (toolInput / toolInput.command + hookEventName) event shapes.
|
||||
# Prefer jq; fallback to python (avoids repeated "jq: command not found" or parse errors if jq missing in env).
|
||||
cmd=$(echo "$input" | jq -r '(.toolInput // .tool_input // {}) | .command // ""' 2>/dev/null || python -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.load(sys.stdin)
|
||||
ti = d.get('toolInput') or d.get('tool_input') or {}
|
||||
print(ti.get('command', ''))
|
||||
except:
|
||||
print('')
|
||||
" 2>/dev/null || echo '')
|
||||
is_grok=$(echo "$input" | jq -r 'if has("hookEventName") or has("toolInput") then "1" else "0" end' 2>/dev/null || python -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.load(sys.stdin)
|
||||
print('1' if ('hookEventName' in d or 'toolInput' in d) else '0')
|
||||
except:
|
||||
print('0')
|
||||
" 2>/dev/null || echo '0')
|
||||
|
||||
# Strip quoted substrings first, so the pattern appearing INSIDE a string or a
|
||||
# commit message (e.g. git commit -m "... > D:\\path ...") does not false-trigger.
|
||||
@@ -22,6 +45,7 @@ bare=$(printf '%s' "$cmd" | sed -E "s/'[^']*'//g; s/\"[^\"]*\"//g")
|
||||
|
||||
# Block when, after quote-stripping, a redirect/tee writes to a bareword X:\ path.
|
||||
if printf '%s' "$bare" | grep -qiP '(>>?|tee)\s*[A-Za-z]:\\'; then
|
||||
reason="Blocked redirect/write to backslashed Windows path in bash (Git Bash would garble it via PUA substitution)."
|
||||
echo "BLOCKED: do not redirect/write to a backslashed Windows path in bash."
|
||||
echo ""
|
||||
echo "Git Bash strips the backslashes and PUA-substitutes ':', creating a"
|
||||
@@ -31,6 +55,10 @@ if printf '%s' "$bare" | grep -qiP '(>>?|tee)\s*[A-Za-z]:\\'; then
|
||||
echo " - relative path: echo dev > .claude/current-mode"
|
||||
echo " - MSYS forward-slash: echo dev > /d/claudetools/.claude/current-mode"
|
||||
echo " - drive forward-slash: echo dev > D:/claudetools/.claude/current-mode"
|
||||
if [ "$is_grok" = "1" ]; then
|
||||
# Emit Grok PreToolUse decision format so the hook runner can deny cleanly.
|
||||
printf '{"decision":"deny","reason":"%s"}\n' "$reason"
|
||||
fi
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user