Created
March 25, 2026 14:45
-
-
Save leoafarias/3ceb297ea47b05d434f0f6ddecfaa6a7 to your computer and use it in GitHub Desktop.
Hook to enforce native tool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -euo pipefail | |
| # Read input from stdin | |
| input=$(cat) | |
| # Extract command (jq required) | |
| command -v jq >/dev/null 2>&1 || exit 0 | |
| cmd=$(echo "$input" | jq -r '.tool_input.command // empty') | |
| [ -z "$cmd" ] && exit 0 | |
| # Strip leading whitespace | |
| cmd="${cmd#"${cmd%%[![:space:]]*}"}" | |
| # Extract first command segment (before any pipe, &&, ||, or ;) | |
| first_segment="${cmd%%|*}" | |
| first_segment="${first_segment%%&&*}" | |
| first_segment="${first_segment%%||*}" | |
| first_segment="${first_segment%%;*}" | |
| # Strip leading env var assignments (e.g., FOO=bar cmd) | |
| clean="$first_segment" | |
| while [[ "$clean" =~ ^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*= ]]; do | |
| clean="${clean#*[[:space:]]}" | |
| done | |
| clean="${clean#"${clean%%[![:space:]]*}"}" | |
| # Get binary name (first word, strip path) | |
| binary="${clean%% *}" | |
| binary="${binary##*/}" | |
| [ -z "$binary" ] && exit 0 | |
| # Unwrap bash -c / sh -c wrappers | |
| if [[ "$binary" == "bash" || "$binary" == "sh" || "$binary" == "zsh" ]]; then | |
| if [[ "$clean" =~ -c ]]; then | |
| inner=$(echo "$clean" | sed -E "s/^[^ ]+ +-c +['\"]?//" | sed -E "s/['\"]?\s*$//") | |
| inner_first="${inner%%|*}" | |
| inner_first="${inner_first%%&&*}" | |
| binary="${inner_first%% *}" | |
| binary="${binary##*/}" | |
| [ -z "$binary" ] && exit 0 | |
| else | |
| exit 0 | |
| fi | |
| fi | |
| # Block and redirect to native tools | |
| deny() { | |
| echo "{\"hookSpecificOutput\": {\"permissionDecision\": \"deny\"}, \"systemMessage\": \"$1\"}" >&2 | |
| exit 2 | |
| } | |
| case "$binary" in | |
| cat) | |
| deny "Use the Read tool instead of 'cat'. It supports offset and limit parameters for partial file reads." ;; | |
| head) | |
| deny "Use the Read tool with the 'limit' parameter instead of 'head'." ;; | |
| tail) | |
| deny "Use the Read tool with 'offset' and 'limit' parameters instead of 'tail'." ;; | |
| grep|rg) | |
| deny "Use the Grep tool instead of '$binary'. It supports regex, glob filters, context lines (-A/-B/-C), and output modes." ;; | |
| find) | |
| deny "Use the Glob tool instead of 'find'. Use patterns like '**/*.ts' for recursive matching." ;; | |
| ls) | |
| deny "Use the Glob tool instead of 'ls'. Use patterns like 'src/*' or '**/*.py'." ;; | |
| sed) | |
| deny "Use the Edit tool for file modifications instead of 'sed'. Use Read with offset/limit for line extraction." ;; | |
| awk) | |
| deny "Use the Grep tool for pattern matching or Edit tool for transformations instead of 'awk'." ;; | |
| *) | |
| exit 0 ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment