skill-snitch
Skill Snitch Protocol
Skill Snitch audits MOOLLM skills for security issues through static analysis and runtime surveillance. It's prompt-driven - no Python code, just orchestration via LLM tool calls.
Skill Type Detection
skill-snitch distinguishes between skill types and applies appropriate requirements:
MOOLLM Skills (Strict Requirements)
MOOLLM skills are identified by:
- Presence of
CARD.ymland/orSKILL.md - Tags containing "moollm"
- References to other
skills/*orPROTOCOLS.yml - Use of K-lines/K-refs
Required files:
CARD.yml- Interface, methods, tools, advertisementsSKILL.md- Protocol documentationREADME.md- GitHub-facing documentation
Anthropic/Generic Skills (Flexible)
Non-MOOLLM skills (Anthropic MCP skills, generic tools) have no strict file requirements:
- May use
.mdcfiles (Anthropic format) - Often standalone with no cross-references
- No CARD.yml/SKILL.md required
Classification Output
classification:
type: moollm # moollm | anthropic | generic | hybrid
confidence: high
missing_requirements:
- README.md # If MOOLLM but missing
recommendations:
- "Add README.md for GitHub visibility"
Hybrid Skills
Skills with some MOOLLM elements but incomplete structure:
- Has CARD.yml but no SKILL.md
- References MOOLLM protocols but lacks proper structure
- Flagged for review
Auto-Initialize / Heal
On first invocation, check for and create missing user files:
.moollm/skill-snitch/
├── config.yml # User preferences
├── ignore.yml # Patterns/paths to skip
├── trust-overrides.yml # Manual trust level overrides
├── scan-history.yml # Log of past scans
├── last-scan.yml # Most recent scan results
└── trust-cache.yml # Cached trust assessments
INIT Protocol
1. Check if .moollm/skill-snitch/ exists
- If not: mkdir -p .moollm/skill-snitch/
2. For each required file, check existence:
- config.yml
- ignore.yml
- trust-overrides.yml
- scan-history.yml
3. Create missing files from templates (see below)
4. Report: "skill-snitch initialized" or "skill-snitch healed: created X files"
File Templates
config.yml
# skill-snitch configuration
version: 1
startup_scan:
enabled: false
min_severity: medium
scan_defaults:
include_examples: false
follow_dependencies: true
max_depth: 3
trust_defaults:
unknown_skill: yellow
no_card_yml: orange
external_network: orange
shell_commands: yellow
ignore.yml
# Patterns and paths to ignore during scans
version: 1
paths:
- "skills/cursor-mirror/scripts/cursor_mirror.py" # Contains pattern definitions
- "**/*_test.py"
- "**/examples/**"
patterns:
- name: "pattern_definition"
description: "Regex pattern definitions (not actual secrets)"
match: 'AuditPattern\(|r".*\(\?i\)|PATTERN_REGISTRY'
- name: "documentation_example"
description: "Examples in docs"
match: '```.*\n.*password.*\n.*```'
categories_to_skip:
- info # Skip INFO-level findings
trust-overrides.yml
# Manual trust level overrides
version: 1
overrides:
# Example:
# skills/trusted-skill/:
# trust: green
# reason: "Reviewed by Don on 2026-01-15"
# expires: 2026-07-15
scan-history.yml
# Scan history (append-only)
version: 1
scans: []
# Entries added automatically:
# - date: 2026-01-15T20:30:00Z
# skill: skills/adventure/
# findings: 3
# trust: yellow
Scan Protocol
## SCAN Protocol
When asked to scan a skill:
1. **INIT** - Ensure .moollm/skill-snitch/ exists
2. **Load ignore list**
- Read .moollm/skill-snitch/ignore.yml
- Merge with skill's own .snitch-ignore if present
3. **Read skill metadata**
- Read {skill_path}/CARD.yml
- Note: tools, dependencies, methods
4. **Static scan**
For each file in skill:
- Skip if matches ignore patterns
- Look for:
- Shell commands (curl, wget, ssh, nc)
- External URLs
- Encoded content (base64, hex)
- File access outside workspace
- Undeclared dependencies
5. **Output K-REFs**
For each finding:
- Emit: {path}:{line} # {pattern} | {severity} - {description}
6. **Log scan**
- Append to .moollm/skill-snitch/scan-history.yml
Fetch-Scan Protocol (Remote URL)
## FETCH-SCAN Protocol
Scan a skill directly from a URL without downloading or installing.
1. **Receive URL**
- User provides any URL: GitHub repo, directory, file, blog post, README
2. **Resolve what it points to**
- GitHub repo URL → list contents via GitHub API or raw.githubusercontent.com
- GitHub directory URL → that's a skill directory, fetch its files
- GitHub file URL → fetch and scan that single file
- Blog post / README → read it, follow links to find skill files (CARD.yml, SKILL.md, *.py)
- Organization / user URL → list repos, identify ones that look like skills
3. **Fetch skill files**
- Fetch via raw.githubusercontent.com (no git clone, no filesystem writes)
- Priority: CARD.yml first (sniffable interface), then SKILL.md, then scripts
- Follow the Semantic Image Pyramid: GLANCE → CARD → SKILL → README → code
4. **Run scan on fetched content**
- Apply same patterns, surfaces, analyzers as local SCAN
- Surface type: "remote" (fetched content, not local files)
- All pattern matching works on content strings, not file paths
5. **Report**
- Trust tier assessment
- Findings with line numbers (relative to fetched content)
- Recommendation: CLONE / REVIEW FIRST / DO NOT INSTALL
- Link back to source URL for manual review
6. **Nothing written to filesystem**
- No clone, no download, no temp files
- The skill was audited in memory
- You decide whether to install AFTER seeing the report
Usage:
FETCH-SCAN https://github.com/someone/their-skill— scan a whole skill repoFETCH-SCAN https://github.com/someone/repo/tree/main/skills/cool-skill— scan one skill directoryFETCH-SCAN https://github.com/someone/repo/blob/main/SKILL.md— scan a single fileFETCH-SCAN https://someone.com/blog/my-new-agent-skill— read the blog, follow links, scan what you find
In WIMP terms: drag and drop a URL onto skill-snitch. It's happy to dive in, find all the skills, and snitch on every one of them. The npm audit before npm install — except you don't even need to install to audit.
Snitch Protocol (Runtime)
## SNITCH Protocol
When asked to snitch on a skill's runtime behavior:
1. **Get composer ID**
- User provides, or find most recent for this skill
2. **Call cursor-mirror's sister script**
python3 skills/cursor-mirror/scripts/cursor_mirror.py deep-snitch --composer {id} --yaml
3. **Load skill's declared behavior**
- From CARD.yml: tools, dependencies
4. **Compare declared vs actual**
- Tools used but not declared?
- Paths accessed outside workspace?
- Network calls made?
5. **Output discrepancies**
DECLARED: tools: [read_file, write_file]
OBSERVED: tools: [read_file, write_file, Shell, WebSearch]
UNDECLARED: [Shell, WebSearch]
Observe Protocol (Deep Runtime)
## OBSERVE Protocol
Full runtime observation with cursor-mirror. Requires user cooperation.
1. **User exercises the skill**
- User must actually invoke and use the skill
- Test various features, edge cases, inputs
- This requires trusting the skill enough to run it
2. **Gather runtime data via cursor-mirror's sister script**
(skill-snitch invokes these internally)
python3 skills/cursor-mirror/scripts/cursor_mirror.py tools <composer> --yaml
python3 skills/cursor-mirror/scripts/cursor_mirror.py context-sources <composer> --yaml
python3 skills/cursor-mirror/scripts/cursor_mirror.py timeline <composer> --yaml
python3 skills/cursor-mirror/scripts/cursor_mirror.py deep-snitch --composer <id> --yaml
3. **Analyze captured activity**
Tool Analysis:
- All tools called vs CARD.yml declared
- Flag undeclared usage
- Check parameters (paths, URLs, commands)
File Analysis:
- All reads/writes — within workspace?
- Any ~/.ssh, ~/.aws, .env, credentials access?
Network Analysis:
- WebSearch, WebFetch, curl, wget?
- Known services or suspicious endpoints?
Secret Scanning:
- API key patterns in tool results
- Tokens, passwords in output
- Base64-encoded sensitive data
4. **Generate runtime report**
RUNTIME OBSERVATION: skills/target-skill/
Session: <composer-id>
TOOL CALLS:
- read_file: 12 (DECLARED)
- Shell: 2 (UNDECLARED!)
FILES ACCESSED:
- skills/target-skill/*.yml (expected)
- ~/.ssh/config (SUSPICIOUS!)
VERDICT: YELLOW - undeclared Shell usage
Like Little Snitch watches your network, skill-snitch watches skill behavior via cursor-mirror.
Trust Assessment
Trust tiers:
- 🟢 GREEN - Verified safe, all checks pass
- 🔵 BLUE - Trusted source, minor warnings
- 🟡 YELLOW - Caution, review recommended
- 🟠 ORANGE - Suspicious, manual review required
- 🔴 RED - Dangerous, do not use
## TRUST Protocol
1. Run SCAN (static)
2. Run SNITCH (runtime) if composer provided
3. Check trust-overrides.yml for manual override
4. Calculate trust tier:
RED if:
- Any CRITICAL finding
- Reverse shell pattern
- Undeclared network + secrets access
ORANGE if:
- HIGH findings without explanation
- Undeclared shell commands
- No CARD.yml
YELLOW if:
- MEDIUM findings
- Undeclared dependencies
- External URLs
BLUE if:
- Only LOW/INFO findings
- All tools declared
- Known publisher
GREEN if:
- No findings
- Consistency check passes
- Trust override from user
5. Cache result in trust-cache.yml
6. Output assessment with K-REFs to concerns
Consistency Check
## CONSISTENCY Protocol
Verify all parts of skill agree:
1. **INDEX.yml ↔ CARD.yml**
- Skill listed in skills/INDEX.yml?
- Path correct?
- Description matches?
2. **CARD.yml ↔ SKILL.md**
- All methods documented?
- Tools match?
3. **CARD.yml ↔ Implementation**
- Declared dependencies imported?
- Declared tools used?
- Undeclared tools/deps?
4. **SKILL.md ↔ README.md**
- Examples current?
- No deprecated features mentioned?
Output drift findings as K-REFs.
Startup Scan (Optional)
If enabled in config.yml:
## SKILL-SCAN Advertisement
On session start:
1. Check config.yml: startup_scan.enabled
2. If enabled:
- List all skills in skills/
- Quick scan each (CARD.yml + *.py only)
- Report summary:
🔍 SKILL-SCAN: 12 skills scanned
✅ 10 clean
⚠️ 2 warnings:
skills/untrusted/ - no CARD.yml
skills/fetcher/ - undeclared network
Integration with cursor-mirror
skill-snitch composes with cursor-mirror. The LLM orchestrates both skills together — you invoke skill-snitch by prompting:
"Snitch on skills/adventure/ in session abc123"
"Observe what skills/target-skill/ did"
Internally, the skill uses cursor-mirror's sister script:
# Deep audit
python3 skills/cursor-mirror/scripts/cursor_mirror.py deep-snitch --composer ID --yaml
# Pattern scan
python3 skills/cursor-mirror/scripts/cursor_mirror.py audit --patterns secrets,shell_exfil --yaml
# Tool usage
python3 skills/cursor-mirror/scripts/cursor_mirror.py tools ID --yaml
The skill abstracts the CLI — you don't need to remember the syntax.
Scan Methodology
See SCAN-METHODOLOGY.md for the two-phase approach:
-
Phase 1: Bash Scripts — Fast structural scan of ALL skills
- Structure check (CARD.yml, SKILL.md, README.md)
- Script detection (.py, .js files)
- Pattern grep (exec, eval, password, etc.)
- Tool tier extraction
-
Phase 2: LLM Batched Review — Deep read in batches of 5
- Actually READ files, don't just grep
- Scripts get full code review
- Context determines if patterns are dangerous
- Trust assessment per skill
Golden Rule: Grep finds. LLM understands.