Skip to content

About

A seatbelt for coding agents: stops them running what an email, web page or issue tells them to.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

clickfix-guard

Your agent reads your email. Don't let it run what the email says.

Blocks the common download-and-run moves (curl | bash, running files from Downloads, installers, stripping macOS quarantine) and anything macOS flagged as downloaded. A seatbelt, not a sandbox. Known bypasses listed.

Claude Code in bypass mode sets up a downloaded project, reads its setup script, decides it is harmless and runs it; clickfix-guard blocks it because macOS flagged the file as downloaded

Real Claude Code session in bypass mode; only the idle start and end are trimmed. Asked to set up a downloaded project, the agent reads the setup script, decides it is harmless, and runs it. clickfix-guard stops it because macOS flagged the file as downloaded. The script here only prints two lines; in a real attack it would not look any different.

Why

ClickFix is the phishing trick where a page or an email tells you to "fix" something by pasting a command into Terminal. It is how a lot of macOS malware (AMOS and friends) gets installed. Coding agents now read email, issues, READMEs and web pages, and they are very good at following setup instructions. The same trick works on them, with no human in the loop to hesitate. Researchers have already shown it against Claude Code: in 0DIN's proof of concept (June 2026) a repo's README walked the agent into a setup script that fetched a reverse shell from a DNS record.

Honest note: clickfix-guard would not have stopped that exact chain. The payload was fetched inside a script, into a variable, and a command-line check does not see that (see KNOWN-BYPASSES.md). It stops the simpler and far more common moves: the pasted one-liner, the downloaded script, the installer, the quarantine strip.

In bypass or auto mode, your agent's own judgment is often the only thing between an injected instruction and a shell. clickfix-guard adds a deterministic check in front of every shell command. It adds roughly 150 to 200 ms per command.

What it blocks

Move Example
Download piped into a shell or interpreter curl ... | bash, wget -qO- ... | python3, bash <(curl ...), eval "$(curl ...)", sh -c "$(curl ...)"
Inline code that downloads and executes python3 -c "exec(urlopen(...).read())", php -r "eval(file_get_contents('https://...'))"
Download, then run the same file curl -o /tmp/x.sh ... && bash /tmp/x.sh, and later in the same session
Anything macOS flagged as downloaded (macOS only) a script saved from Safari or Mail, even after it was copied or unzipped elsewhere (com.apple.quarantine)
Running files from the Downloads folder bash ~/Downloads/fix.sh, chmod +x ~/Downloads/x, open ~/Downloads/x.command (localized folder names too)
Gatekeeper bypass xattr -d com.apple.quarantine, xattr -c, spctl --master-disable
Installers and disk images installer -pkg, hdiutil attach, open x.dmg, dpkg -i, apt install ./x.deb, rpm -i
AppleScript shell escapes osascript -e 'do shell script ...', JXA doShellScript

On Linux every rule runs except the quarantine check, which is a macOS feature.

What still works: curl api | jq, curl api | python3 -c 'import json...', downloading files, reading and copying files in Downloads, opening PDFs and documents (even quarantined ones), running your own project scripts.

Install

Claude Code

/plugin marketplace add better-isms/clickfix-guard
/plugin install clickfix-guard@clickfix-guard

Codex CLI

Clone the repo somewhere stable, then add to ~/.codex/hooks.json:

{
  "hooks": {
    "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "/path/to/clickfix-guard/scripts/clickfix-guard --harness codex", "timeout": 10 }] }],
    "PostToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "/path/to/clickfix-guard/scripts/clickfix-guard --harness codex --post", "timeout": 10 }] }]
  }
}

Then run /hooks in Codex once and trust the two hooks. Codex skips hooks you have not trusted.

Grok Build

Put the same JSON as above in ~/.grok/hooks/clickfix-guard.json, with --harness grok. Hooks in ~/.grok/hooks/ are always trusted, so there is no trust step. Check with grok inspect: it lists two matcher=Bash hooks. Grok also reads the hooks in ~/.claude/settings.json and can discover Claude plugins; those routes are not live-tested, the explicit file is.

