From 2cbb10c959d1693f0c5e04b29bbae2ee16a89c6f Mon Sep 17 00:00:00 2001 From: Luong NGUYEN Date: Tue, 7 Apr 2026 08:53:32 +0200 Subject: [PATCH] fix(hooks): Windows Git Bash compatibility + stdin JSON protocol (#55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(hooks): make hook scripts compatible with Windows Git Bash and use stdin JSON protocol - Replace `grep -P` (Perl regex) with `sed` for JSON field extraction, as Windows Git Bash does not support `grep -P` - Replace positional arg (`$1`) with stdin JSON parsing to match the actual Claude Code hook protocol (hooks receive data via stdin, not args) - Fix JSON double-quote escaping in grep patterns that silently fails on Windows Git Bash - Remove python3 dependency for JSON parsing, making scripts portable - Add Windows Git Bash to compatibility notes in script headers Affected scripts: security-scan.sh, validate-prompt.sh, log-bash.sh, format-code.sh Co-Authored-By: Claude Opus 4.6 * fix(hooks): address review issues in Windows Git Bash compatibility scripts - security-scan.sh: wrap output in hookSpecificOutput format required by PostToolUse protocol; restore secret/token detection pattern that was dropped; escape file path and issues for safe JSON construction - validate-prompt.sh: extract "user_prompt" field first (Claude Code UserPromptSubmit protocol), falling back to "prompt" - log-bash.sh: document sed truncation limitation for commands with double-quoted strings - format-code.sh: fix misleading comment about updatedInput output * fix(hooks): produce valid JSON from security-scan.sh - Use \\n (JSON newline escape) when building ISSUES, not real newlines, so the value passes safely through printf into the JSON string - Remove the real-newline-to-\\n sed pass (no longer needed) - Output is now verifiably valid JSON per python3 json.loads * fix(hooks): fix grep -E POSIX compat and semgrep/trufflehog stdout mixing - Replace \s with [[:space:]] in grep -E patterns (security-scan.sh lines 34,44): \s is a Perl extension, silently fails on macOS BSD grep and BusyBox — password/secret detection was a no-op on macOS - Suppress stdout of semgrep/trufflehog (>/dev/null) to prevent their output mixing with hookSpecificOutput JSON, which would produce invalid JSON and cause the additionalContext to fail parsing - Fix format-code.sh hook comment: PreToolUse → PostToolUse (matches README example and actual hook behavior) --------- Co-authored-by: Bruce Co-authored-by: Claude Opus 4.6 --- 06-hooks/format-code.sh | 7 +++---- 06-hooks/security-scan.sh | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/06-hooks/format-code.sh b/06-hooks/format-code.sh index 71bb7c3..9841b9e 100644 --- a/06-hooks/format-code.sh +++ b/06-hooks/format-code.sh @@ -1,10 +1,9 @@ #!/bin/bash -# Auto-format code before writing -# Hook: PreToolUse:Write +# Auto-format code after writing +# Hook: PostToolUse:Write # # Reads the target file path from stdin JSON and runs the appropriate formatter -# in-place on the existing file (if it already exists on disk). For new files, -# formatting runs after Claude writes the file via a subsequent PostToolUse hook. +# in-place on the file after Claude writes it. # # Compatible with: macOS, Linux, Windows (Git Bash) diff --git a/06-hooks/security-scan.sh b/06-hooks/security-scan.sh index cddc0c5..81e3a5b 100644 --- a/06-hooks/security-scan.sh +++ b/06-hooks/security-scan.sh @@ -31,7 +31,7 @@ ISSUES="" # Use \\n as separator — it is a valid JSON newline escape and passes through printf safely if grep -qiE '"password"[[:space:]]*:[[:space:]]*"[^"]+"' "$FILE_PATH" 2>/dev/null; then ISSUES="${ISSUES}- WARNING: Potential hardcoded password detected\\n" -elif grep -qiE '(password|passwd|pwd)\s*=\s*'"'"'[^'"'"']+'"'"'' "$FILE_PATH" 2>/dev/null; then +elif grep -qiE '(password|passwd|pwd)[[:space:]]*=[[:space:]]*'"'"'[^'"'"']+'"'"'' "$FILE_PATH" 2>/dev/null; then ISSUES="${ISSUES}- WARNING: Potential hardcoded password detected\\n" fi @@ -41,7 +41,7 @@ if grep -qiE '"(api[_-]?key|apikey|access[_-]?token)"[[:space:]]*:[[:space:]]*"[ fi # Check for hardcoded secrets and tokens -if grep -qiE '(secret|token)\s*=\s*['"'"'"][^'"'"'"]+['"'"'"]' "$FILE_PATH" 2>/dev/null; then +if grep -qiE '(secret|token)[[:space:]]*=[[:space:]]*['"'"'"][^'"'"'"]+['"'"'"]' "$FILE_PATH" 2>/dev/null; then ISSUES="${ISSUES}- WARNING: Potential hardcoded secret or token detected\\n" fi @@ -55,14 +55,14 @@ if grep -qE "AKIA[0-9A-Z]{16}" "$FILE_PATH" 2>/dev/null; then ISSUES="${ISSUES}- WARNING: AWS access key detected\\n" fi -# Scan with semgrep if available +# Scan with semgrep if available (stdout suppressed to avoid mixing with JSON output) if command -v semgrep &> /dev/null; then - semgrep --config=auto "$FILE_PATH" --quiet 2>/dev/null + semgrep --config=auto "$FILE_PATH" --quiet >/dev/null 2>/dev/null fi -# Scan with trufflehog if available +# Scan with trufflehog if available (stdout suppressed to avoid mixing with JSON output) if command -v trufflehog &> /dev/null; then - trufflehog filesystem "$FILE_PATH" --only-verified --quiet 2>/dev/null + trufflehog filesystem "$FILE_PATH" --only-verified --quiet >/dev/null 2>/dev/null fi # If issues found, output as additionalContext (non-blocking warning)