Scan an AI agent skill before you install it. skillxray reads a SKILL.md bundle, a Claude Code plugin, an MCP bundle, or a whole folder of them and tells you what's in there: prompt injection, hidden Unicode, curl | sh and reverse shells, credential-stealing patterns, leaked keys, and auto-running hooks. You get a per-finding report and a letter grade, with exit codes for CI.
Skills are just instructions and scripts a model will follow, and most people install them the way they'd npm install anything: without reading a line. Recent audits of public skills found prompt injection in a large share of them. This is the tool that reads the skill so you don't have to trust it blind.
That skill lives in examples/sketchy-pdf-summarizer/. Every URL and key in it is fake. Run the scan yourself:
skillxray examples/sketchy-pdf-summarizer/See the Rules Reference for full details on each rule, its severity, and how to fix it.
- Prompt injection aimed at the agent: "ignore previous instructions", "don't tell the user", "reveal your system prompt", silent tool execution.
- Hidden Unicode: bidi overrides (Trojan Source), invisible tag characters that smuggle instructions, zero-width characters breaking up words. Unicode tag characters render as nothing in your editor but read as text to a model, so a line that displays as
follow these rulescan carry an invisibleand exfiltrate the keysbehind it. skillxray decodes what the invisible bytes actually spell. (This README stays clean on purpose; the live payload sits in the example skill.) - Dangerous commands:
curl | sh, base64 piped to a shell, reverse shells,rm -rf ~, writes to shell startup files, cron persistence,shell=True, disabled TLS. - Data exfiltration: reads of
~/.ssh, cloud credentials,.env, browser cookies, and whether the same file can send them out, plus known paste, webhook, and tunnel endpoints. - Hardcoded secrets: AWS keys, GitHub and GitLab tokens, OpenAI and Anthropic keys, Stripe keys, private key blocks. Matched values are redacted, never echoed back.
- Permissions: broad
allowed-toolsgrants, MCP servers that launch local binaries, Claude Code hooks that run shell automatically on an event. - Supply chain: compiled
.pycshipped without its source, release assets pulled from a GitHub account the skill never claims, password-protected archives. Content nobody can read before it runs. - Hygiene: missing name or description, bloated
SKILL.md, broken file references, no license. Reported separately from the security grade, and it never fails the build.
One command:
pipx install git+https://github.com/munzzyy/skillxrayThe PyPI package is still catching up: the published release predates the MIT relicense and several rule fixes, so install from git until it lands. Everything below works either way.
Pure standard library, Python 3.9+, no runtime dependencies, so a plain clone works too:
git clone https://github.com/munzzyy/skillxray
cd skillxray
python -m skillxray ./some-skill # run it directly, no installskillxray ./my-skill # scan a skill directory
skillxray ./SKILL.md # scan a single file
skillxray ./skills-folder # scan every skill under a folder
skillxray --git https://github.com/someone/their-skill # clone (read-only) and scanNothing in a scanned skill is ever executed. --git clones shallowly with hooks disabled and only reads files.
skillxray exits non-zero when it finds something at or above a severity you choose, so it drops straight into a pipeline:
- run: pipx run --spec git+https://github.com/munzzyy/skillxray@v0.2.1 skillxray ./skills --fail-on high--fail-on takes critical, high, medium, low, or none (default high). It gates on security findings only. A missing LICENSE or a broken link is a hygiene note and never reds a build.
The exit code is the whole contract:
| Code | Meaning |
|---|---|
0 |
nothing at or above the threshold |
1 |
at least one security finding at or above the threshold |
2 |
skillxray could not run: bad flag, missing path, failed clone |
2 is deliberately separate from 1. A misspelled flag means the scan never happened, and a pipeline that reads that as "found something" is drawing the wrong conclusion in both directions.
It also speaks SARIF, so findings show up in the GitHub Security tab, tagged with their OWASP Agentic Skills Top 10 identifier and linked to the Rules Reference:
- run: pipx run --spec git+https://github.com/munzzyy/skillxray@v0.2.1 skillxray ./skills --sarif > skillxray.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: skillxray.sarifOr skip the two steps and use the packaged action, which installs skillxray, scans, and uploads the SARIF in one uses: (needs security-events: write for the upload). It installs the exact code at the ref you pin, so the rules always match the tag:
- uses: munzzyy/skillxray@v0.2.1
with:
path: ./skills
fail-on: highAny repo that ships security fixtures, a red-team corpus, or docs that quote curl | sh will light up, including this one. --exclude takes a glob relative to the path you scanned and is repeatable:
skillxray . --exclude 'tests/corpus/*' --exclude 'examples/*'Globs match on forward slashes on every platform, and a bare directory name excludes everything under it.
You can also run skillxray as a pre-commit hook to block dangerous skills from being committed. Add this to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/munzzyy/skillxray
rev: v0.2.1
hooks:
- id: skillxraypre-commit hands over the changed SKILL.md and nothing else, so skillxray scans the whole skill that file belongs to. A payload in a sibling script gets read even when only the markdown changed.
- default: colored human report
--json: full findings for scripting--sarif: SARIF 2.1.0 for code scanning--quiet: just the grade and counts
Scan a folder of skills and every finding carries the skill it came from, in all three formats.
Plenty of scanners are adjacent to this and none of them cover it:
- Semgrep and friends analyze source code. A skill's attack surface is mostly natural-language instructions like "ignore previous instructions" and "don't tell the user", which code SAST has no rules for. skillxray scans the prose and the scripts.
- TruffleHog / gitleaks hunt secrets across git history. skillxray checks the files in front of it for secrets as one rule among thirty, alongside injection, exfiltration, and persistence patterns.
- Runtime guardrails (Lakera Guard, LLM Guard, NeMo Guardrails) sit between your app and the model and filter live traffic, which means network calls and a vendor. skillxray runs offline, before install. The point is to catch a malicious skill while it's still a folder you're deciding about.
- It's a static scanner. It reads text and matches patterns; it does not run the skill or trace what a script actually does at runtime. A determined attacker can obfuscate past any static rule, and skillxray flags obfuscation itself (base64-to-shell, hidden Unicode) rather than pretending to defeat it.
- A clean grade means nothing obvious tripped, not that the skill is safe. Read anything before you trust it with your machine.
- It expects skill-shaped input (a
SKILL.md, a plugin, or a folder of them). Point it at an arbitrary code repo and you'll get noisier results, because it will read every text file it finds. - It is not a secret scanner for your whole git history. It checks the files in front of it.
Every check is a deterministic rule over the skill's text. No model calls, no network (except --git, which only clones), no telemetry. Findings carry a rule id, severity, file and line, and a fix. The grade starts at 100 and loses points by severity, with two hard rules: any critical finding is an F, and any high keeps it out of the top band. The whole thing is standard-library Python so it installs anywhere and you can read every rule yourself in skillxray/rules/.
Found a skill that should have been flagged and wasn't, or a false positive? Open an issue with the smallest example that reproduces it. New rules land with a fixture in tests/corpus/ (a malicious one that must be caught, or a benign one that must stay clean) so coverage only goes up.
MIT. Free to use, change, and ship, commercial or not. See LICENSE.
If skillxray flagged something before it reached your machine, sponsoring is what keeps the rules growing.