Gatekeeper: look before you install
Installing a skill is not like installing a library. A skill loads a stranger's
instructions into the agent's context and ships scripts that run with the
user's own permissions. The SKILL.md can tell the agent what to do; the
scripts can do anything the user can.
The catalogs make this a supply chain. Collections advertise hundreds of skills; one compromised entry is enough. This skill checks one before it is trusted.
What it cannot do — read this first
A clean report is not a safety guarantee, and this tool is not a sandbox.
It reduces risk; it does not remove it. Three honest limits, stated up front because a false sense of safety is more dangerous than none:
- It is triage, not proof. It surfaces evidence for a human to weigh. It does not certify anything.
- Prose findings are adversarial. A determined attacker rewords things to dodge keyword matching. This catches the careless and the opportunistic, not a targeted attack written to beat it.
- The real defences are elsewhere. Don't install what you haven't read; run untrusted skills in a container; keep permissions narrow. Gatekeeper is the first filter, not the last.
One more, learned the hard way: a security scanner's own source contains every threat pattern it searches for, so it matches itself and any other security tool. Gatekeeper excludes itself by identity (not by suppressing rules), but when scanning another scanner, expect its pattern definitions to trip these rules — read those findings as pattern definitions, not behaviour.
The one idea that makes it work
The same evidence means opposite things depending on the skill's purpose.
- A
curlin an API-testing skill is its job. In a Markdown formatter it is a red flag. - Reading
~/.sshin an SSH-key manager is expected. In a CSV tidier it is theft.
So a scanner that judges patterns in isolation is useless — it either buries the user in false positives or waves the dangerous case through. The question is never "does it do X", it is:
Does what it does exceed what it claims to do?
That comparison is the work, and it is the part a person (or agent) must do. The tool's job is to lay the two side by side.
Step 1: get the evidence
scripts/gatekeeper.py ~/.claude/skills/the-skill # one skill
scripts/gatekeeper.py --all ~/.claude/skills # every installed skill
scripts/gatekeeper.py --json path/to/skill # for further processing
Standard library only. It reads files and runs regexes — it never executes the skill, so it is safe to point at something untrusted.
It prints the skill's own declared purpose, then what it found, grouped by category with file and line. Three verdicts:
| Verdict | Meaning | Exit |
|---|---|---|
PASS |
nothing notable | 0 |
REVIEW |
findings to weigh against the claim | 1 |
REFUSE |
a combination with no benign reading | 2 |
Only mechanical, hard-to-argue combinations auto-REFUSE (reads credentials
and reaches the network; obfuscation wrapping either). Everything at the
instruction level is REVIEW — because the scanner genuinely cannot tell an
attack from a skill that documents one, and guessing wrong in either direction
destroys its usefulness.
Step 2: weigh each finding against the claim
This is the judgement the tool sets up but does not make. For every finding:
- Read the declared purpose printed at the top.
- Ask if the finding is consistent with it. A deploy skill that reads
~/.aws/credentials— plausible. A "poem generator" that does — stop. - Follow the worst combinations first. Credentials + network is the classic
exfiltration pair. Obfuscation is rarely innocent. Anything writing to
.claude/settings.jsoncan widen its own permissions.
The categories it reports:
| Category | Why it matters |
|---|---|
| reads credentials | keys, tokens, cookies — rarely needed unless it is an auth tool |
| sends data out | network egress — the exit route for anything it read |
| hides what it does | base64/eval/hex — legitimate tools are readable |
| installs itself | shell rc, cron, git hooks — outlives the task |
| changes permissions | edits .claude/settings.json / MCP config — can free itself |
| destroys data | recursive/forced deletes, force-push, history wipe |
| instruction-level | text aimed at the agent: overrides, secrecy, "run on every turn", permission grabs |
Step 3: decide, and record why
- REFUSE — do not install. The exfiltration pattern has no good explanation.
- REVIEW — read the flagged lines in full. If each has an innocent reading consistent with the purpose, proceed; if one does not, treat it as REFUSE.
- PASS — lowest risk, still not a guarantee. For anything that will run with real permissions, skim the scripts yourself regardless.
Report the verdict, the findings that decided it, and — for a REVIEW that was cleared — why each flag was benign. "Cleared after reading" with a reason is a decision; a silent pass is not.
When the skill is on GitHub, not yet installed
Clone it somewhere disposable and scan there. Do not install first.
git clone --depth 1 https://github.com/owner/skill /tmp/inspect-me
scripts/gatekeeper.py /tmp/inspect-me
Cloning runs nothing. Installing into ~/.claude/skills makes it live. Keep that
order.
Resources
scripts/gatekeeper.py— the scanner. Categories, combination logic, and the three verdicts.--allfor a directory,--jsonfor pipelines,-vfor every hit untruncated.references/threats.md— each category expanded: what a real attack looks like, why the benign version is common, and how to tell them apart. Plus the limits of static scanning and what genuine isolation requires.
Related: this is the install-time companion to the rest of the evidence plugin — the same refusal to accept "it's probably fine" without looking.