Skill: adamthompson33/moltcops-skill (version 1.0.0)

--- name: moltcops version: 1.0.0 description: Pre-install security scanner for AI agent skills. Detects malicious patterns before you trust code. Local-first — code never leaves your machine. --- # MoltCops — Skill Security Scanner Scan any skill for security threats **before** you install it. Detects prompt injection, data exfiltration, sleeper triggers, drain patterns, and 16 more threat categories. **Local-first.** Your code never leaves your machine. No API calls. No uploads. No accounts. ## When to Use - **Before installing any skill** from ClawHub, GitHub, or other sources - **Before running** skills shared by other agents - **When evaluating** unknown code from any source - **After ClawHavoc**: 341 malicious skills were found on ClawHub this week. Scan first. ## How to Run ```bash python3 scripts/scan.py <path-to-skill-folder> ``` Example: ```bash # Scan a skill before installing python3 scripts/scan.py ~/.openclaw/skills/suspicious-skill # Scan a freshly downloaded skill python3 scripts/scan.py ./my-new-skill ``` **No dependencies required** — uses only Python 3 standard library. ## Reading Results The scanner returns three verdicts: | Verdict | Exit Code | Meaning | |---------|-----------|---------| | **PASS** | 0 | No critical or high-risk threats detected. Safe to install. | | **WARN** | 1 | High-risk patterns found. Review findings before installing. | | **BLOCK** | 2 | Critical threats detected. Do NOT install this skill. | ## What It Detects 20 detection rules across these threat categories: | Category | Rules | Examples | |----------|-------|---------| | **Prompt Injection** | MC-001, MC-002, MC-003 | System prompt override, jailbreak payloads, tool-use steering | | **Code Injection** | MC-004, MC-005, MC-006, MC-019 | Shell injection, eval/exec, base64-to-exec, child_process | | **Data Exfiltration** | MC-007, MC-008, MC-009, MC-010, MC-020 | Webhook URLs, env var harvesting, SSH key access, credential files | | **Hardcoded Secrets** | MC-011, MC-012 | API keys in source, private key material | | **Financial** | MC-013 | Drain patterns, unlimited withdrawals | | **Lateral Movement** | MC-014 | Git credential access, repo manipulation | | **Persistence** | MC-015, MC-016 | SOUL.md writes, cron job creation | | **Autonomy Abuse** | MC-017 | Destructive force flags (rm -rf, git push --force) | | **Infrastructure** | MC-018 | Permission escalation (sudo, chmod 777) | ## False Positive Handling The scanner includes context-aware filtering to reduce false positives: - **Env var access** (MC-008): Only flags when variable names contain KEY, SECRET, PASSWORD, TOKEN, or CREDENTIAL - **Git operations** (MC-014): Skips standard remotes (github.com, gitlab.com, bitbucket.org) - **Force flags** (MC-017): Only flags on destructive operations, not install scripts ## Example Output ``` MoltCops Security Scanner ======================================== Scanning: ./suspicious-skill Files: 5 Rules: 20 FINDINGS ---------------------------------------- [CRITICAL] MC-007: Exfiltration URL (main.py:14) [CRITICAL] MC-004: Shell Injection (helper.sh:8) [HIGH] MC-005: Dynamic Code Execution (main.py:22) SUMMARY ======================================== Files scanned: 5 Total findings: 3 Critical: 2 High: 1 Medium: 0 VERDICT: BLOCK Critical threats detected. Do NOT install this skill. ``` ## Web Scanner For a browser-based version with the same engine, visit: **https://scan.moltcops.com** ## About MoltCops MoltCops protects the AI agent ecosystem from malicious skills. While VirusTotal catches known malware signatures, MoltCops catches **behavioral patterns** — drain logic, sleeper triggers, prompt injection, and data exfiltration that signature-based scanning misses. - Web: https://moltcops.com - Moltbook: https://moltbook.com/u/MoltCops

path: rules.json
content: id: MC-001
name: System Prompt Override
category: Prompt Injection
severity: CRITICAL
pattern: (ignore|disregard|forget|override|bypass).{0,20}(previous|prior|above|system|original).{0,20}(instructions|rules|prompts|guidelines)
description: Attempts to override system instructions
confidence: 0.95

id: MC-002
name: Jailbreak Payload
category: Prompt Injection
severity: CRITICAL
pattern: (DAN.mode|developer.mode|jailbreak|do.anything.now|unlocked.mode|no.restrictions|bypass.safety)
description: Known jailbreak patterns
confidence: 0.9

