Skill: ttttstc/skill-safety-verifier (version 0.1.1)

--- name: skill-safety-verifier description: | Security-first skill vetting for AI agents. Use before installing any skill from ClawdHub, GitHub, or other sources. Checks for red flags, permission scope, and suspicious patterns. Provides risk assessment and safety recommendations. trigger: install.*skill|skill.*install|safety.*check|安全.*检查| vetting.*skill| skill.*risk --- # Skill Safety Verifier > Security-first skill vetting for AI agents ## Overview **Skill Safety Verifier** 是一个安全优先的技能审查工具，用于在安装任何外部技能前进行安全检查。 ### Why This Matters AI agents run with elevated permissions and can: - Execute arbitrary commands - Access file systems - Make network requests - Read environment variables **Unvetted skills = security risk** --- ## Workflow ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ SKILL SAFETY VERIFIER FLOW │ └─────────────────────────────────────────────────────────────────────────┘ User Request │ ▼ ┌─────────────────────┐ │ 1. Fetch Skill │ │ - Clone/Extract │ │ - Read SKILL.md │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 2. Parallel Scan │ │ ┌───────────────┐ │ │ │ Socket Check │ │ ← Check outbound connections │ └───────────────┘ │ │ ┌───────────────┐ │ │ │ Vuln Scan │ │ ← Query GitHub Advisory API │ └───────────────┘ │ │ ┌───────────────┐ │ │ │ Code Patterns │ │ ← Scan dangerous functions │ └───────────────┘ │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 3. Risk Score │ │ - Calculate total │ ← Network + Vuln + Permission │ - Classify level │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 4. Present Result │ │ - Risk Radar │ │ - Warnings │ │ - Recommendation │ └─────────────────────┘ │ ▼ User Decision ``` --- ## Core Responsibilities ### 1. Risk Assessment Evaluate skills across three dimensions: | Dimension | What to Check | Risk Level | |-----------|---------------|------------| | **Socket/Network** | Outbound connections, API calls | Critical | | **Code Quality** | Snyk vulnerabilities, dependencies | High | | **Permissions** | File access, command execution | High | ### 2. Red Flag Detection Common suspicious patterns: ```yaml red_flags: network: - "Suspicious domain (non-standard TLD)" - "Hardcoded IP addresses" - "DNS lookups to unknown domains" - "Excessive outbound connections" execution: - "os.system() / subprocess without sanitization" - "eval() / exec() usage" - "Shell injection patterns" - "Download and execute code" data: - "Reading sensitive env vars" - "Exfiltrating files" - "Logging keystrokes" - "Credential harvesting" permissions: - "Overbroad file permissions" - "sudo/root without prompt" - "Full disk access requests" ``` ### 3. Risk Classification ``` ┌─────────────────────────────────────────────────────────────┐ │ RISK CLASSIFICATION │ ├──────────────┬──────────────┬─────────────────────────────┤ │ Level │ Score │ Action │ ├──────────────┼──────────────┼─────────────────────────────┤ │ 🟢 Safe │ 0-10 │ Install & use freely │ │ 🟡 Low │ 11-30 │ Install with caution │ │ 🟠 Medium │ 31-60 │ Review code, then decide │ │ 🔴 High │ 61-100 │ Do not install │ └──────────────┴──────────────┴─────────────────────────────┘ ``` --- ## Assessment Criteria ### Socket/Network Risk | Alert Count | Risk Score | |-------------|------------| | 0 | 0 | | 1-2 | 10 | | 3-5 | 25 | | >5 | 50 | ### Code/Vulnerability Risk | Severity | Score | |----------|-------| | Critical | 25 | | High | 15 | | Medium | 10 | | Low | 5 | ### GitHub Advisory API Integration 使用 GitHub Advisory API 获取真实漏洞数据，不阻塞安装流程。 **API**: `https://api.github.com/advisories` **认证**: 无需 Token（匿名 60次/小时） **TTL**: 本地缓存 24 小时 **流程**: ``` 1. 用户触发安装 → 立即返回 "安检中..." 2. 后台并行: 克隆代码 + 查缓存 + 异步请求 API 3. 缓存命中? Yes → 直接返回 | No → 等 API (超时 5s) 4. 合并 Socket分析 + 代码模式 + 漏洞数据 → 呈现 ``` ### Permission Scope Risk | Scope | Score | |-------|-------| | Read-only | 0 | | Network calls | 10 | | File write | 15 | | Command execution | 25 | | Full system access | 50 | --- ## Workflow ``` User: "Install skill X" │ ▼ ┌─────────────────────┐ │ 1. Fetch Skill │ │ - Clone/Extract │ │ - Read SKILL.md │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 2. Security Scan │ │ - Socket alerts │ │ - Snyk vulnerabilities│ │ - Code patterns │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 3. Risk Score │ │ - Calculate total │ │ - Classify level │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ 4. Present Result │ │ - Risk level │ │ - Warnings │ │ - Recommendation │ └─────────────────────┘ │ ▼ User Decision ``` --- ## Output Format ### Progress Bar Style (Recommended) ``` ✅ weather - Risk Assessment ┌─────────────────────────────────────────┐ │ 📊 Risk Radar │ ├─────────────────────────────────────────┤ │ Network [░░░░░░░░░░░] 0/50 🟢 │ │ Vulnerabil. [░░░░░░░░░░░░] 0/25 🟢 │ │ Permissions [░░░░░░░░░░░░] 0/50 🟢 │ │ ───────────────────────────────────── │ │ TOTAL [░░░░░░░░░░░░] 0/100 🟢 │ ├─────────────────────────────────────────┤ │ Dependencies: curl, wttr.in │ │ Recommendation: ✅ Safe to install │ └─────────────────────────────────────────┘ ``` ### Implementation Logic ```python # Progress bar rendering def render_bar(score, max_score, label, width=10): percent = score / max_score filled = int(percent * width) bar = '█' * filled + '░' * (width - filled) emoji = get_risk_emoji(percent) return f" {label:<12} [{bar}] {score}/{max_score} {emoji}" # Color mapping def get_risk_emoji(percent): if percent < 0.2: return '🟢' if percent < 0.5: return '🟡' if percent < 0.8: return '🟠' return '🔴' ``` ### Safe Skill ``` ✅ skill-name - Risk Assessment Complete ┌─────────────────────────────────┐ │ 🟢 LOW RISK │ │ Socket Alerts: 0 │ │ Snyk Issues: 0 │ │ Code Patterns: Clean │ └─────────────────────────────────┘ Recommendation: Safe to install and use. What's included: - Feature A - Feature B ``` ### Medium Risk Skill ``` ⚠️ skill-name - Risk Assessment Complete ┌─────────────────────────────────┐ │ 🟠 MEDIUM RISK │ │ Socket Alerts: 2 │ │ Snyk Issues: 1 (Medium) │ │ Notes: Network calls detected │ └─────────────────────────────────┘ Warnings: - Makes API calls to external service - Stores credentials in config file Recommendation: Review the code before installing. ``` ### High Risk Skill ``` 🚫 skill-name - Risk Assessment Complete ┌─────────────────────────────────┐ │ 🔴 HIGH RISK │ │ Socket Alerts: 5 │ │ Snyk Issues: 3 (Critical) │ │ Red Flags: Command execution │ └─────────────────────────────────┘ 🚨 DO NOT INSTALL Reasons: 1. Multiple critical vulnerabilities found 2. Executes shell commands 3. No input sanitization If you must proceed: - Review every line of code - Run in sandboxed environment - Never expose credentials ``` --- ## Usage ### Pre-Installation Check ```bash # Before installing any skill skill-safety-check --source <skill-url> # Example skill-safety-check --source github.com/user/skill-repo ``` ### Post-Installation Verification ```bash # Verify installed skill skill-safety-check --verify --skill-name <name> ``` ### Interactive Mode ``` User: "Install jina-cli" Agent: [Runs skill-safety-verifier] → Analyzes skill → Presents risk assessment → User decides ``` --- ## Integration ### With ClawdHub ```bash # Install with auto-verification npx skills add <owner/repo> --verify ``` ### With OpenClaw The verifier runs automatically before skill installation when enabled in config. --- ## Configuration ### Settings ```yaml # .openclaw/config.yaml skill_safety: enabled: true auto_check: true block_high_risk: false # Set true to auto-block scan_depth: full # quick | full ``` ### Environment Variables ```bash SKILL_SAFETY_LOG_LEVEL=info SKILL_SAFETY_CACHE_TTL=3600 ``` --- ## Best Practices 1. **Always verify** - Never install unvetted skills 2. **Read the code** - Automated checks aren't enough 3. **Least privilege** - Only grant necessary permissions 4. **Isolate** - Run high-risk skills in containers 5. **Monitor** - Log all skill activity --- ## Related - [ClawdHub](https://clawhub.com) - Skill marketplace - [OpenClaw Security](https://docs.openclaw.ai/security) - Platform security docs - [Skill Writing Guide](https://docs.openclaw.ai/skills/write) - How to write safe skills --- ## License MIT License - See LICENSE file for details

