The Antigravity CLI (AGY) supports hooks.json to intercept tool calls. We can use a PreToolUse hook running a Python script to deeply inspect arguments and deny requests that try to read or rewrite sensitive credentials.
First, register the hook in your ~/.gemini/config/hooks.json:
{
"safety-gate": {
"PreToolUse": [
{
"hooks": [
{
"command": "bash ~/.gemini/config/scripts/guard-env.sh",
"timeout": 5,
"type": "command"
}
],
"matcher": "run_command|view_file|grep_search|replace_file_content"
}
]
}
}Then, place the python logic inside ~/.gemini/config/scripts/guard-env.sh:
#!/usr/bin/env bash
python3 -c '
import sys, json, os, re
SAFE_ENV_NAMES = (
".env.example", ".env.template", ".env.sample", ".env.schema",
"env.ts", "env.mjs", "env.js", "env.d.ts"
)
def is_sensitive_filename(path_str):
if not path_str: return False
basename = os.path.basename(path_str.strip("\x27\x22 ")).lower()
for safe in SAFE_ENV_NAMES:
if basename == safe or basename.startswith(safe): return False
if basename == ".env" or basename.startswith(".env."): return True
if basename == ".dev.vars" or basename.startswith(".dev.vars."): return True
if basename.endswith(".pem") or basename.endswith(".key"): return True
if basename in ("id_rsa", "id_ed25519", "id_ecdsa", "id_dsa"): return True
return False
def check_command_security(cmd_str):
if not cmd_str: return False, "", None
cmd = cmd_str.strip()
if re.search(r"(^|[;&|]\s*)(printenv|export -p)(\s*([;&|]|$))", cmd, re.IGNORECASE):
return True, "SECURITY BLOCKED: Direct access to secrets forbidden.", None
if re.search(r"(^|[;&|]\s*)env(\s*([;&|]|$))", cmd, re.IGNORECASE):
return True, "SECURITY BLOCKED: Direct access to secrets forbidden.", None
file_access_pattern = r"(cat|less|more|head|tail|source|\.|\/bin\/cat|\/usr\/bin\/cat|bat|view|nano|vim|vi|grep|awk|sed|cp|mv|scp|rsync)\s+([^;&|]*)"
for match in re.finditer(file_access_pattern, cmd, re.IGNORECASE):
args_segment = match.group(2)
for token in re.split(r"[\s=]+", args_segment):
if is_sensitive_filename(token.strip("\x27\x22()[]{}")):
return True, "SECURITY BLOCKED: File access command on sensitive file.", None
# Safe rg injection
if re.search(r"(^|[;&|]\s*)rg\s", cmd):
safe_cmd = re.sub(r"(^|[;&|]\s*)rg\s", r"\g<1>rg --glob \"!.env*\" --glob \"!.dev.vars\" ", cmd)
if safe_cmd != cmd: return False, "", safe_cmd
return False, "", None
try:
data = json.load(sys.stdin)
except Exception:
print(json.dumps({"decision": "allow"}))
sys.exit(0)
tool = data.get("toolCall", {})
name = tool.get("name", "")
args = tool.get("args", {})
denied, reason, overwrite = False, "", None
if name in ("view_file", "write_to_file", "replace_file_content"):
target = args.get("AbsolutePath", args.get("TargetFile", ""))
if is_sensitive_filename(target):
denied, reason = True, f"SECURITY BLOCKED: Direct access to {os.path.basename(target)} is forbidden."
elif name == "run_command":
denied, reason, safe_cmd = check_command_security(args.get("CommandLine", ""))
if not denied and safe_cmd:
overwrite = {"CommandLine": safe_cmd}
if denied:
print(json.dumps({"decision": "deny", "reason": reason}))
else:
res = {"decision": "allow"}
if overwrite: res["overwrite"] = overwrite
print(json.dumps(res))
'