Security scanner for AI skill files (SKILL.md)
Project description
t2i-skillguard
Security scanner for AI skill files (SKILL.md) and bundled code (scripts/, tools/).
It helps you catch high-risk patterns before publishing or running a skill.
TL;DR
# 1) Install
pip install t2i-skillguard
# 2) Scan default locations
t2i-skillguard scan -v
# 3) JSON report with CVSS + payment aggregation
t2i-skillguard scan --format json --aggregate-payment --output report.json
Example execution output:
Why this project
- Focused on real AI-skill risks (prompt injection, third-party content abuse, unsafe code execution).
- Scans both skill metadata and executable code paths.
- Clear terminal output + machine-readable JSON for CI.
- Opinionated severity model to support go/no-go decisions.
Features
- 13 built-in security rules for AI skill ecosystems.
- Scans both
SKILL.mdand bundled code inscripts/+tools/. - CVSS metadata per vulnerability in JSON output.
- Optional aggregation of repetitive payment findings (
--aggregate-payment). - Severity/category filters for focused triage.
- CI-friendly exit codes (
0clean,1findings).
What it detects
t2i-skillguard currently ships with 13 security rules across CRITICAL/HIGH/MEDIUM/LOW.
| Severity | Rule | Category | Typical risk |
|---|---|---|---|
| CRITICAL | prompt_injection |
Injection | Override safety instructions / exfiltrate context |
| CRITICAL | command_injection |
Injection | Arbitrary command execution |
| HIGH | secret_exposure |
Exposure | Hardcoded keys, tokens, passwords, private keys |
| HIGH | malicious_patterns |
Dangerous Patterns | eval/exec/unsafe deserialization paths |
| HIGH | supply_chain |
Supply Chain | Downloading/executing untrusted code |
| HIGH / MEDIUM | third_party_exposure |
Dangerous Patterns | External content driving AI behavior |
| MEDIUM | insecure_flags |
Configuration | TLS/cert bypass, unsafe runtime flags |
| MEDIUM | path_traversal |
Configuration | Access outside allowed file roots |
| MEDIUM | credential_exposure |
Exposure | Secrets leaked in logs, errors, output |
| MEDIUM | external_content |
Exposure | Unsafe processing of untrusted web/user content |
| LOW | payment_capability |
Supply Chain | Financial capability signal for extra review |
| MEDIUM | system_alteration |
Configuration | Service/firewall/startup/system modifications |
| LOW | frontmatter |
Metadata | Missing/invalid skill metadata |
Full rule docs: docs/rules/README.md
Installation
Option A: install from PyPI (recommended for usage)
pip install t2i-skillguard
Option B: local development (recommended for contributors)
poetry install
Quick start
Scan default skill locations:
t2i-skillguard scan
Scan one skill:
t2i-skillguard scan-skill /path/to/skill
Scan custom paths:
t2i-skillguard scan /path/to/skills-a /path/to/skills-b
CLI usage
Output modes
# Terminal (default)
t2i-skillguard scan --format terminal
# JSON
t2i-skillguard scan --format json
# Terminal + JSON
t2i-skillguard scan --format both
# Compact JSON (single line)
t2i-skillguard scan --format compact
Common filters
# Only HIGH and CRITICAL findings
t2i-skillguard scan --min-severity HIGH
# Only one rule category
t2i-skillguard scan --category prompt_injection
# Ignore a whole category
t2i-skillguard scan --ignore-rule prompt_injection
# Ignore a specific pattern in a category
t2i-skillguard scan --ignore-rule prompt_injection.role_confusion
# Repeat or use comma-separated values
t2i-skillguard scan --ignore-rule insecure_flags.http_url --ignore-rule path_traversal
t2i-skillguard scan --ignore-rule insecure_flags.http_url,path_traversal
# Load scoped ignores from YAML file
t2i-skillguard scan --ignore-file .t2i-skillguard-ignore.yml
# Disable warnings for unmatched ignore entries
t2i-skillguard scan --no-warn-unused-ignores
# Skip scripts/tools analysis
t2i-skillguard scan --no-scripts
Ignore rules
Use --ignore-rule when you need to suppress known/accepted findings for a specific run.
- Accepted format:
categoryorcategory.pattern - Repeatable:
--ignore-rule a --ignore-rule b.c - Comma-separated supported:
--ignore-rule a,b.c - Works with both
scanandscan-skill - You can combine with
--ignore-filefor file/line-scoped suppression
Examples:
# Ignore all prompt injection findings
t2i-skillguard scan --ignore-rule prompt_injection
# Keep only prompt_injection category, then ignore one noisy pattern
t2i-skillguard scan --category prompt_injection --ignore-rule prompt_injection.role_confusion
# Single-skill scan with ignores
t2i-skillguard scan-skill /path/to/skill --ignore-rule insecure_flags.http_url
Ignore file (YAML)
The scanner will auto-load ./.t2i-skillguard-ignore.yml if it exists. You can also set a custom path with --ignore-file.
Supported schema (version: 1):
version: 1
ignores:
- category: prompt_injection
- category: insecure_flags
pattern: http_url
- category: credential_exposure
file: skills/skill-creator/scripts/aggregate_benchmark.py
- category: prompt_injection
pattern: concealment_instruction
file: skills/skill-creator/SKILL.md
line: 327
reason: "Known accepted case"
Fields:
category(required)pattern(optional)file(optional)lineorlines(optional, requiresfile)reason(optional)
Note: ignore rules (CLI and YAML) affect the final report and exit code for that run. They do not modify detector logic.
Unused ignore warnings
By default, the CLI warns when an ignore entry does not match any finding in the current run.
- Default:
--warn-unused-ignores - Disable warnings:
--no-warn-unused-ignores
Example warning output:
Ignored findings: 3
Warning: 2 ignore entries did not match any finding.
- prompt_injection.role_confusion @ /repo/skills/skill-a/SKILL.md:120
- external_content
Reporting
# Verbose terminal output
t2i-skillguard scan -v
# Save JSON report
t2i-skillguard scan --output report.json
# Aggregate repetitive payment capability findings in terminal and JSON output
t2i-skillguard scan --format both --aggregate-payment
# Aggregate for a single skill scan
t2i-skillguard scan-skill /path/to/skill --format json --aggregate-payment
# Show default locations that will be scanned
t2i-skillguard list-paths
Exit codes (CI-friendly)
0-> no vulnerabilities found1-> one or more vulnerabilities found
This makes it easy to fail CI pipelines when findings are detected.
CI integration (GitHub Actions)
name: Skill Security Scan
on:
pull_request:
push:
branches: [main]
jobs:
scan-skills:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Poetry
run: pipx install poetry
- name: Install dependencies
run: poetry install
- name: Run scanner
run: poetry run t2i-skillguard scan --format json --aggregate-payment --output skillguard-report.json
Release and PyPI publish
When a GitHub Release is published, .github/workflows/publish-pypi.yml runs and publishes the package to PyPI.
Important:
- Release tag must match
tool.poetry.versioninpyproject.toml. - Both
1.0.0andv1.0.0tag formats are accepted. - Publish job runs lint (
poetry run ruff check .) and tests (poetry run pytest) before build/upload. - Trusted Publisher must be configured in PyPI for this repository/workflow.
Scoring model
Each skill gets a score on a 0-10 scale:
raw_score = min(10, max_severity + vulnerability_count * 0.5)
score = min(raw_score, severity_cap[max_severity])
severity_cap = {
CRITICAL: 10,
HIGH: 9,
MEDIUM: 7,
LOW: 4,
INFO: 0,
}
This cap keeps score bands coherent with max-severity verdicts (for example, many LOW findings cannot become CRITICAL by count alone).
Verdict bands:
0-> OK1-4-> LOW5-7-> MEDIUM8-9-> HIGH10-> CRITICAL
Default scan locations
If no path is provided, scanner checks existing directories in this order.
Project scope:
./.opencode/skills/./.claude/skills/./.agents/skills/
Global scope:
~/.config/opencode/skills/~/.claude/skills/~/.agents/skills/
Compatibility (when present):
${XDG_CONFIG_HOME}/opencode/skills/${XDG_CONFIG_HOME}/claude/skills/${XDG_CONFIG_HOME}/agents/skills/${APPDATA}/opencode/skills/(Windows)${APPDATA}/claude/skills/(Windows)${APPDATA}/agents/skills/(Windows)~/.config/claude/skills/(legacy)~/.config/anthropic/skills/(legacy)
Tip: run t2i-skillguard list-paths to see exactly which paths were found on your machine.
Supported environments
- OS: Linux, macOS, Windows (including
APPDATApath discovery support). - Python: 3.10+ recommended.
- Execution modes:
- local development via Poetry,
- CI pipelines (GitHub Actions example included above).
- Input layout:
- project-local skill folders (
.opencode,.claude,.agents), - global skill folders (
~/.config/opencode,~/.claude,~/.agents), - compatibility paths (
XDG_CONFIG_HOME, legacy config paths).
- project-local skill folders (
CVSS in JSON output
Each vulnerability in JSON output includes CVSS metadata:
cvss_vectorcvss_scorecvss_severity
CVSS is resolved per category.pattern (with category fallback coverage).
When using --aggregate-payment, output also includes grouped-vs-raw counters:
- report level:
raw_total_vulnerabilities - skill level:
raw_vulnerability_count,raw_payment_vulnerability_count
Aggregation is presentation-only; detection logic, score, and verdict are unchanged.
Example:
{
"id": "CMDI-001",
"severity": "CRITICAL",
"category": "command_injection",
"pattern": "subprocess_shell_true",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"cvss_score": 10.0,
"cvss_severity": "CRITICAL"
}
Development
# Install dependencies
poetry install
# Run lint
poetry run ruff check .
# Run tests
poetry run pytest
# Run scanner locally
poetry run t2i-skillguard scan -v
Contributing
Contributions are welcome.
- Open an issue for bugs, false positives, or rule improvements.
- Submit focused PRs with tests for behavior changes.
- Keep rule docs in
docs/rules/aligned with detector changes. - Pull requests run CI checks (
.github/workflows/tests.yml) that execute lint + tests and fail the PR check if either command fails. - Run the same checks locally before opening PR:
poetry run ruff check .
poetry run pytest
Recommended contribution scope:
- one detector fix/improvement per PR,
- documentation updates in the same PR,
- include before/after examples when tuning detection patterns.
Security policy
See SECURITY.md for supported versions, private reporting instructions, and disclosure policy.
Documentation
- Rules index:
docs/rules/README.md - Rule deep-dives:
docs/rules/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file t2i_skillguard-0.1.0.tar.gz.
File metadata
- Download URL: t2i_skillguard-0.1.0.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50afbb950175748b92ba251648f7250a874985f1298fdbcb4e7969d0af2f9354
|
|
| MD5 |
0bb397cac091530b4d1decf2e11d26f7
|
|
| BLAKE2b-256 |
6b1f165aba6d281cc3963b17221b7d6006c6df4d65fdc4b5c4c6e6bd9ef54ab5
|
Provenance
The following attestation bundles were made for t2i_skillguard-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on Tech2Insights/t2i-skillguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
t2i_skillguard-0.1.0.tar.gz -
Subject digest:
50afbb950175748b92ba251648f7250a874985f1298fdbcb4e7969d0af2f9354 - Sigstore transparency entry: 1258445998
- Sigstore integration time:
-
Permalink:
Tech2Insights/t2i-skillguard@aef4f4e422447ec6c51cafed845ad5e923d2594c -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/Tech2Insights
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@aef4f4e422447ec6c51cafed845ad5e923d2594c -
Trigger Event:
release
-
Statement type:
File details
Details for the file t2i_skillguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: t2i_skillguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b534198629209a24454c0b7fd09ce6a796ce603c038596323e305d4d62fa7771
|
|
| MD5 |
6c6d863150b0e66661d078272fe912ba
|
|
| BLAKE2b-256 |
03462ac538a40aa12037924274954e5b4be244245ae6158830097c7e4e3f5e5b
|
Provenance
The following attestation bundles were made for t2i_skillguard-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on Tech2Insights/t2i-skillguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
t2i_skillguard-0.1.0-py3-none-any.whl -
Subject digest:
b534198629209a24454c0b7fd09ce6a796ce603c038596323e305d4d62fa7771 - Sigstore transparency entry: 1258446173
- Sigstore integration time:
-
Permalink:
Tech2Insights/t2i-skillguard@aef4f4e422447ec6c51cafed845ad5e923d2594c -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/Tech2Insights
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@aef4f4e422447ec6c51cafed845ad5e923d2594c -
Trigger Event:
release
-
Statement type: