Created
May 27, 2026 04:00
-
-
Save nna774/82113817a74bff1bedec498d83f30ec6 to your computer and use it in GitHub Desktop.
Claude Code PreToolUse hook: block write-method gh api calls
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
| #!/usr/bin/env python3 | |
| """PreToolUse hook: block write-method `gh api` calls. | |
| Allows GET-only `gh api` invocations to pass through to the normal permission | |
| check. Blocks any invocation that uses: | |
| - -X / --method with a non-read method (POST/PUT/PATCH/DELETE), | |
| including attached short form like -XPOST | |
| - -f / -F / --field / --raw-field (gh implicitly POSTs when these are set), | |
| including attached short form like -ftitle=foo | |
| - --input (reads body from file, implies POST) | |
| Exits with 2 to deny the tool call and send the reason back to Claude. | |
| """ | |
| import json | |
| import shlex | |
| import sys | |
| WRITE_METHODS = {"POST", "PUT", "PATCH", "DELETE"} | |
| FIELD_FLAGS = {"-f", "-F", "--field", "--raw-field"} | |
| SEPARATORS = {"|", "||", "&&", ";", "&", "|&"} | |
| def block(reason: str) -> None: | |
| print(f"gh api write blocked: {reason}", file=sys.stderr) | |
| sys.exit(2) | |
| def main() -> None: | |
| try: | |
| data = json.load(sys.stdin) | |
| except json.JSONDecodeError: | |
| sys.exit(0) | |
| if data.get("tool_name") != "Bash": | |
| sys.exit(0) | |
| cmd = data.get("tool_input", {}).get("command", "") | |
| try: | |
| tokens = shlex.split(cmd, posix=True) | |
| except ValueError: | |
| sys.exit(0) | |
| i = 0 | |
| while i < len(tokens): | |
| if tokens[i] == "gh" and i + 1 < len(tokens) and tokens[i + 1] == "api": | |
| j = i + 2 | |
| while j < len(tokens) and tokens[j] not in SEPARATORS: | |
| t = tokens[j] | |
| if t in ("-X", "--method") and j + 1 < len(tokens): | |
| if tokens[j + 1].upper() in WRITE_METHODS: | |
| block(f"{t} {tokens[j + 1]}") | |
| elif t.startswith("--method="): | |
| if t.split("=", 1)[1].upper() in WRITE_METHODS: | |
| block(t) | |
| elif t.startswith("-X") and len(t) > 2: | |
| if t[2:].upper() in WRITE_METHODS: | |
| block(t) | |
| elif t in FIELD_FLAGS or t.startswith(("--field=", "--raw-field=")): | |
| block(f"{t} (implies POST)") | |
| elif t.startswith(("-f", "-F")) and len(t) > 2: | |
| block(f"{t} (implies POST)") | |
| elif t == "--input" or t.startswith("--input="): | |
| block(f"{t} (implies POST)") | |
| j += 1 | |
| i = j | |
| else: | |
| i += 1 | |
| sys.exit(0) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment