← Claude Code Hooks Security Check
Claude Code Hooks Security Check vs. reading the hooks config by eye
Reading a short hooks config with one obvious curl-to-shell download catches that download. It reliably misses a credential read and network send split across a pipe, an environment dump piped to curl, a base64-decoded payload piped to a shell, and a broad rm -rf buried inside a longer chain of statements — which is what this free, no-upload checker computes exactly, in one pass.
Where reading it yourself stops being enough
Anyone about to trust a hooks configuration already does this: open settings.json, read each
hook's command string, and decide whether it looks right before letting it fire automatically
and unattended. For a single hook with an obviously broad curl | bash, that read catches it.
The problem is the other ten checks below are not the kind of thing a skim of a rendered command string
reliably surfaces, especially once a config carries several hooks across several events.
| What you're trying to confirm | Reading the config yourself | This checker |
|---|---|---|
A command pipes a curl/wget download straight into a shell interpreter, or downloads with PowerShell and executes it with Invoke-Expression. | Works. This is the one case a plain read reliably catches. | Also catches it, in the same pass as everything below. |
A command reads a credential path (SSH key, AWS credentials, .npmrc, kube config, and similar) and separately sends data out over the network, anywhere in the same command line. | Easy to miss when the read and the send sit on opposite ends of a long piped or chained command with ordinary-looking steps between them. | Matched as a pair — a sensitive path plus a network-send shape — in the same command, regardless of where each half sits. |
Environment variables dumped (env/printenv/export -p) and piped or sent to a network command in the same line. | The dump and the send can be several tokens apart in a chained command; easy to read each half as unremarkable on its own. | Checked as a pair against the same command line, every time. |
An unguarded recursive delete (rm -rf or equivalent flags) targeting a broad path like /, ~, or $HOME, possibly third or fourth in a &&/;-separated chain. | Easy to skim past while reading left to right for the command's obvious purpose. | Every statement in the chain split and checked for the same broad-delete shape. |
TLS/certificate verification disabled on a request (curl -k, NODE_TLS_REJECT_UNAUTHORIZED=0, and similar), which can sit as one flag among many. | A single flag buried in a long argument list is easy to read past. | Matched against the exact flag/variable shapes across every command, every time. |
| A base64-decoded payload piped straight into a shell — the dangerous part is hidden inside an encoded blob, not written as a readable command. | Reads as an unremarkable-looking pipe unless you already know to treat decode-then-execute as the same risk as a plain download-to-shell. | Flags the decode-then-execute pipe shape itself, with no need to decode or run anything. |
| Persistence via a shell startup file, crontab, or user service, so the command runs again later independent of this hook firing again. | Requires noticing an append (>>) to a dotfile or a crontab/systemctl --user call buried in a longer line. | Matched against the specific persistence-target shapes, every time. |
World-writable permissions granted (chmod 777/a+w/o+w) and a process started detached from the hook (nohup/setsid/tmux -d and similar). | Each is a short, easy-to-miss flag in a longer argument list. | Checked against the exact flag shapes, across every command in the config. |
| Re-checking an entire hooks config again after adding or editing one hook. | One hook at a time, remembered by hand, every time something changes. | One paste, every command, every time. |
A quick read is still the fastest way to catch an obviously dangerous command in a short config, and this checker is not doing anything a careful line-by-line audit couldn't — it is reading the same command strings a careful audit would, just checking every one against eleven fixed shapes automatically instead of leaving the comparison to be remembered by hand across however many hooks the config has.
This tool needs no account and no sign-in: the browser page parses JSON you paste entirely in your own browser. It never sends your hooks configuration anywhere.
Common questions
Can't I just read the hooks config myself?
For a short config with one hook and an obvious curl-piped-to-bash download, yes -- that case is visible on a single read. What reading does not reliably catch is a credential file read and a network send that live in different parts of the same command line, an environment-variable dump piped to curl, a base64-decoded payload piped to a shell, or a broad rm -rf buried inside a longer chain of && and ; separated statements. Those need the whole command line checked against eleven separate shapes the same mechanical way every time, not remembered while skimming a settings.json with a dozen hooks in it.
Why is credential exfiltration easy to miss on a read-through?
Because the read of a sensitive path like ~/.ssh/id_rsa or ~/.aws/credentials and the curl -d or scp that sends it out can sit on opposite ends of a long piped or chained command, with ordinary-looking steps in between. A reviewer's eye catches either half in isolation easily; confirming both halves are present in the same command line, for every command in the config, is the kind of pairing a mechanical check does the same way every time and a skim does not.
Why is a base64-decoded payload piped to a shell hard to spot?
Because the dangerous part -- what actually runs -- is hidden inside an encoded blob, not written out as a readable command. The visible text is just "base64 -d | bash" or similar, which looks unremarkable next to a curl-to-shell download unless you already know to treat any decode-then-execute pipe as the same risk shape. This tool flags the pipe shape itself, without needing to decode or run the payload.
What about a broad rm -rf buried in a longer command?
A hook command chained with && or ; can run several statements in sequence, and an unguarded rm -rf ~ or rm -rf $HOME sitting third or fourth in that chain is easy to skim past while reading left to right for the command's obvious purpose. Splitting every command on its statement separators and checking each part for a broad, unguarded recursive delete is a mechanical pass through every clause, not a read of the parts that look load-bearing.
Is this page making a live comparison against a tool or company?
No. This page describes a manual habit anyone reviewing a Claude Code hooks configuration already has -- reading each hook's command text by eye before trusting it to fire unattended. It names no competing product or company. The checker itself makes no network request of any kind: the browser page parses pasted JSON in your own browser, and nothing you paste is ever uploaded.
Want to know what a zero-finding scan actually means? Read why a clean scan isn't a safety guarantee.