Edictum
Reference

Skill Scanner

Deterministic static analysis of agent skill directories (SKILL.md files) for security findings, with CRITICAL/HIGH/MEDIUM/CLEAN risk tiering.

AI Assistance

Right page if: you want to understand how `edictum skill scan` works, what it detects, how risk tiers are assigned, or how to integrate it with Console. Wrong page if: you want the CLI flags for `edictum skill scan` -- see https://docs.edictum.ai/docs/reference/cli. For Console environment variables, see https://docs.edictum.ai/docs/console/reference/environment-variables. Gotcha: The scanner only analyzes code blocks in SKILL.md files. Prompt injection in prose sections is NOT detected. Also, RFC1918 private IPs do NOT escalate pipe-to-shell findings to CRITICAL.

AI agents that execute skills from SKILL.md files can be tricked into running malicious code if those files are compromised. edictum skill scan performs deterministic static analysis of skill directories before deployment, assigning each skill a risk tier based on what its code blocks contain.

pip install edictum[cli]
edictum skill scan ./skills

What it detects

The scanner analyzes code blocks only — fenced code in SKILL.md files. Prose sections (including any prompt injection attempts embedded in text) are not scanned.

Five finding categories are checked per skill:

Dangerous commands

20 patterns covering shell commands that indicate unauthorized execution, exfiltration, or privilege escalation. Examples:

  • Pipe-to-shell: curl | bash, wget | sh
  • Reverse shells: nc -e, bash -i >& /dev/tcp/
  • Destructive commands: rm -rf /
  • Netcat listeners and similar

Credential paths

12 patterns matching paths to common credential and key files. Examples:

  • ~/.aws/credentials
  • ~/.ssh/id_rsa, ~/.ssh/id_ed25519
  • .env files, credentials.json

Exfiltration domains

10 known exfiltration or out-of-band channel domains. Examples:

  • webhook.site
  • ngrok.io
  • requestbin.com

Obfuscation signals

7 patterns that indicate runtime deobfuscation attempts:

  • Hex-encoded shellcode
  • JavaScript charCodeAt / fromCharCode payloads
  • Base64 strings decoded at runtime (e.g., base64 -d | bash)

Base64 payload classification

When a base64 string is found, the scanner classifies the decoded content as shell_command, url, text, or binary. The classification is reported without leaking the decoded content.


Risk tiers

Each skill receives one of four tiers:

TierMeaning
CRITICALHigh-confidence exploit pattern present
HIGHSerious threat signal, not definitively exploitable
MEDIUMSuspicious pattern requiring review
CLEANNo findings above threshold

Escalation rules (examples):

  • Pipe-to-shell (curl | bash) + a public IP address → CRITICAL
  • Pipe-to-shell alone (no public IP, or only RFC1918 address) → HIGH
  • Credential path access alone → HIGH
  • Exfiltration domain reference → HIGH
  • Obfuscation signal alone → MEDIUM
  • Base64 payload classifying as shell_command → escalates to HIGH or CRITICAL based on other signals

RFC1918 private IP addresses (10.x, 172.16–31.x, 192.168.x) do not escalate pipe-to-shell findings to CRITICAL.


CLI usage

edictum skill scan PATH [OPTIONS]

See the CLI reference for the full flag table and exit codes. Common patterns:

Fail CI on any HIGH or above:

edictum skill scan ./skills --threshold HIGH

JSON output for downstream tooling:

edictum skill scan ./skills --json --threshold MEDIUM

Check which skills lack contracts.yaml coverage:

edictum skill scan ./skills --structural-only

Parallel batch scan with verbose pattern output:

edictum skill scan ./skills --workers 8 --verbose

Console integration

Pass --server to POST scan findings to a Console server:

edictum skill scan ./skills \
    --server https://console.example.com \
    --json

Findings are POSTed to /api/v1/skill-scan. Authentication uses the EDICTUM_CONSOLE_TOKEN environment variable.

Requires edictum[server]:

pip install edictum[server]

The server URL must use HTTPS. HTTP targets are rejected.


Security design notes

  • YAML parsing: skill metadata is loaded with yaml.safe_load only — no arbitrary Python object deserialization.
  • Base64 classification: decoded content is classified (shell_command/url/text/binary) but never included in scan output. The raw decoded bytes are discarded after classification.
  • File size cap: skills over 1 MB are skipped with a scan error.
  • Code block cap: only the first 50 code blocks per SKILL.md are analyzed.
  • Timeout: a SIGALRM timeout is applied per skill to prevent hanging on pathological input.

Next Steps

Last updated on

On this page