Skill: johstracke/security-checker (version 1.0.1)

--- name: security-checker description: Security scanner for Python skills before publishing to ClawHub. Use before publishing any skill to check for dangerous imports, hardcoded secrets, unsafe file operations, and dangerous functions like eval/exec/subprocess. Essential for maintaining trust and ensuring published skills are safe for others to install and run. --- # Security Checker Security scan Python skills before publishing to ensure code safety. ## Quick Start ```bash security_scan.py <file_or_directory> ``` **Examples:** ```bash # Scan a single Python file security_scan.py scripts/my_script.py # Scan an entire skill directory security_scan.py /path/to/skill-folder # Scan multiple skills security_scan.py skills/ ``` ## What It Checks ### Dangerous Imports Detects imports that could be used maliciously: - `os` - System-level operations - `subprocess` - Command execution - `shutil` - File operations - `socket` - Network operations - `urllib` / `requests` - HTTP requests **Why dangerous?** These imports enable system command execution, file manipulation, and network access that could be exploited. ### Dangerous Functions Detects potentially unsafe function calls: - `os.system()` - Executes shell commands - `subprocess.call()`, `subprocess.run()`, `subprocess.Popen()` - Command execution - `eval()` - Executes arbitrary code - `exec()` - Executes arbitrary code **Why dangerous?** These can execute arbitrary commands or code, leading to remote code execution vulnerabilities. ### Hardcoded Secrets Detects tokens, keys, and passwords: - API keys - Auth tokens (including ClawHub tokens) - Passwords - Private keys - JWT-like tokens **Why dangerous?** Secrets leaked in published code can be stolen and abused. ### Unsafe File Operations Detects risky file access patterns: - Absolute file paths outside expected directories - Parent directory traversal (`..`) - Writing to system directories **Why dangerous?** Could lead to unintended file access, data loss, or system modification. ## Usage Pattern: Pre-Publish Checklist Before publishing any skill: ```bash # 1. Run security scan security_scan.py /path/to/skill # 2. Review any warnings # If warnings appear, fix the code or document why it's safe # 3. Re-scan after fixes security_scan.py /path/to/skill # 4. Only publish if scan passes clawhub publish /path/to/skill --slug my-skill ... ``` ## Interpretation of Results ### ✅ "No security issues found" Code appears safe. Proceed with publishing. ### ⚠️ "Warning" (Yellow) Potentially risky pattern detected. Review the specific line and decide: - **Is it legitimate?** Document why in code comments or SKILL.md - **Can it be avoided?** Refactor to safer alternatives - **Is it necessary?** Clearly document the risk and purpose ### 🔴 "Possible hardcoded secret" Secret detected. Before publishing: - Remove the secret - Use environment variables instead: `os.getenv('API_KEY')` - Document required env variables in SKILL.md - Never commit real secrets ## Examples ### Legitimate os module usage (documented) ```python import os # Used only for path.join() - safe file path construction workspace = os.path.join(os.path.expanduser("~"), ".openclaw", "workspace") ``` **Scan result:** ⚠️ Warning about os import **Action:** Document safe usage pattern in code comments ### Hardcoded secret (must fix) ```python [REDACTED_SECRET]" # DON'T DO THIS ``` **Scan result:** 🔴 Possible hardcoded secret **Action:** Remove and use environment variable: ```python [REDACTED_SECRET]"MY_SKILL_API_KEY") # Document in SKILL.md: Requires MY_SKILL_API_KEY environment variable ``` ### Safe pattern (no issues) ```python # JSON storage for local data only data = {"notes": [], "metadata": {}} with open("data.json", "w") as f: json.dump(data, f) ``` **Scan result:** ✅ No issues ## Best Practices 1. **Always scan before publishing** - Make it part of your workflow 2. **Review warnings manually** - The scanner can't judge context 3. **Use environment variables for secrets** - Never hardcode 4. **Prefer json over eval** - Safe parsing vs code execution 5. **Document necessary risks** - If dangerous code is required, explain why 6. **Minimize dangerous imports** - Only use what's truly necessary 7. **Keep code simple** - Complex code is harder to audit ## Integration with Development Workflow ### Before committing to repo ```bash # Pre-commit hook concept python3 /path/to/security_scan.py scripts/ if [ $? -ne 0 ]; then echo "❌ Security scan failed. Fix issues before committing." exit 1 fi ``` ### Automated pre-publish check ```bash #!/bin/bash # publish-safe.sh SKILL_PATH=$1 echo "🔒 Running security scan..." python3 /path/to/security_scan.py "$SKILL_PATH" if [ $? -ne 0 ]; then echo "❌ Cannot publish: Security scan failed" exit 1 fi echo "✅ Security scan passed" clawhub publish "$SKILL_PATH" ``` ## Limitations This scanner: - **Can't judge context** - Some dangerous code may be legitimate - **Static analysis only** - Doesn't execute code - **Python-focused** - Other languages need different tools - **Basic patterns** - Sophisticated obfuscation may evade detection **Complement with:** - Manual code review - Testing in isolated environment - Reading through all code before publishing - Using additional tools: `bandit`, `safety` ## Trust Building Publishing skills that pass security scans builds trust in the community: - Users know you care about safety - Your reputation improves - Skills get adopted more readily - ClawHub may highlight safe skills ## Examples of Published Skills (All Scanned) ```bash # research-assistant security_scan.py /home/ubuntu/.openclaw/workspace/skills/research-assistant # ✅ All clear # task-runner security_scan.py /home/ubuntu/.openclaw/workspace/skills/task-runner # ✅ All clear # security-checker security_scan.py /home/ubuntu/.openclaw/workspace/skills/security-checker # ✅ All clear ``` All three skills passed security scans before publishing to ClawHub.