path: README.md
content: # Skill Safety Verifier

**[English](README.md) | [Chinese](README_zh.md)**

> Security-first skill vetting for AI agents. Use before installing any skill from ClawdHub, GitHub, or other sources.

## Why This Matters

AI agents run with elevated permissions and can:
- Execute arbitrary commands
- Access file systems
- Make network requests
- Read environment variables

**Unvetted skills = security risk**

## Features

- Code Pattern Scanning - Detect dangerous code (exec/eval/subprocess)
- GitHub Advisory API - Check dependencies for known CVEs
- Permission Analysis - Analyze required permissions from config
- Risk Classification - Classify skills as Safe/Low/Medium/High
- Risk Radar Visualization - Progress bar with color-coded scores

---

## Prerequisites

### Python Version
- Python 3.8 or higher

### Required Dependencies
```bash
# Option 1: Install via pip
pip install -r requirements.txt

# Option 2: Install via setup.py
pip install -e .

# Option 3: Manual install
pip install requests>=2.28.0
```

### Optional (for development)
```bash
pip install pytest pytest-cov
```

---

## Quick Start

### Installation

#### Option 1: Clone and install
```bash
git clone https://github.com/ttttstc/skill-safety-verifier.git
cd skill-safety-verifier
pip install -e .
```

#### Option 2: Via ClawdHub
```bash
npx skills add ttttstc/skill-safety-verifier
```

