Skip to main content

agentscan

PyPI version License: MIT PyPI downloads

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.

Website: agentscan.baldbee.me — docs, scan examples, and the Trust Pack storefront.

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.1.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) and confidence (how likely it is true) — separate axes, reported separately
  • evidence: file:line + snippet for every claim
  • attack_path: per-hop citations for correlated/taint findings
  • capability: the capability the evidence contributes to
  • fingerprint: stable per (rule, location) — baseline support
  • origin: deterministic (or model-assisted in 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 activate validates 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.sig is a placeholder; cryptographic signing is designed in (agentscan verify is 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.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentscan-cli 1.2.1
File Size Uploaded
agentscan_cli-1.2.1.tar.gz 103.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentscan-cli 1.2.1
File Interpreter ABI Platform
agentscan_cli-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 203.6 kB

Release files / agentscan_cli-1.2.1.tar.gz

Download URL agentscan_cli-1.2.1.tar.gz
Size 103.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0acdda253b8dfb3eb0f0df2e6891aa2bf84189e3047838df284d1a318e5c2644
BLAKE2b-256 checksum
How to use checksums
bcfd7704f40da2dcfd1a5f89cbf2631550d2edd41de9165b077171cc7fecd4fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release files / agentscan_cli-1.2.1-py3-none-any.whl

Download URL agentscan_cli-1.2.1-py3-none-any.whl
Size 100.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6bc4ab400e316f6699d2a8648bd81a216f91f99adc8dd2d8d4ae097e394d5bea
BLAKE2b-256 checksum
How to use checksums
a1b18196ce596aa3514f01f9a5055d88b47082e94fb442ee8570022ec7ef02a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release history Release notifications | RSS feed

1.2.2

2 release files

This release

1.2.1 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

1 release file

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page