Skip to content

Snippet: Env Guard Hook for AGY (Antigravity)

An Antigravity (AGY) pre_run hook to secure your local workspace.

snippet
dev-tools
security
ai

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))
'

Get new posts by email

New writing in your inbox. Unsubscribe anytime.