id: MC-003
name: Tool-Use Steering
category: Prompt Injection
severity: CRITICAL
pattern: (secretly\s+(send|post|upload|execute|exfil)|silently\s+(send|post|upload|execute)|without\s+(telling|informing|notifying)\s+(the\s+)?(user|human))
description: Hijacking agent tool usage covertly
confidence: 0.92

id: MC-004
name: Shell Injection
category: Code Injection
severity: CRITICAL
pattern: (subprocess\.(call|run|Popen).*shell\s*=\s*True|os\.system\s*\(|os\.popen\s*\(|commands\.getoutput)
description: Shell injection via subprocess or os.system
confidence: 0.9

id: MC-005
name: Dynamic Code Execution
category: Code Injection
severity: HIGH
pattern: \b(eval|exec)\s*\(
description: eval() or exec() for dynamic code execution
confidence: 0.8

id: MC-006
name: Base64 Decode to Exec
category: Code Injection
severity: CRITICAL
pattern: (base64\.(b64decode|decodebytes)|atob).*?(exec|eval|system|popen|subprocess|Function\()
description: Base64 decode + execute — payload obfuscation
confidence: 0.95

id: MC-007
name: Exfiltration URL
category: Data Exfiltration
severity: CRITICAL
pattern: (ngrok\.io|ngrok-free\.app|webhook\.site|requestbin\.com|hookbin\.com|pipedream\.net|burpcollaborator|interact\.sh)
description: Outbound data via webhook/tunnel URLs
confidence: 0.95

id: MC-008
name: Env Var Key Access
category: Data Exfiltration
severity: MEDIUM
pattern: (os\.environ|os\.getenv|process\.env)
description: Reading sensitive environment variables
confidence: 0.7

id: MC-009
name: SSH Key Access
category: Data Exfiltration
severity: CRITICAL
pattern: (cat|read|open|load|send|post|upload).{0,40}(id_rsa|id_ed25519|id_ecdsa|id_dsa|\.pem|authorized_keys)
description: Reading SSH private keys
confidence: 0.9

id: MC-010
name: Credential File Access
category: Data Exfiltration
severity: HIGH
pattern: (/\.ssh/|/\.aws/|/\.config/.{0,20}credentials|\.env\b|/\.netrc|/\.npmrc)
description: Accessing known credential file paths
confidence: 0.85

id: MC-011
name: Hardcoded API Key
category: Hardcoded Secrets
severity: HIGH
pattern: (sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36}|glpat-[a-zA-Z0-9_-]{20}|xox[bpsa]-[a-zA-Z0-9-]{10,})
description: Hardcoded API keys (OpenAI, AWS, GitHub, GitLab, Slack)
confidence: 0.9

id: MC-012
name: Private Key Material
category: Hardcoded Secrets
severity: CRITICAL
pattern: (BEGIN\s+(RSA\s+)?PRIVATE\s+KEY|BEGIN\s+EC\s+PRIVATE)
description: Private key material in source code
confidence: 0.95

id: MC-013
name: Drain Pattern
category: Financial
severity: CRITICAL
pattern: (transfer.*all|withdraw.*unlimited|drain.*wallet|sweep.*funds|send.*entire.*balance)
description: Cryptocurrency drain patterns
confidence: 0.88

id: MC-014
name: Git Credential Access
category: Lateral Movement
severity: HIGH
pattern: (gh\s+auth|gh\s+repo\s+create|git\s+credential|git\s+remote\s+add)
description: Git credential or repository manipulation
confidence: 0.8

id: MC-015
name: SOUL.md Write
category: Persistence
severity: CRITICAL
pattern: (write|append|modify|overwrite|edit).{0,30}(SOUL\.md|system.prompt|\.clawdrc|personality)
description: Writing to agent identity or system prompt files
confidence: 0.92

id: MC-016
name: Scheduled Task Creation
category: Persistence
severity: HIGH
pattern: (crontab|schtasks|systemctl\s+enable|launchctl\s+load)
description: Creating scheduled tasks for persistence
confidence: 0.8

id: MC-017
name: Force Flag Abuse
category: Autonomy Abuse
severity: HIGH
pattern: (rm\s+.*--force|rm\s+-rf|git\s+push\s+.*--force|git\s+reset\s+--hard)
description: Destructive force flags
confidence: 0.75

id: MC-018
name: Permission Escalation
category: Infrastructure
severity: CRITICAL
pattern: (sudo\s+|chmod\s+777|chmod\s+\+s|chown\s+root|setuid)
description: Privilege escalation attempts
confidence: 0.9

id: MC-019
name: Child Process Spawn
category: Code Injection
severity: HIGH
pattern: (child_process|execSync\s*\(|execFile\s*\(|spawn\s*\()
description: Node.js child process execution
confidence: 0.75

id: MC-020
name: Outbound HTTP POST
category: Data Exfiltration
severity: MEDIUM
pattern: (requests\.post|httpx\.post|urllib\.request\.urlopen|fetch\s*\(.{0,30}POST)
description: HTTP POST to external endpoints
confidence: 0.55

path: scripts/scan.py
content: #!/usr/bin/env python3
"""MoltCops Security Scanner — local-first, no network calls."""
import json, os, re, sys

def load_rules(path):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)

def scan_file(filepath, rules):
    findings = []
    try:
        with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
            lines = f.readlines()
    except Exception:
        return findings

    for rule in rules:
        try:
            pat = re.compile(rule["pattern"], re.IGNORECASE if rule.get("case_insensitive", True) else 0)
        except re.error:
            continue

        for i, line in enumerate(lines, 1):
            if pat.search(line):
                # False positive filters
                if rule["id"] == "MC-008":
                    if not re.search(r"(KEY|SECRET|PASSWORD|TOKEN|CREDENTIAL)", line, re.IGNORECASE):
                        continue
                if rule["id"] == "MC-014":
                    if re.search(r"(github\.com|gitlab\.com|bitbucket\.org)", line, re.IGNORECASE):
                        continue
                if rule["id"] == "MC-017":
                    if not re.search(r"(rm\s|git push.*force|git reset|del\s|destroy|drop\s)", line, re.IGNORECASE):
                        continue

                findings.append({
                    "rule_id": rule["id"],
                    "rule_name": rule["name"],
                    "severity": rule["severity"],
                    "category": rule["category"],
                    "file": filepath,
                    "line": i,
                    "matched": line.strip()[:120],
                    "description": rule["description"]
                })
    return findings

def main():
    if len(sys.argv) != 2:
        print("Usage: python scan.py <path-to-skill-folder>")
        sys.exit(1)

    skill_path = sys.argv[1]
    if not os.path.isdir(skill_path):
        print(f"Error: {skill_path} is not a directory")
        sys.exit(1)

    script_dir = os.path.dirname(os.path.abspath(__file__))
    # Check same directory first (ClawHub flattens structure), then parent
    rules_path = os.path.join(script_dir, "rules.json")
    if not os.path.exists(rules_path):
        rules_path = os.path.join(os.path.dirname(script_dir), "rules.json")
    if not os.path.exists(rules_path):
        print("Error: rules.json not found")
        sys.exit(1)
    rules = load_rules(rules_path)

    print("MoltCops Security Scanner")
    print("=" * 40)
    print(f"Scanning: {skill_path}")

    # Find files
    exts = {".md", ".py", ".js", ".ts", ".sh", ".jsx", ".tsx"}
    files = []
    for root, dirs, fnames in os.walk(skill_path):
        dirs[:] = [d for d in dirs if d not in ("node_modules", ".git", "__pycache__")]
        for fn in fnames:
            if os.path.splitext(fn)[1] in exts:
                files.append(os.path.join(root, fn))

    print(f"Files: {len(files)}")
    print(f"Rules: {len(rules)}")
    print()

    all_findings = []
    for f in files:
        all_findings.extend(scan_file(f, rules))

    critical = sum(1 for f in all_findings if f["severity"] == "CRITICAL")
    high = sum(1 for f in all_findings if f["severity"] == "HIGH")
    medium = sum(1 for f in all_findings if f["severity"] == "MEDIUM")

    if all_findings:
        print("FINDINGS")
        print("-" * 40)
        for f in all_findings:
            sev = f["severity"]
            color = {"CRITICAL": "\033[0;31m", "HIGH": "\033[1;33m", "MEDIUM": "\033[0;34m"}.get(sev, "")
            nc = "\033[0m"
            rel = os.path.relpath(f["file"], skill_path)
            print(f"{color}[{sev}]{nc} {f['rule_id']}: {f['rule_name']} ({rel}:{f['line']})")
        print()

    print("SUMMARY")
    print("=" * 40)
    print(f"Files scanned: {len(files)}")
    print(f"Total findings: {len(all_findings)}")
    print(f"  Critical: {critical}")
    print(f"  High:     {high}")
    print(f"  Medium:   {medium}")
    print()

    if critical > 0:
        print("\033[0;31mVERDICT: BLOCK\033[0m")
        print("Critical threats detected. Do NOT install this skill.")
        sys.exit(2)
    elif high > 0:
        print("\033[1;33mVERDICT: WARN\033[0m")
        print("High-risk patterns found. Review before installing.")
        sys.exit(1)
    else:
        print("\033[0;32mVERDICT: PASS\033[0m")
        print("No critical or high-risk threats detected.")
        sys.exit(0)

if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)