tool-gates scans write/edit bodies for 28 anti-patterns organised into three tiers, including Claude Write/Edit, Codex apply_patch added lines, Antigravity write_to_file/replace_file_content/multi_replace_file_content, and Gemini write_file/replace before-tool checks. The hard floor denies source writes before the file ever lands, while documentation files get a post-write warning. The middle tier nudges the assistant after a write so the next action can self-correct. The top tier informs without blocking.
Why Tier 2 nudges after the write
Self-correction beats re-prompting.
Tier 2 patterns let the write succeed, then attach a <system-reminder> on additionalContext. Claude and Codex see the warning in the next turn and can edit the file before doing anything else. Gemini and Antigravity install no post hook, so Tier 2 nudges are unavailable there. Gemini still receives Tier 1 denies and displays Tier 3 warnings through BeforeTool systemMessage; Antigravity applies Tier 1 denies on PreToolUse but cannot carry Tier 3 context. No wasted Write call from blocking-then-retrying. Each (file, rule) pair fires at most once per session.
Tier 1 · Hard-coded secrets
source deny · docs warn
hardcoded_aws_key
Block
Hardcoded AWS access key detected. Use environment variables or a secrets manager instead. Never commit AWS keys to source code.
hardcoded_private_key
Block
Private key detected in file content. Private keys must never be committed to source code. Use environment variables, a secrets manager, or file references outside the repo.
hardcoded_github_token
Block
GitHub token detected in file content. Use GITHUB_TOKEN environment variable or gh auth instead. Revoke this token if it was ever committed.
hardcoded_generic_secret
Block
API key or token detected in file content. Use environment variables or a secrets manager. Never hardcode secrets in source code.
github_actions_injection
Block
GitHub Actions workflow injection risk. Untrusted input (issue title, PR body, commit message, head_ref) used directly in a run: block can lead to command injection. UNSAFE: run: echo "${{ github.event.issue.title }}" SAFE: env: TITLE: ${{ github.event.issue.title }} run: echo "$TITLE" See: https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/
Tier 2 · Anti-patterns in code
post-write nudge · PostToolUse
child_process_exec
Nudge
child_process.exec() can lead to command injection. Use child_process.execFile() or child_process.spawn() instead. They don't invoke a shell and prevent argument injection.
new_function_injection
Nudge
new Function() with dynamic strings can lead to code injection. Consider alternative approaches that don't evaluate arbitrary code.
eval_injection
Nudge
eval() executes arbitrary code and is a major security risk. Use JSON.parse() for data parsing, or alternative design patterns that don't require code evaluation.
os_system_injection
Nudge
os.system() passes commands through the shell and is vulnerable to injection. Use subprocess.run() with a list of arguments (no shell=True) instead.
pickle_deserialization
Nudge
pickle can execute arbitrary code during deserialization. Use JSON, msgpack, or other safe serialization formats for untrusted data. Only use pickle with data you fully trust.
dangerous_inner_html
Nudge
dangerouslySetInnerHTML can lead to XSS if used with untrusted content. Sanitize all content with DOMPurify or use safe alternatives like textContent.
document_write_xss
Nudge
document.write() can be exploited for XSS attacks. Use DOM manipulation methods like createElement() and appendChild() instead.
inner_html_assignment
Nudge
Setting innerHTML with untrusted content can lead to XSS. Use textContent for plain text, or sanitize HTML content with DOMPurify.
unsafe_yaml_load
Nudge
yaml.load() without SafeLoader can execute arbitrary Python code. Use yaml.safe_load() or yaml.load(f, Loader=yaml.SafeLoader) instead.
sql_string_interpolation
Nudge
SQL query built with string interpolation is vulnerable to SQL injection. Use parameterized queries (?, %s, :param) instead of f-strings or .format().
subprocess_shell_true
Nudge
subprocess with shell=True is vulnerable to command injection. Pass a list of arguments instead: subprocess.run(["cmd", "arg1", "arg2"]).
flask_ssti
Nudge
render_template_string() with user input can lead to server-side template injection (SSTI). Use render_template() with a file instead, or sanitize all dynamic content.
marshal_deserialization
Nudge
marshal can execute arbitrary code during deserialization. Use JSON or other safe serialization formats for untrusted data.
python_dynamic_import
Nudge
__import__() with dynamic strings can load arbitrary modules. Use static imports or importlib with validated module names.
php_unserialize
Nudge
unserialize() with untrusted data can lead to arbitrary code execution via PHP object injection. Use json_decode() instead.
SSL/TLS verification is disabled. This makes the connection vulnerable to man-in-the-middle attacks. Only disable for local development with self-signed certs.
chmod_777
Allowwarn
chmod 777 / 0o777 grants read+write+execute to all users. Use more restrictive permissions (e.g., 0o755 for dirs, 0o644 for files).
weak_crypto_hash
Allowwarn
MD5/SHA1 are cryptographically broken for security purposes. Use SHA-256+ for integrity checks, bcrypt/argon2 for passwords.
vue_v_html
Allowwarn
v-html renders raw HTML and is vulnerable to XSS with untrusted content. Sanitize content with DOMPurify or use text interpolation {{ }} instead.
template_autoescape_disabled
Allowwarn
Disabling autoescape removes XSS protection from template output. Only disable for content you have already sanitized.
cors_wildcard
Allowwarn
CORS wildcard origin (*) allows any website to make requests. Restrict to specific trusted origins in production.
math_random_security
Allowwarn
Math.random() is not cryptographically secure. Use crypto.getRandomValues() or crypto.randomUUID() for security-sensitive values (tokens, session IDs, nonces).
js_weak_crypto_hash
Allowwarn
MD5/SHA1 are cryptographically broken for security purposes. Use SHA-256+ for integrity checks, bcrypt/scrypt/argon2 for passwords.
Disable individual rules by id when a Tier 2 nudge fires on a legitimate use of (for example) eval() in your codebase.
Tier 1 secret rules are on by default. Disable them by id via disable_rules, or all at once with secrets = false.
Documentation files (.md, .txt, .rst, etc.) are exempt for Tier 2/3 content checks. Tier 1 secrets in source files deny before write; Tier 1 secrets in docs get a PostToolUse warning; dedicated secret files (.env, .envrc, .env.*) skip secret detection because they exist to hold secrets.