Verify, don't pipe

There is deliberately no curl | bash installer. Clone with git, read scripts/clickfix-guard (one bash file), and pin a tag. Needs bash, jq, grep, sed, awk. Without jq it falls back to matching the raw event and says so on stderr.

State and uninstall

The session tracker writes the paths of files the agent downloaded to ~/.local/state/clickfix-guard/ (mode 700, pruned after 7 days). Nothing leaves your machine.

Uninstall: /plugin uninstall clickfix-guard@clickfix-guard in Claude Code; remove the two hook entries from ~/.codex/hooks.json or ~/.grok/hooks/clickfix-guard.json; delete ~/.local/state/clickfix-guard/.

Supported agents

Agent Status Tested with
Claude Code supported, blocks and asks 2.1.284, real CLI
Codex CLI supported, blocks (Codex has no "ask") 0.153.2, real CLI
Grok Build supported, blocks (asks only for allowlisted installers, in prompting modes) 1.0.46, real CLI (~/.grok/hooks/ route)
Gemini CLI, Cursor, Copilot CLI coming in v1.1
OpenCode, Amp, Cline not yet open an issue

Modes

Set CLICKFIX_GUARD in the agent's environment.

Mode Behaviour
auto (default) Claude Code in interactive modes (default, acceptEdits, plan): asks you. Bypass mode, auto mode, unknown modes and agents without "ask": blocks.
block Always blocks. Ignores the allowlist. For unattended agents that read untrusted input.
ask Asks where the agent supports it (Claude Code; Grok in prompting modes), blocks elsewhere.
warn Allows, prints a warning.
off Does nothing.

Allowlist

Official installers that are meant to be piped into a shell (Homebrew, rustup, uv) are in allowlist.default. The allowlist never allows anything on its own: it turns a refusal into a question for you. So it only matters where the agent can ask: Claude Code, and Grok in its prompting modes (default, acceptEdits, plan). Grok approves an "ask" by itself under always-approve, and Codex has no ask, so there allowlisted installers are refused like everything else; run them yourself. CLICKFIX_GUARD=block ignores the allowlist. Add your own in ~/.config/clickfix-guard/allow.txt:

# host [path]  (exact https host; exact path, or a directory prefix ending in /)
get.example.dev /install.sh

URLs are parsed, not prefix-matched, and a bare host or / means the root path only. Lookalike hosts (raw.githubusercontent.com.evil.io), embedded credentials (raw.githubusercontent.com@evil.io) and plain http:// never match. The allowlist applies only when the whole command is one installer one-liner (curl with plain flags and one URL, piped into a shell, or sh -c "$(curl ...)"); anything else on the line and the normal rules apply.

What it is not

  • Not a sandbox. It reads the command line. An agent that knows it exists, or an attacker who writes a script to disk and runs it later, can get past it. See KNOWN-BYPASSES.md.
  • Not phishing protection in general. It does not stop an agent from typing a password into a fake page, sending data out, or paying a fake invoice.
  • Layer it. For sessions that read untrusted input, also use your agent's sandbox (Claude Code /sandbox, Codex's default sandbox) or auto mode. See THREAT-MODEL.md.

False positives

Benchmark: replayed against the unique shell commands from two weeks of real agent sessions on one developer's Mac. See THREAT-MODEL.md for the numbers. Known false positives: inline code that merely mentions both a download call and an exec call (for example python3 -c "print('urlopen exec(')"); commands over 64 KB, which are refused rather than checked; and, rarely, running a file that a failed curl -o pointed at within ten minutes of it being modified. If it blocks something legitimate, open an issue with the command (redacted), and use warn or the allowlist meanwhile.

Development

bash tests/run.sh

Every rule change needs a test. Every known bypass is pinned as a test that currently passes through, so a fix flips it on purpose.

License

MIT. Built by the team behind Aevral, the AI security reviewer for code written with coding agents.

About

A seatbelt for coding agents: stops them running what an email, web page or issue tells them to.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages