From eacf3a9fb4c58999a2f24bf63d37755e5e0196cb Mon Sep 17 00:00:00 2001 From: Agentic-Worker <76669236+cskwork@users.noreply.github.com> Date: Wed, 1 Apr 2026 06:12:09 +0900 Subject: [PATCH] fix(hooks): collapse multi-line commands in bash audit logs (#741) * fix(hooks): collapse multi-line commands in bash audit logs Add gsub("\\n"; " ") to jq filters in bash audit log and cost-tracker hooks so multi-line commands produce single-line log entries, preventing breakage in downstream line-based parsing. Fixes #734 * fix: forward stdin to downstream hooks using echo pattern Addresses review feedback: PostToolUse hooks now preserve stdin for subsequent hooks by echoing $INPUT back to stdout after processing. Changed ; to && for proper error propagation. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: make stdin passthrough unconditional and broaden secret redaction - Use semicolons instead of && so printf passthrough always runs even if jq fails - Add || true after jq to prevent non-zero exit on parse errors - Use printf '%s\n' instead of echo for safe binary passthrough - Fix Authorization pattern to handle 'Bearer ' with space - Add ASIA (STS temp credentials) alongside AKIA redaction - Add GitHub token patterns (ghp_, gho_, ghs_, github_pat_) Co-Authored-By: Claude Opus 4.6 (1M context) * fix: use [: ]* instead of s* for Authorization whitespace matching jq's ONIG regex engine interprets s* as literal 's' zero-or-more, not \s* (whitespace). This caused 'Authorization: Bearer ' to only redact 'Authorization:' and leak the actual token. Using [: ]* avoids the JSON/jq double-escape issue entirely and correctly matches both 'Authorization: Bearer xyz' and 'Authorization:xyz' patterns. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- hooks/hooks.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hooks/hooks.json b/hooks/hooks.json index 8fb52cb3..e54c6a4e 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -153,6 +153,26 @@ } ], "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "#!/bin/bash\nmkdir -p ~/.claude; INPUT=$(cat);\necho \"$INPUT\" | jq -r '\"[\" + (now | todate) + \"] \" + ((.tool_input.command // \"?\") | gsub(\"\n\"; \" \") | gsub(\"--token[= ][^ ]*\"; \"--token=\") | gsub(\"Authorization:[: ]*[^ ]*[: ]*[^ ]*\"; \"Authorization:\") | gsub(\"AKIA[A-Z0-9]{16}\"; \"\") | gsub(\"ASIA[A-Z0-9]{16}\"; \"\") | gsub(\"password[= ][^ ]*\"; \"password=\") | gsub(\"ghp_[A-Za-z0-9_]+\"; \"\") | gsub(\"gho_[A-Za-z0-9_]+\"; \"\") | gsub(\"ghs_[A-Za-z0-9_]+\"; \"\") | gsub(\"github_pat_[A-Za-z0-9_]+\"; \"\"))' >> ~/.claude/bash-commands.log 2>/dev/null || true;\nprintf '%s\n' \"$INPUT\"" + } + ], + "description": "Audit log all bash commands to ~/.claude/bash-commands.log" + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "#!/bin/bash\nmkdir -p ~/.claude; INPUT=$(cat);\necho \"$INPUT\" | jq -r '\"[\" + (now | todate) + \"] tool=Bash command=\" + ((.tool_input.command // \"?\") | gsub(\"\n\"; \" \") | gsub(\"--token[= ][^ ]*\"; \"--token=\") | gsub(\"Authorization:[: ]*[^ ]*[: ]*[^ ]*\"; \"Authorization:\") | gsub(\"AKIA[A-Z0-9]{16}\"; \"\") | gsub(\"ASIA[A-Z0-9]{16}\"; \"\") | gsub(\"password[= ][^ ]*\"; \"password=\") | gsub(\"ghp_[A-Za-z0-9_]+\"; \"\") | gsub(\"gho_[A-Za-z0-9_]+\"; \"\") | gsub(\"ghs_[A-Za-z0-9_]+\"; \"\") | gsub(\"github_pat_[A-Za-z0-9_]+\"; \"\"))' >> ~/.claude/cost-tracker.log 2>/dev/null || true;\nprintf '%s\n' \"$INPUT\"" + } + ], + "description": "Cost tracker - log bash tool usage with timestamps" + }, { "matcher": "Bash", "hooks": [