#### Option 3: Direct download
```bash
# Just download and use
wget https://raw.githubusercontent.com/ttttstc/skill-safety-verifier/main/analyzer.py
wget https://raw.githubusercontent.com/ttttstc/skill-safety-verifier/main/risk_radar.py
```

### Usage

```bash
# Option 1: After pip install
skill-safety-check /path/to/skill

# Option 2: Direct Python
python analyzer.py /path/to/skill
python3 analyzer.py /path/to/skill

# Output JSON
python analyzer.py /path/to/skill --json

# Help
python analyzer.py --help
```

---

## Project Structure

```
skill-safety-verifier/
├── bin/                      # Entry point scripts
│   └── skill-safety-check   # CLI launcher
├── analyzer.py               # Main analyzer
├── risk_radar.py             # Risk radar renderer
├── requirements.txt           # Python dependencies
├── setup.py                  # Package configuration
├── SKILL.md                  # OpenClaw skill definition
├── README.md                 # English documentation
├── README_zh.md              # Chinese documentation
└── LICENSE                  # MIT license
```

---

## Workflow

```
User triggers installation
    |
    v
1. Clone skill repository
    |
    v
2. Parallel scanning:
   - Scan code patterns (exec/eval/subprocess)
   - Check GitHub Advisory API
   - Analyze permissions
    |
    v
3. Calculate risk scores
    |
    v
4. Render Risk Radar
    |
    v
User decision
```

---

## Risk Classification

| Level | Score | Color | Action |
|-------|-------|-------|--------|
| Safe | 0-10 | Green | Install & use freely |
| Low | 11-30 | Yellow | Install with caution |
| Medium | 31-60 | Orange | Review code first |
| High | 61-100 | Red | Do not install |

---

## Output Examples

### Safe Skill

```
skill-name - Risk Assessment

Risk Radar:
  Network      0/50  Green
  Vulnerabil. 0/25  Green
  Permissions 0/50  Green
  Total       0/100 Green

Recommendation: Safe to install
```

### Medium Risk Skill

```
skill-name - Risk Assessment

Risk Radar:
  Network      20/50 Yellow
  Vulnerabil.  5/25  Green
  Permissions 25/50 Orange
  Total        50/100 Orange

Warning: Network calls detected
Recommendation: Review code before install
```

---

## Configuration

### Cache Directory
Default: `~/.cache/skill-safety/`

### Cache TTL
- Advisory data: 24 hours
- Skill dependencies: 6 hours

### GitHub API
- No authentication required (60 requests/hour)
- With authentication: 5000 requests/hour

---

## Integration

### With ClawdHub

```bash
# Install with auto-verification
npx skills add <owner/repo> --verify
```

### In Your Own Pipeline

```python
from analyzer import analyze_skill

result = analyze_skill('/path/to/skill')
print(result['scores'])
# {'network': 0, 'vuln': 0, 'permission': 25, 'total': 25}
```

