agentscan
The trust layer for AI agent skills.
A deterministic, local security scanner for AI agent skills — plus the Trusted Distribution: a curated, continuously audited package registry.
pip install agentscan-cli
agentscan scan ~/.claude/skills # free, local, deterministic
agentscan activate # Trusted Distribution license
agentscan search # what's available
agentscan install trust-pack # one command, verified
agentscan update # like brew upgrade
The scanner (free, open source, MIT)
Scan any skill, MCP server, or agent config for what it actually does — shell, exfiltration, secrets, supply-chain, obfuscation — before you run it in your agent. v2 adds structural and semantic analysis: the scanner now understands what a skill is (instructions vs docs vs code), tracks secret-shaped data flows with attack paths, extracts capabilities, and correlates evidence instead of reporting raw pattern matches.
- Facts, not verdicts. Every finding is a checkable fact with
file:line. The scanner never calls anything "malicious" — that verdict is yours. Every finding carries a confidence score separate from its severity, and an evidence list. - Deterministic, not ML. Regex, entropy, AST, taint, structure. Same input, same report, every time. No model, no hallucination.
- Zero dependency, zero execution. Pure Python stdlib. Never runs a
skill, never calls out (OSV lookup is opt-in with
--osv), works offline.
$ agentscan scan ~/Downloads/suspicious-skill
agentscan 1.0.0 — /home/you/Downloads/suspicious-skill
scanned 1 artifact(s), 8 finding(s)
ARTIFACT [claude-skill] auto-updater
CRITICAL [exfil] Local secret read piped to network
SKILL.md:41
CRITICAL [analysis] Secret data flows to external endpoint
SKILL.md:17
attack path: reads sensitive file (open.read) -> urllib.request.urlopen receives tainted data (...)
...
summary: critical=3 high=2 medium=5 low=2 info=2
capabilities:
secret.access SKILL.md:24
network.upload SKILL.md:41
review queue (manual review — never a verdict):
HIGH [prompt_patterns] Instruction directs transfer of credential material — SKILL.md:30
Exit codes: 0 clean · 1 findings at/above threshold · 2 usage error.
What it checks
| Check | Observes |
|---|---|
shell |
bash/sh/python -c/node -e/exec/eval/subprocess invocations — fence- and context-aware (markdown inline code is not shell) |
filesystem |
rm -r/-f, shutil.rmtree, git reset --hard/clean/push --force, chmod 777 — defensive contexts excluded, path scope graded (TMPDIR vs $HOME) |
network |
curl/wget/fetch/requests, URLs, credential-in-URL, IP literals, cleartext http — destination-trust tiered (loopback/private/metadata/public) |
secrets |
20+ token formats (AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, JWT, PEM…) — config/env reads exempt, documentation-context examples downgraded |
license |
declared license at skill granularity (one finding per skill) |
supply_chain |
curl|bash pipes, git clone, unpinned pip/npm, script downloads — user-install docs downgraded |
prompt_patterns |
high-risk prompt-manipulation phrasing + explicit credential-transfer instructions + SCH-shaped compliance-rule phrasings (review queue) — flagged, never "detected" |
exfil |
credentialed webhooks, env-in-URL, secret-read → network — official-API destinations with env-configured credentials are informational |
obfuscation |
decode-to-execute chains, nested eval/exec, hex escapes |
config_tamper |
remote MCP servers, hook commands, npm lifecycle scripts, poisoned MCP tool descriptions (credential read + data transfer in one description) |
dependencies |
dependency extraction, pin status, typosquat candidates, SBOM seed |
analysis |
taint chains with attack paths (Python AST + shell parser), cross-file script references, capability extraction, evidence correlation |
Artifacts detected: claude-skill, mcp-server, cursor-rules,
context-file, github-actions, npm-package, generic.
Usage
python3 -m agentscan <dir> # human report
python3 -m agentscan <dir> --json # machine-readable
python3 -m agentscan <dir> --sarif # SARIF 2.1.0 (GitHub code scanning)
python3 -m agentscan <dir> --sbom # CycloneDX 1.5 SBOM of dependencies
python3 -m agentscan <dir> --osv # OSV vulnerability lookup (online, opt-in)
python3 -m agentscan <dir> --severity high # only high+ fails exit code
v2 finding model
Every finding carries:
severity(impact if true) andconfidence(how likely it is true) — separate axes, reported separatelyevidence: file:line + snippet for every claimattack_path: per-hop citations for correlated/taint findingscapability: the capability the evidence contributes tofingerprint: stable per (rule, location) — baseline supportorigin:deterministic(ormodel-assistedin a future optional tier)
The report also includes a capabilities map (what the artifact can do,
with evidence) and a review_queue (low-confidence semantic signals that
are review items, never verdicts).
Benchmark
python3 bench/run_bench.py --exit runs a 22-skill corpus (10 malicious
attack classes, 12 benign FP classes) and fails on contract violations:
malicious skills must produce high/critical findings, benign skills must
not. Malicious recall at high: 10/10 on the shipped corpus. This is
the guardrail that prevents the scanner from being "improved" by going
quiet.
The Trusted Distribution (paid)
Access to curated, security-reviewed packages. Buy once ($49), receive a
license key, activate, install. No dashboard, no browser login, no accounts —
it's a developer tool.
Buy ($49, Polar checkout)
↓
License key (shown on your Polar purchases page)
↓
pip install agentscan-cli
↓
agentscan activate → Polar /v1/customer-portal/license-keys/validate (direct)
↓
agentscan search → GET /api/packages
↓
agentscan install <package> → GET /api/download/<id> (license-gated) → sha256 → extract → install
↓
agentscan update → like brew upgrade
Architecture
PyPI (agentscan CLI, MIT)
↓
Polar (payment + license keys — source of truth, no users table)
↑ direct validation (public endpoint, no server in the middle)
|
agentscan.baldbee.me (Next.js site + API route handlers)
├── /api/packages public catalog
└── /api/download/[id] license-gated tarball proxy
↓
agentscan-registry (private GitHub repo)
├── packages/ source of truth
├── packages.json generated catalog (sha256 per package)
└── GitHub Releases tarball distribution
↓
~/.agentscan/ license · installed.json · config.json · cache/
~/.claude/skills/<skill-id>/ Claude Code install
~/.config/opencode/skills/<skill-id>/ OpenCode install
~/.agents/skills/<skill-id>/ Codex install (+ AGENTS.md at repo root)
~/.hermes/skills/<skill-id>/ Hermes install ($HERMES_HOME honored)
~/.grok/skills/<skill-id>/ Grok Build install ($GROK_HOME honored)
The website and API are the same Next.js application. No separate backend, no database, no object storage. Git is the source of truth; GitHub Releases are the CDN. License validation is on-demand against Polar — nothing is stored server-side, no webhooks, no subscriptions.
The Polar organization id is public metadata and is baked into the CLI as a
constant (DEFAULT_POLAR_ORGANIZATION_ID in agentscan/config.py). Users
never configure it — install, activate, done.
Commands
agentscan scan . # scan a directory of agent skills (free)
agentscan activate # prompt for license → verify → store
agentscan logout # remove local license
agentscan whoami # show active license
agentscan search # browse the catalog (package cards)
agentscan install <package> # verified install, runtime auto-detected
agentscan install <p> --runtime codex # install into a specific runtime
agentscan update # upgrade installed packages
agentscan verify # signature · latest · audit · intact
Package names are matched flexibly — any of these work:
agentscan install trust-pack
agentscan install "Trust Pack"
agentscan install Trust Pack
agentscan install trust
agentscan install trustpac # typos are suggested, not silent
Runtimes
Packages install into the agent runtimes found on your machine:
| Runtime | Detected via | Skills install to |
|---|---|---|
| Claude Code | ~/.claude or claude |
~/.claude/skills/<skill-id>/ |
| OpenCode | ~/.config/opencode or opencode |
~/.config/opencode/skills/<skill-id>/ |
| OpenAI Codex | ~/.codex, ~/.agents, or codex |
~/.agents/skills/<skill-id>/ + AGENTS.md |
| Hermes | $HERMES_HOME, ~/.hermes, or hermes |
$HERMES_HOME/skills/<skill-id>/ (default ~/.hermes/skills/) |
| Grok Build | $GROK_HOME, ~/.grok, or grok |
$GROK_HOME/skills/<skill-id>/ (default ~/.grok/skills/) |
agentscan install detects installed runtimes automatically. When several
are present it installs into all of them; when none are detected it prompts,
or you can pick explicitly:
agentscan install trust-pack --runtime claude
agentscan install trust-pack --runtime opencode
agentscan install trust-pack --runtime codex
agentscan install trust-pack --runtime hermes
agentscan install trust-pack --runtime grok
agentscan install trust-pack --runtime all
Hermes follows the agentskills.io open standard: skills are discovered
recursively under its skills root and load as slash commands (/skill-name).
HERMES_HOME is the official override and is honored when set (it also
covers named profiles).
Grok Build (xAI) reads the same agentskills.io SKILL.md standard and
discovers skills recursively under skills/ dirs — including
~/.grok/skills/, ~/.agents/skills/, and ~/.claude/skills/. GROK_HOME
is the official home override and is honored when set. Skills load as slash
commands (/skill-name) and grok inspect lists everything discovered.
Codex installs also write the package's AGENTS.md (the agents.md
convention) to the repository root when you are inside a git work tree. The
file is marked with a <!-- agentscan:<package> --> comment; reinstalling
replaces that section, and the rest of your AGENTS.md is left untouched.
Add --quiet (or -q) to suppress progress lines for automation; results
and errors still print. Run agentscan --help for examples.
Local state
Everything lives in ~/.agentscan/:
~/.agentscan/
license the activated license (JSON)
installed.json {package-id: {version, runtimes: {runtime: {skills: [...]}}}}
config.json api_url override (optional)
cache/ downloaded tarballs
Package format
A package is not just a skill. It may contain agents, skills, slash commands, templates, workflows, and knowledge:
trust-pack/
manifest.json id, title, version, description, license, requires
agents/ optional agent definitions
skills/ SKILL.md files
commands/ optional slash commands
templates/ optional templates
knowledge/ reference material
audit.json latest deterministic scan result
signature.sig placeholder — real signing lands with the Polar milestone
README.md
manifest.json ships one package definition per the catalog shape:
{ "packages": [ { "id": "trust-pack", "title": "Trust Pack",
"version": "1.0.0", "description": "…", "sha256": "…",
"release": "v1.0.0", "asset": "trust-pack-1.0.0.tar.gz" } ] }
Status of the paid layer
- License verification is real.
agentscan activatevalidates the key against Polar's public customer-portal endpoint on demand. No mock, no database, no webhooks. - Downloads are real and license-gated. The CLI sends the license key
as
Authorization: Bearer <key>; the site validates it against Polar before proxying the tarball from the private GitHub registry. Checksums are verified end to end (server-side and CLI-side). signature.sigis a placeholder; cryptographic signing is designed in (agentscan verifyis structured to add it without CLI changes).
The numbers (4,000 skills measured)
We scanned a random sample of 4,000 unique skills across 174 public repos:
| Signal | % of skills |
|---|---|
| No recognizable license | 93.6% |
| Invokes shell / interpreter | 75.3% |
| Supply-chain patterns (curl|bash, unpinned installs) | 23.1% |
| Credential-format strings | 14.7% |
| Destructive filesystem ops | 7.6% |
| Exfiltration sinks | 4.2% |
| Prompt-manipulation phrasing | 3.3% |
| Obfuscation chains | 0.4% |
| Any high/critical finding | 18.0% |
Full methodology: CORPUS-REPORT.md
Development
python3 -m unittest discover -s tests -v # 105 tests
Pure stdlib, Python 3.8+, works offline. To point the CLI at a local server:
export AGENTSCAN_API_URL=http://localhost:3100 # overrides the default API
License
MIT © baldbee
Independent project. Not affiliated with Anthropic, Snyk, or any vendor. Patterns modeled on gitleaks/trufflehog (secrets), OWASP Agentic Top 10, MCPGuard threat taxonomy, and Snyk's ToxicSkills findings.
Metadata
Release files for agentscan-cli 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentscan_cli-1.1.0.tar.gz | 100.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentscan_cli-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 200.4 kB
Release files / agentscan_cli-1.1.0.tar.gz
| Download URL | agentscan_cli-1.1.0.tar.gz |
|---|---|
| Size | 100.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7f9734fdfa0e81b51e369022953bfe6a4aa2caa86c357fbf39e163430aca934
|
|
BLAKE2b-256 checksum How to use checksums |
7f53e4f2575e3698b312202949aadf243c42e8b06b941550f4aebda42fe0c33d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / agentscan_cli-1.1.0-py3-none-any.whl
| Download URL | agentscan_cli-1.1.0-py3-none-any.whl |
|---|---|
| Size | 99.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6fce20fded3e4391cde557ccf7dc41b7d4ca7da1369fb4c3b7ee3dceb75a1b7
|
|
BLAKE2b-256 checksum How to use checksums |
21ba9753386481817a2882f6641c83ed3896eb2b16b58865b2754089c325fd61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|