path: scripts/security_scan.py
content: #!/usr/bin/env python3
"""
Security scanner for Python scripts before publishing
Checks for:
- Dangerous imports (os.system, subprocess, etc.)
- Hardcoded secrets/tokens
- File operations outside workspace
- Network calls
"""

import re
import sys
from pathlib import Path

# Patterns that might indicate security issues
DANGEROUS_IMPORTS = [
    r'import\s+os\b',
    r'from\s+os\s+import',
    r'import\s+subprocess\b',
    r'from\s+subprocess\s+import',
    r'import\s+shutil\b',
    r'from\s+shutil\s+import',
    r'import\s+socket\b',
    r'from\s+socket\s+import',
    r'import\s+urllib\b',
    r'from\s+urllib\s+import',
    r'import\s+requests\b',
]

DANGEROUS_FUNCTIONS = [
    r'\bos\.system\s*\(',
    r'\bsubprocess\.call\s*\(',
    r'\bsubprocess\.run\s*\(',
    r'\bsubprocess\.Popen\s*\(',
    r'\beval\s*\(',
    r'\bexec\s*\(',
]

SECRET_PATTERNS = [
    r'api[_-]?key\s*=\s*["\']([^"\']{20,})["\']',
    r'token\s*=\s*["\']([^"\']{20,})["\']',
    r'password\s*=\s*["\']([^"\']{8,})["\']',
    r'secret\s*=\s*["\']([^"\']{8,})["\']',
    r'private[_-]?key\s*=\s*["\']([^"\']{20,})["\']',
    r'clh_[A-Za-z0-9_]{30,}',  # ClawHub tokens
    r'Bearer\s+[A-Za-z0-9\-._~+/]+=*',  # JWT-like tokens
]

FILE_PATTERNS = [
    r'\bopen\s*\([^)]*["\']\s*[/\\]',  # Absolute paths
    r'\bPath\s*\([^)]*\.\.',  # Parent directory traversal
    r'\.write_text\s*\(',  # File write operations
    r'\.write_bytes\s*\(',  # Binary file write
    r'\bopen\s*\([^)]*["\'].*["\'].*\b["\']w',  # Open for writing
]

def scan_file(filepath):
    """Scan a single Python file for security issues."""
    issues = []
    
    try:
        with open(filepath, 'r') as f:
            content = f.read()
            lines = content.split('\n')
    except Exception as e:
        return [f"ERROR: Could not read file: {e}"]
    
    # Check dangerous imports
    for pattern in DANGEROUS_IMPORTS:
        matches = list(re.finditer(pattern, content, re.MULTILINE))
        for match in matches:
            line_num = content[:match.start()].count('\n') + 1
            issues.append(f"⚠️  Line {line_num}: Dangerous import: {match.group()}")
    
    # Check dangerous functions
    for pattern in DANGEROUS_FUNCTIONS:
        matches = list(re.finditer(pattern, content, re.MULTILINE))
        for match in matches:
            line_num = content[:match.start()].count('\n') + 1
            issues.append(f"⚠️  Line {line_num}: Dangerous function: {match.group()}")
    
    # Check for hardcoded secrets
    for pattern in SECRET_PATTERNS:
        matches = list(re.finditer(pattern, content, re.MULTILINE))
        for match in matches:
            line_num = content[:match.start()].count('\n') + 1
            # Don't show the actual secret
            issues.append(f"🔴 Line {line_num}: Possible hardcoded secret detected")
    
    # Check file operations
    for pattern in FILE_PATTERNS:
        matches = list(re.finditer(pattern, content, re.MULTILINE))
        for match in matches:
            line_num = content[:match.start()].count('\n') + 1
            issues.append(f"⚠️  Line {line_num}: Potentially unsafe file operation: {match.group()}")
    
    return issues

def scan_directory(directory):
    """Scan all Python files in a directory."""
    issues = []
    directory = Path(directory)
    
    for py_file in directory.rglob('*.py'):
        print(f"\n🔍 Scanning: {py_file.relative_to(directory)}")
        file_issues = scan_file(py_file)
        
        if file_issues:
            issues.append((str(py_file.relative_to(directory)), file_issues))
            for issue in file_issues:
                print(f"  {issue}")
        else:
            print("  ✅ No issues found")
    
    return issues

def main():
    if len(sys.argv) < 2:
        print("Security Scanner - Scan Python code before publishing")
        print("\nUsage: security_scan.py <file_or_directory>")
        print("\nChecks for:")
        print("  • Dangerous imports (os, subprocess, etc.)")
        print("  • Dangerous functions (eval, exec, system)")
        print("  • Hardcoded secrets/tokens")
        print("  • Unsafe file operations")
        return
    
    target = Path(sys.argv[1])
    
    print(f"\n🔒 Security Scan: {target}")
    print("=" * 60)
    
    if target.is_file():
        issues = scan_file(target)
        if issues:
            print(f"\n⚠️  Found {len(issues)} issue(s):\n")
            for issue in issues:
                print(issue)
        else:
            print("\n✅ No security issues found")
    elif target.is_dir():
        all_issues = scan_directory(target)
        total_issues = sum(len(file_issues) for _, file_issues in all_issues)
        
        print("\n" + "=" * 60)
        if total_issues > 0:
            print(f"\n⚠️  Found {total_issues} total issue(s) across {len(all_issues)} file(s)")
        else:
            print("\n✅ All clear - No security issues found")
    else:
        print(f"Error: {target} is not a valid file or directory")

if __name__ == "__main__":
    main()


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