---

## Best Practices

1. Always verify - Never install unvetted skills
2. Read the code - Automated checks aren't enough
3. Least privilege - Only grant necessary permissions
4. Isolate - Run high-risk skills in containers
5. Monitor - Log all skill activity

---

## Troubleshooting

### "No module named 'requests'"
```bash
pip install requests
```

### "SSL certificate verify failed"
The analyzer uses SSL verification by default. If you encounter issues, you can disable SSL verification (not recommended for production).

### "API rate limit exceeded"
Wait an hour or use a GitHub token for higher rate limits.

---

## Related

- ClawdHub (https://clawhub.com) - Skill marketplace
- GitHub Advisory API (https://api.github.com/advisories)
- OpenClaw Docs (https://docs.openclaw.ai)

---

## License

MIT License - see LICENSE file for details.


path: README_zh.md
content: # Skill Safety Verifier

**[English](README.md) | [中文](README_zh.md)**

> AI Agent 安全审查工具。在安装任何来自 ClawdHub、GitHub 或其他来源的 Skill 前进行安全检查。

## 为什么重要

AI Agent 以高级权限运行，可以：
- 执行任意命令
- 访问文件系统
- 发起网络请求
- 读取环境变量

**未经审查的 Skill = 安全风险**

## 功能特性

- 代码模式扫描 - 检测危险代码 (exec/eval/subprocess)
- GitHub Advisory API - 检查依赖项已知 CVE
- 权限分析 - 从配置文件分析所需权限
- 风险分类 - 将 Skill 分类为 Safe/Low/Medium/High
- Risk Radar 可视化 - 进度条 + 颜色评分

---

## 前置依赖

### Python 版本
- Python 3.8 或更高

### 必需依赖
```bash
# 方式 1: 通过 pip 安装
pip install -r requirements.txt

# 方式 2: 通过 setup.py 安装
pip install -e .

# 方式 3: 手动安装
pip install requests>=2.28.0
```

### 可选依赖（开发用）
```bash
pip install pytest pytest-cov
```

---

## 快速开始

### 安装

#### 方式 1: 克隆并安装
```bash
git clone https://github.com/ttttstc/skill-safety-verifier.git
cd skill-safety-verifier
pip install -e .
```

#### 方式 2: 通过 ClawdHub
```bash
npx skills add ttttstc/skill-safety-verifier
```

#### 方式 3: 直接下载使用
```bash
# 仅下载并使用
wget https://raw.githubusercontent.com/ttttstc/skill-safety-verifier/main/analyzer.py
wget https://raw.githubusercontent.com/ttttstc/skill-safety-verifier/main/risk_radar.py
```

### 使用

```bash
# 方式 1: 安装后使用
skill-safety-check /path/to/skill

# 方式 2: 直接 Python 运行
python analyzer.py /path/to/skill
python3 analyzer.py /path/to/skill

# 输出 JSON
python analyzer.py /path/to/skill --json

# 帮助
python analyzer.py --help
```

---

## 项目结构

```
skill-safety-verifier/
├── bin/                      # 入口脚本
│   └── skill-safety-check   # CLI 启动器
├── analyzer.py               # 主分析器
├── risk_radar.py             # Risk Radar 渲染器
├── requirements.txt           # Python 依赖
├── setup.py                  # 包配置
├── SKILL.md                  # OpenClaw skill 定义
├── README.md                 # 英文文档
├── README_zh.md              # 中文文档
└── LICENSE                  # MIT 许可证
```

---

## 工作流程

```
用户触发安装
    |
    v
1. 克隆 Skill 仓库
    |
    v
2. 并行扫描:
   - 扫描代码模式 (exec/eval/subprocess)
   - 检查 GitHub Advisory API
   - 分析权限
    |
    v
3. 计算风险评分
    |
    v
4. 渲染 Risk Radar
    |
    v
用户决定
```

---

## 风险分类

| 等级 | 分数 | 颜色 | 操作 |
|------|------|------|------|
| Safe | 0-10 | 绿色 | 自由安装使用 |
| Low | 11-30 | 黄色 | 谨慎安装 |
| Medium | 31-60 | 橙色 | 先审查代码 |
| High | 61-100 | 红色 | 禁止安装 |

---

## 输出示例

### 安全 Skill

```
skill-name - 风险评估

风险雷达:
  网络风险    0/50  绿色
  漏洞风险    0/25  绿色
  权限风险    0/50  绿色
  总分        0/100 绿色

建议: 安全，可安装
```

### 中风险 Skill

```
skill-name - 风险评估

风险雷达:
  网络风险    20/50 黄色
  漏洞风险    5/25  绿色
  权限风险    25/50 橙色
  总分        50/100 橙色

警告: 检测到网络调用
建议: 安装前先审查代码
```

---

## 配置

### 缓存目录
默认: `~/.cache/skill-safety/`

### 缓存 TTL
- Advisory 数据: 24 小时
- Skill 依赖: 6 小时

### GitHub API
- 无需认证（60 次/小时）
- 有认证: 5000 次/小时

---

## 集成

### 与 ClawdHub 集成

```bash
# 安装时自动验证
npx skills add <owner/repo> --verify
```

### 在你自己的流水线中

```python
from analyzer import analyze_skill

result = analyze_skill('/path/to/skill')
print(result['scores'])
# {'network': 0, 'vuln': 0, 'permission': 25, 'total': 25}
```

---

## 最佳实践

1. 始终验证 - 永远不要安装未经审查的 Skill
2. 阅读代码 - 自动检查不够，需要人工审查
3. 最小权限 - 只授予必要的权限
4. 隔离运行 - 在容器中运行高风险 Skill
5. 监控日志 - 记录所有 Skill 活动

---

## 常见问题

### "No module named 'requests'"
```bash
pip install requests
```

### "SSL certificate verify failed"
默认使用 SSL 验证。如果遇到问题，可以禁用 SSL 验证（生产环境不推荐）。

### "API rate limit exceeded"
等待一小时或使用 GitHub Token 提高速率限制。

---

## 相关链接

- ClawdHub (https://clawhub.com) - Skill 市场
- GitHub Advisory API (https://api.github.com/advisories)
- OpenClaw 文档 (https://docs.openclaw.ai)

---

## License

MIT License - see LICENSE file for details.


path: analyzer.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Skill Safety Verifier - Full Analyzer
自动分析 Skill 的安全性：Socket 扫描、GitHub Advisory API、代码模式扫描
"""
import sys
import os
import json
import re
import subprocess
import time
import hashlib
from pathlib import Path
from typing import Dict, List, Tuple, Optional
from urllib.request import urlopen, Request
from urllib.error import URLError
import ssl

# Enable UTF-8
if sys.platform == 'win32':
    import io
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# Risk scoring constants
NETWORK_SCORES = {0: 0, 1: 10, 2: 10, 3: 25, 5: 50}
VULN_SCORES = {'CRITICAL': 25, 'HIGH': 15, 'MEDIUM': 10, 'LOW': 5}
PERM_SCORES = {'read': 0, 'network': 10, 'write': 15, 'exec': 25, 'full': 50}

# Dangerous patterns
DANGEROUS_PATTERNS = {
    'exec': [
        r'os\.system\(',
        r'subprocess\.(call|run|Popen)\(',
        r'exec\(',
        r'eval\(',
        r'__import__\([\'"]os[\'"]\)',
        r'shell=True',
    ],
    'network': [
        r'requests\.(get|post)\(',
        r'urllib\.request',
        r'http\.client',
        r'fetch\(',
        r'axios\.',
        r'http://',
        r'https://',
    ],
    'data': [
        r'os\.environ',
        r'process\.env',
        r'os\.getenv\(',
        r'os\.putenv\(',
    ],
}


class CacheManager:
    """本地缓存管理"""
    
    def __init__(self, cache_dir: str = None):
        if cache_dir is None:
            home = os.path.expanduser('~')
            cache_dir = os.path.join(home, '.cache', 'skill-safety')
        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.advisory_cache = self.cache_dir / 'advisory.json'
    
    def get_advisories(self, max_age_hours: int = 24) -> List[Dict]:
        """获取缓存的 Advisory 数据"""
        if not self.advisory_cache.exists():
            return []
        
        # Check age
        age = time.time() - self.advisory_cache.stat().st_mtime
        if age > max_age_hours * 3600:
            return []
        
        try:
            with open(self.advisory_cache, 'r', encoding='utf-8') as f:
                return json.load(f)
        except:
            return []
    
    def save_advisories(self, advisories: List[Dict]):
        """保存 Advisory 到缓存"""
        try:
            with open(self.advisory_cache, 'w', encoding='utf-8') as f:
                json.dump(advisories, f, ensure_ascii=False, indent=2)
        except:
            pass


class GitHubAdvisor:
    """GitHub Advisory API 客户端"""
    
    def __init__(self, cache_manager: CacheManager):
        self.cache = cache_manager
        self.api_url = 'https://api.github.com/advisories'
        self.advisories = None
    
    def fetch_advisories(self, timeout: int = 5) -> List[Dict]:
        """获取最新 Advisory（带缓存）"""
        # Try cache first
        cached = self.cache.get_advisories(max_age_hours=24)
        if cached:
            self.advisories = cached
            return cached
        
        # Fetch from API
        try:
            ctx = ssl.create_default_context()
            ctx.check_hostname = False
            ctx.verify_mode = ssl.CERT_NONE
            
            req = Request(self.api_url + '?type=reviewed&per_page=100')
            req.add_header('User-Agent', 'SkillSafetyVerifier/1.0')
            
            with urlopen(req, timeout=timeout, context=ctx) as response:
                data = json.loads(response.read().decode('utf-8'))
            
            # Save to cache
            self.advisories = data
            self.cache.save_advisories(data)
            return data
            
        except (URLError, Exception) as e:
            print(f"Warning: Failed to fetch advisories: {e}", file=sys.stderr)
            return []
    
    def check_package(self, package_name: str) -> List[Dict]:
        """检查特定包的漏洞"""
        if self.advisories is None:
            self.fetch_advisories()
        
        if not self.advisories:
            return []
        
        matches = []
        for adv in self.advisories:
            # Check package names
            vulnerable_packages = adv.get('vulnerabilities', [])
            for vuln in vulnerable_packages:
                if package_name.lower() == vuln.get('package_name', '').lower():
                    matches.append({
                        'cve': adv.get('ghsa_id', 'N/A'),
                        'severity': adv.get('severity', 'UNKNOWN'),
                        'summary': adv.get('summary', '')[:100],
                    })
        
        return matches


class SkillAnalyzer:
    """Skill 安全分析器"""
    
    def __init__(self, skill_path: str):
        self.skill_path = Path(skill_path)
        self.advisor = GitHubAdvisor(CacheManager())
        self.findings = {
            'network_calls': [],
            'vulnerabilities': [],
            'dangerous_code': [],
            'permissions': [],
        }
    
    def analyze(self) -> Dict:
        """执行完整分析"""
        # Step 1: Scan for dangerous code patterns
        self._scan_code_patterns()
        
        # Step 2: Check dependencies for vulnerabilities
        self._check_dependencies()
        
        # Step 3: Analyze permissions
        self._analyze_permissions()
        
        # Step 4: Calculate scores
        scores = self._calculate_scores()
        
        return {
            'skill_path': str(self.skill_path),
            'findings': self.findings,
            'scores': scores,
        }
    
    def _scan_code_patterns(self):
        """扫描危险代码模式"""
        patterns_found = {'exec': [], 'network': [], 'data': []}
        
        # Scan all code files
        for ext in ['.js', '.ts', '.py', '.sh', '.rb', '.go']:
            for file_path in self.skill_path.rglob(f'*{ext}'):
                if 'node_modules' in str(file_path):
                    continue
                
                try:
                    content = file_path.read_text(encoding='utf-8', errors='ignore')
                    
                    for category, patterns in DANGEROUS_PATTERNS.items():
                        for pattern in patterns:
                            if re.search(pattern, content, re.IGNORECASE):
                                patterns_found[category].append({
                                    'file': str(file_path.relative_to(self.skill_path)),
                                    'pattern': pattern,
                                })
                except:
                    pass
        
        self.findings['dangerous_code'] = patterns_found
    
    def _check_dependencies(self):
        """检查依赖漏洞"""
        vulnerabilities = []
        
        # Check package.json
        pkg_json = self.skill_path / 'package.json'
        if pkg_json.exists():
            try:
                with open(pkg_json, 'r', encoding='utf-8') as f:
                    pkg = json.load(f)
                
                deps = {}
                deps.update(pkg.get('dependencies', {}))
                deps.update(pkg.get('devDependencies', {}))
                
                for dep_name in deps.keys():
                    vulns = self.advisor.check_package(dep_name)
                    vulnerabilities.extend(vulns)
            except:
                pass
        
        self.findings['vulnerabilities'] = vulnerabilities
    
    def _analyze_permissions(self):
        """分析权限需求"""
        permissions = []
        
        # Check plugin.json or SKILL.md for allowed tools
        for config_file in ['plugin.json', 'SKILL.md']:
            config_path = self.skill_path / config_file
            if config_path.exists():
                try:
                    content = config_path.read_text(encoding='utf-8', errors='ignore')
                    
                    # Look for allowed tools / permissions
                    tool_matches = re.findall(r'allowed-tools:?\s*\[(.*?)\]', content, re.DOTALL)
                    for match in tool_matches:
                        tools = [t.strip().strip('"\'-') for t in match.split(',')]
                        permissions.extend(tools)
                    
                    # Check for exec/write permissions
                    if 'exec' in content.lower() or 'subprocess' in content.lower():
                        permissions.append('command_execution')
                    if 'write' in content.lower() or 'file' in content.lower():
                        permissions.append('file_write')
                    if 'http' in content.lower() or 'request' in content.lower():
                        permissions.append('network')
                        
                except:
                    pass
        
        self.findings['permissions'] = list(set(permissions))
    
    def _calculate_scores(self) -> Dict:
        """计算风险评分"""
        # Network score
        network_findings = len(self.findings['dangerous_code'].get('network', []))
        network_score = min(network_findings * 10, 50)
        
        # Vulnerability score
        vuln_score = 0
        for vuln in self.findings['vulnerabilities']:
            severity = vuln.get('severity', '').upper()
            vuln_score += VULN_SCORES.get(severity, 0)
        vuln_score = min(vuln_score, 25)
        
        # Permission score
        perms = self.findings['permissions']
        perm_score = 0
        if 'command_execution' in perms or 'exec' in perms:
            perm_score = 25
        elif 'file_write' in perms or 'write' in perms:
            perm_score = 15
        elif 'network' in perms:
            perm_score = 10
        
        return {
            'network': network_score,
            'vuln': vuln_score,
            'permission': perm_score,
            'total': network_score + vuln_score + perm_score,
        }


def analyze_skill(skill_path: str) -> Dict:
    """分析单个 Skill"""
    analyzer = SkillAnalyzer(skill_path)
    return analyzer.analyze()


def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Skill Safety Analyzer')
    parser.add_argument('skill_path', help='Path to skill directory')
    parser.add_argument('--json', action='store_true', help='Output JSON')
    
    args = parser.parse_args()
    
    result = analyze_skill(args.skill_path)
    
    if args.json:
        print(json.dumps(result, ensure_ascii=False, indent=2))
    else:
        scores = result['scores']
        findings = result['findings']
        
        # Build warnings
        warnings = []
        
        # Network warnings
        for item in findings['dangerous_code'].get('network', [])[:2]:
            warnings.append(f"Network call in {item['file']}")
        
        # Vuln warnings
        for vuln in findings['vulnerabilities'][:2]:
            warnings.append(f"{vuln['severity']}: {vuln.get('cve', 'N/A')}")
        
        # Permission warnings
        for perm in findings['permissions'][:3]:
            warnings.append(f"Permission: {perm}")
        
        # Run renderer
        import risk_radar
        print(risk_radar.render_risk_radar(
            network=scores['network'],
            vuln=scores['vuln'],
            permission=scores['permission'],
            skill_name=Path(args.skill_path).name,
            warnings=warnings
        ))


if __name__ == '__main__':
    main()


path: requirements.txt
content: # Requirements
requests>=2.28.0


path: risk_radar.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Skill Safety Verifier - Risk Radar Renderer
Renders risk assessment results with progress bars.
"""
import sys
import io

import json
from typing import Dict, List, Tuple


def render_bar(score: int, max_score: int, width: int = 10) -> str:
    """Render a progress bar."""
    percent = min(score / max_score, 1.0) if max_score > 0 else 0
    filled = int(percent * width)
    return '█' * filled + '░' * (width - filled)


def get_risk_emoji(score: int, max_score: int) -> str:
    """Get emoji based on risk level."""
    percent = score / max_score if max_score > 0 else 0
    if percent < 0.2:
        return '🟢'
    elif percent < 0.5:
        return '🟡'
    elif percent < 0.8:
        return '🟠'
    return '🔴'


def get_risk_level(score: int) -> str:
    """Get risk level text."""
    if score <= 10:
        return '🟢 SAFE'
    elif score <= 30:
        return '🟡 LOW'
    elif score <= 60:
        return '🟠 MEDIUM'
    return '🔴 HIGH'


def render_risk_radar(
    network: int,
    vuln: int,
    permission: int,
    skill_name: str = "skill",
    warnings: List[str] = None
) -> str:
    """Render complete Risk Radar."""
    
    total = network + vuln + permission
    
    # Determine emoji based on total
    if total <= 10:
        status_emoji = '✅'
    elif total <= 30:
        status_emoji = '⚠️'
    elif total <= 60:
        status_emoji = '⚠️'
    else:
        status_emoji = '🚫'
    
    lines = [
        f"",
        f"{status_emoji} {skill_name} - Risk Assessment",
        f"",
        f"┌{'─' * 46}┐",
        f"│  📊 Risk Radar{' ' * 28}│",
        f"├{'─' * 46}┤",
        f"│  Network      [{render_bar(network, 50)}] {network:>2}/50  {get_risk_emoji(network, 50):<2}│",
        f"│  Vulnerabil. [{render_bar(vuln, 25)}] {vuln:>2}/25  {get_risk_emoji(vuln, 25):<2}│",
        f"│  Permissions  [{render_bar(permission, 50)}] {permission:>2}/50  {get_risk_emoji(permission, 50):<2}│",
        f"│  {'─' * 40}│",
        f"│  TOTAL        [{render_bar(total, 100)}] {total:>3}/100 {get_risk_emoji(total, 100):<2}│",
        f"├{'─' * 46}┤",
    ]
    
    # Add risk level
    lines.append(f"│  {get_risk_level(total):^42}│")
    
    # Add warnings if any
    if warnings:
        lines.append(f"├{'─' * 46}┤")
        lines.append(f"│  ⚠️ Warnings:{' ' * 32}│")
        for warning in warnings[:3]:  # Max 3 warnings
            lines.append(f"│    • {warning:<38}│")
    
    lines.append(f"└{'─' * 46}┘")
    
    return '\n'.join(lines)


def render_compact(
    network: int,
    vuln: int,
    permission: int,
    skill_name: str = "skill"
) -> str:
    """Render compact version for quick output."""
    total = network + vuln + permission
    level = get_risk_level(total)
    
    return f"""
{skill_name} - {level}
  Network: {network}/50 {get_risk_emoji(network, 50)}
  Vulnerabil.: {vuln}/25 {get_risk_emoji(vuln, 25)}
  Permissions: {permission}/50 {get_risk_emoji(permission, 50)}
  TOTAL: {total}/100 {get_risk_emoji(total, 100)}
"""


def parse_args():
    """Parse command line arguments."""
    import argparse
    
    parser = argparse.ArgumentParser(
        description='Skill Safety Verifier - Risk Radar Renderer'
    )
    parser.add_argument('--network', '-n', type=int, default=0,
                        help='Network risk score (0-50)')
    parser.add_argument('--vuln', '-v', type=int, default=0,
                        help='Vulnerability score (0-25)')
    parser.add_argument('--perm', '-p', type=int, default=0,
                        help='Permission risk score (0-50)')
    parser.add_argument('--name', type=str, default='skill',
                        help='Skill name')
    parser.add_argument('--warnings', nargs='*', default=[],
                        help='Warning messages')
    parser.add_argument('--json', action='store_true',
                        help='Input from JSON')
    parser.add_argument('--compact', action='store_true',
                        help='Use compact output')
    
    return parser.parse_args()


def main():
    args = parse_args()
    
    # If JSON mode, read from stdin
    if args.json:
        try:
            data = json.loads(sys.stdin.read())
            network = data.get('network', 0)
            vuln = data.get('vuln', 0)
            permission = data.get('permission', 0)
            name = data.get('name', 'skill')
            warnings = data.get('warnings', [])
        except json.JSONDecodeError:
            print("Error: Invalid JSON input", file=sys.stderr)
            sys.exit(1)
    else:
        network = args.network
        vuln = args.vuln
        permission = args.perm
        name = args.name
        warnings = args.warnings
    
    # Render output
    if args.compact:
        print(render_compact(network, vuln, permission, name))
    else:
        print(render_risk_radar(network, vuln, permission, name, warnings))


if __name__ == '__main__':
    main()


path: setup.py
content: from setuptools import setup, find_packages

setup(
    name='skill-safety-verifier',
    version='1.0.0',
    description='Security-first skill vetting for AI agents',
    author='ttttstc',
    author_email='[REDACTED_SECRET]',
    url='https://github.com/ttttstc/skill-safety-verifier',
    packages=find_packages(),
    install_requires=[
        'requests>=2.28.0',
    ],
    entry_points={
        'console_scripts': [
            'skill-safety-check=analyzer:main',
        ],
    },
    python_requires='>=3.8',
    classifiers=[
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
        'Programming Language :: Python :: 3.12',
    ],
)


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