Skip to content

Instantly share code, notes, and snippets.

@nna774
Created May 27, 2026 04:00
Show Gist options
  • Select an option

  • Save nna774/82113817a74bff1bedec498d83f30ec6 to your computer and use it in GitHub Desktop.

Select an option

Save nna774/82113817a74bff1bedec498d83f30ec6 to your computer and use it in GitHub Desktop.
Claude Code PreToolUse hook: block write-method gh api calls
#!/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