Skill: torchesfrms/skill-sec-scan (version 4.4.1)

# 🔒 Skill Security Scanner **企业级 Skill 安全检测工具** 自动检测 ClawHub / GitHub / 本地 Skill 的安全风险，支持 JavaScript、TypeScript、Python、Shell 等多种文件类型，基于威胁情报驱动的静态分析引擎 + LLM 语义分析双层检测。 --- ## ⚡ 交互流程（必须遵循） ``` 用户提出检测请求 ↓ Agent 运行 scan.sh 执行静态扫描 ↓ Agent 输出完整安全报告（Markdown 格式） ↓ Agent 询问用户是否需要语义分析 ↓ 用户确认后 ↓ Agent 生成 LLM 语义分析提示（从脚本输出中提取） ↓ 用户将提示复制给 Agent ↓ Agent 执行 LLM 语义分析 ↓ Agent 输出最终结论 ``` ### 重要原则 - **必须先输出完整静态报告，再询问用户** - **必须由用户确认后才生成 LLM 语义分析提示** - **禁止替用户做决定或跳过询问步骤** - **完整报告包含：下载信息 → 安全评分 → 风险详情 → 每条风险解释** --- ## 🎯 核心能力 | 能力 | 说明 | |------|------| | **多文件检测** | 支持 `.js`、`.ts`、`.py`、`.sh` 四种文件类型 | | **威胁分类** | 4 大威胁类别：数据外泄、注入攻击、代码混淆、木马后门 | | **检测规则** | 57 条精细化检测规则，覆盖 60+ 危险操作特征 | | **智能评分** | 100 分制安全评分，量化风险等级 | | **LLM 语义分析** | 静态扫描定位疑似问题，用户确认后生成提示，Agent 执行深度分析 | | **白名单机制** | 支持用户自定义信任的 Skill | | **双层检测** | 静态规则精准定位 → LLM 语义判断真伪 | --- ## 🛡️ 威胁分类体系 | 分类 | 代码 | 风险类型 | |------|------|----------| | **数据外泄 (EXFIL)** | EXFIL | 凭证访问、私钥泄露、网络后门、HTTP 服务 | | **注入攻击 (INJECTION)** | INJECTION | 命令执行、npx/远程包执行、动态代码执行、提示注入、越狱攻击 | | **代码混淆 (OBFUSCATION)** | OBFUSCATION | Base64 编码、环境触发、隐藏执行、持久化进程 | | **木马后门 (TROJAN)** | TROJAN | 文件删除、凭证读取、进程终止、postinstall 攻击 | --- ## 📊 检测规则清单 | 威胁类型 | 规则数 | 代表性规则 | |----------|--------|------------| | INJECTION | 19 | cmd-execution、npx-exec、dynamic-exec、prompt-injection、jailbreak、backtick-injection、system-probing、crypto-operation | | EXFIL | 18 | credential-access、vault-access、private-key、network-backdoor、database-access、config-write、wallet-access、port-listening、proxy-tor、git-operation、file-download | | TROJAN | 14 | destructive-delete、process-kill、shell-injection、persistent-timer、archive-exploit、arbitrary-write、deserialize | | OBFUSCATION | 6 | base64、env-trigger、hidden-exec、persistent-shell | | **合计** | **57** | - | --- ## ⚡ 快速开始 ### 扫描 ClawHub / GitHub Skill ```bash ./scripts/scan.sh https://clawhub.ai/owner/skill-name ./scripts/scan.sh https://github.com/user/repo ``` ### 扫描本地 Skill ```bash ./scripts/scan.sh /path/to/skill ./scripts/scan.sh ~/.openclaw/workspace/skills/crypto-wallet ``` ### JSON 格式输出 ```bash ./scripts/scan.sh --format json https://clawhub.ai/owner/skill-name ``` ### 批量扫描 ```bash ./scripts/scan-all.sh ``` --- ## 📊 评分标准 | 评分 | 等级 | 建议 | |------|------|------| | 80-100 | ✅ 安全 | 可正常使用 | | 30-79 | ⚠️ 可疑 | 建议审查后使用 | | 0-29 | 🚫 危险 | 不推荐安装使用 | --- ## 🤖 双层检测机制 ### 第一层：静态扫描 - **作用**：精准定位疑似风险点（规则匹配） - **局限**：误报率高（工具函数、检测库本身会被标记） - **输出**：完整安全报告 + 风险详情表格 ### 第二层：LLM 语义分析 - **触发条件**：用户确认后才生成提示 - **作用**：理解代码真实意图，区分恶意行为与正常工具功能 - **输入**：LLM 语义分析提示（包含：风险文件代码 + 上下文） ### 为什么需要双层？ 静态扫描适合找"有没有危险操作"，但判断不了"这个危险操作是不是工具本身的功能"。例如： - `credential-access` — 安全工具检测 credential 是正常功能，不是泄露 - `backtick-injection` — 可能是 Markdown 转义，不是命令注入 - `npx-exec` — 可能是构建/安装脚本，不是运行恶意包 --- ## 📋 Agent 操作规范 ### 正确流程（必须遵循） ``` 1. 用户："检测 [skill] 的安全性" 2. Agent 执行： bash scripts/scan.sh https://clawhub.ai/owner/skill-name 3. Agent 输出完整报告（包含所有章节） 4. Agent 询问： "发现 {N} 个风险，安全评分 {score}/100。 是否需要我进行 LLM 语义分析来判断这些是真实威胁还是误报？" 5. 用户确认后，Agent 从脚本输出中提取 LLM 语义分析提示， 执行语义分析并输出结论 6. 用户选择不分析时，Agent 仅输出报告，不追问 ``` ### 禁止行为 - ❌ 不输出报告就跳过询问直接生成语义分析 - ❌ 替用户决定要不要分析 - ❌ 自行调用 API 或外部工具 - ❌ 修改或删除报告内容 - ❌ 只输出摘要而非完整报告 --- ## 报告输出格式 ### 标准模板（完整章节） ``` 🔒 **Skill 安全检测报告** ════════════════════════════════════════════════════════════════════ **Skill 名称:** <skill-name> **扫描文件:** <N> 个 **检测问题:** <N> 个 📊 安全评分：✅/⚠️/🚫 <score>/100 — <status> 🔍 **检测清单：** ✅ 全部威胁分类已检测 | 文件类型 | 状态 | |----------|------| | JavaScript (.js) | ✅/➖ 无 | | TypeScript (.ts) | ✅/➖ 无 | | Python (.py) | ✅/➖ 无 | | Shell (.sh) | ✅/➖ 无 | **🛡️ 威胁检测规则（共 57 项）：** - **EXFIL (18):** credential-access, vault-access... - **INJECTION (19):** remote-script, cmd-execution... - **TROJAN (14):** destructive-delete, process-kill... - **OBFUSCATION (6):** base64, env-trigger... 📊 **检测结果：** 发现 <N> 个风险 - 🔴 critical: <N> 个 - 🟠 high: <N> 个 - 🟡 medium: <N> 个 --- **⚠️ 风险详情：** | # | 分类 | 风险 | 等级 | 文件 | |:---:|--------|------------------|-----------|------------| | 1 | <分类> | <风险名> | <等级> | <文件> | --- **每条风险解释：** - <风险名> — <解释> --- ✅/⚠️/🚫 **结论：<结论>** --- ⚠️ 发现 {N} 个风险，安全评分 {score}/100。 是否需要我进行 LLM 语义分析来判断这些是真实威胁还是误报？ ``` --- ## LLM 语义分析提示格式 当用户确认后，从脚本输出中提取以下内容生成提示： ``` # Skill 安全语义分析请求 ## 任务 你是一位专业的代码安全审计员。请从安全产品角度分析以下 Skill，判断它是否可以安全安装使用。 ## 安全评估六维框架 | 维度 | 问题 | 权重 | |------|------|------| | **1. 数据透明** | 它访问/发送了什么数据？去了哪里？是否告知用户？ | 高 | | **2. 权限边界** | 申请的权限（网络/文件/进程/环境变量）是否超出声明功能？ | 高 | | **3. 行为透明** | 有没有未声明的偷偷执行行为？ | 极高 | | **4. 供应链风险** | 执行外部包（npx/pip/git）来源可信吗？版本固定吗？ | 高 | | **5. 用户控制** | 用户能随时停止/禁用吗？敏感操作有确认吗？ | 中 | | **6. 成本告知** | 有没有隐性成本（Gas费/算力消耗/付费API）？ | 中 | ## 额外判断 如存在以下任一情况，直接标记为 failed： 1. **数据外泄**：向非功能必需的第三方发送用户数据 2. **偷偷执行**：在用户不知情时执行额外行为 3. **权限超出**：申请的权限超出 Skill 声明的功能范围 4. **供应链攻击**：执行来源不明的外部包 5. **用户无控制**：无法停止、或停止后自动重启 6. **成本不透明**：产生用户无法预估的成本 ## 分析要求 请按以下格式输出： ### 1. 六维评估 - **[维度名]**：[通过/关注/警告] — [简要说明] ### 2. 总体评估 - 语义检测结果：✅ passed / ⚠️ failed / 🚫 failed - 理由：综合六维判断，给出整体结论 ### 3. 逐文件分析 对每个有风险的文件，以表格形式输出： | 风险 | 判断 | 说明 | 越界行为 | |------|------|------|---------| | [风险名] | ✅误报/⚠️低风险/🚨真实威胁 | [详细解释] | ⚠️ 维度X+Y / 无明显越界行为 | ### 4. 最终建议 - 是否可以安装使用？ - 需要注意什么？ ## 静态扫描报告 [从脚本输出中提取风险表格 + 相关文件代码] ``` ## 触发方式 当用户提出以下问题时触发检测流程： | 类型 | 触发短语 | |------|----------| | **扫描检测** | "扫描 [链接/名称] 的安全性" | | | "检测一下 [skill名称]" | | | "帮我扫描一下这个 skill" | | **风险询问** | "[skill] 有风险吗" | | | "这个 skill 安全吗" | | | "检查 [链接] 是否有安全问题" | | **安全评估** | "评估一下 [名称] 的安全性" | | | "[skill] 会不会有恶意代码" | --- ## JSON 输出格式 ```bash ./scripts/scan.sh --format json <target> ``` ```json { "skill": "example-skill", "score": 55, "files": 3, "issues": [ {"category":"INJECTION","rule":"backtick-injection","level":"critical","file":"handler.js","snippet":"backtick"} ] } ``` --- ## 白名单管理 ```bash # 添加到白名单 ./scripts/scan.sh --whitelist-add skill-name # 从白名单移除 ./scripts/scan.sh --whitelist-remove skill-name # 查看白名单 ./scripts/scan.sh --whitelist-list ``` --- ## 文件结构 ``` skill-security-scanner/ ├── SKILL.md # 本文档 ├── scripts/ │ ├── scan.sh # 核心扫描脚本 │ └── scan-all.sh # 批量扫描脚本 ├── references/ │ ├── rules.md # 详细检测规则 │ └── dangerous-commands.md # 危险命令列表 └── whitelist.txt # 白名单文件（自动创建） ``` --- ## 版本历史 | 版本 | 日期 | 更新内容 | |------|------|----------| | v1.0 | - | 基础命令注入检测 | | v2.0 | - | 混淆检测、间接执行、依赖审计 | | v3.0 | 2026-03-18 | 整合 ClawHub jax 规则（AI投毒、木马检测） | | v4.0 | 2026-03-18 | 威胁分类、完整报告规范、JSONL 输出 | | v4.1 | 2026-03-21 | 新增 LLM 语义分析提示生成（解决静态扫描误报） | | v4.3 | 2026-03-21 | 重构 LLM 语义分析 prompt：引入安全评估六维框架 + 六维额外判断标准（数据透明/权限边界/行为透明/供应链风险/用户控制/成本告知）；修复文件内容重复输出 bug |

path: SKILL_EN.md
content: # 🔒 Skill Security Scanner

**Enterprise-Grade Skill Security Scanner**

Automatically detect security risks in ClawHub / GitHub / local Skills. Supports JavaScript, TypeScript, Python, Shell files with threat intelligence-driven static analysis.

---

## 🎯 Core Features

| Feature | Description |
|---------|-------------|
| **Multi-File Detection** | Supports `.js`, `.ts`, `.py`, `.sh` |
| **Threat Classification** | 4 Major Categories: EXFIL, INJECTION, OBFUSCATION, TROJAN |
| **Detection Rules** | 57 refined detection rules covering 60+ dangerous patterns |
| **Smart Scoring** | 100-point scoring system for quantified risk levels |
| **Whitelist** | User-defined trusted Skills |

---

## 🛡️ Threat Classification

| Category | Code | Risks |
|----------|------|-------|
| **Data Exfiltration** | EXFIL | credential-access, vault-access, private-key, api-key, network-backdoor, http-server, network-request, http-client, database-access, config-write, wallet-access, port-listening, proxy-tor, git-operation, file-download |
| **Injection Attack** | INJECTION | remote-script, cmd-execution, npx-exec, dynamic-exec, prompt-injection, jailbreak, dynamic-import, backtick-injection, system-probing, shell-injection, crypto-operation |
| **Trojan/Backdoor** | TROJAN | destructive-delete, credential-read, process-kill, persistent-timer, postinstall-risk, archive-exploit, arbitrary-write, deserialize |
| **Code Obfuscation** | OBFUSCATION | base64, env-trigger, hidden-exec, persistent-shell, obfuscated-package |

---

## 📊 Detection Rules (57 Total)

| Category | Rules | Sample Rules |
|----------|-------|--------------|
| INJECTION | 19 | cmd-execution, npx-exec, dynamic-exec, prompt-injection, jailbreak |
| EXFIL | 18 | credential-access, vault-access, private-key, network-backdoor |
| TROJAN | 14 | destructive-delete, process-kill, shell-injection, persistent-timer |
| OBFUSCATION | 6 | base64, env-trigger, hidden-exec, persistent-shell |

---

## ⚡ Quick Start

### Single Scan

```bash
./scripts/scan.sh https://clawhub.ai/owner/skill-name
./scripts/scan.sh ~/.openclaw/workspace/skills/skill-name
```

### Batch Scan

```bash
./scripts/scan-all.sh
```

---

## Trigger Methods

### Scan Detection

| Trigger | Description |
|---------|-------------|
| `scan [URL/name]` | Scan a skill for security |
| `check security of [skill]` | Check if skill is safe |
| `detect [skill]` | Detect security issues |

### Risk Inquiry

| Trigger | Description |
|---------|-------------|
| `is [skill] risky?` | Ask about risk level |
| `is [skill] safe?` | Ask if skill is safe |
| `does [skill] have issues?` | Ask about problems |

---

## Report Output Format

### Standard Template

```
🔒 **Skill Security Report**
════════════════════════════════════════════════════════════════════

**Skill Name:** <name>
**Scanned Files:** <N>
**Issues Found:** <N>

📊 Score: ✅/⚠️/🚫 <score>/100 — <status>

🔍 **Checklist:** All threat categories scanned

| File Type | Status |
|-----------|--------|
| JavaScript (.js) | ✅/➖ None |
| TypeScript (.ts) | ✅/➖ None |
| Python (.py) | ✅/➖ None |
| Shell (.sh) | ✅/➖ None |

**🛡️ Detection Rules (57 total):**
- **EXFIL (18):** credential-access, vault-access, private-key...
- **INJECTION (19):** remote-script, cmd-execution...
- **TROJAN (14):** destructive-delete, process-kill...
- **OBFUSCATION (6):** base64, env-trigger...

📊 **Results:** Found <N> issues (if any)
- 🔴 critical: <N>
- 🟠 high: <N>
- 🟡 medium: <N>

---

**⚠️ Issue Details:** (if any)

| # | Category | Rule | Level | File |
|:---:|----------|------|-------|------|
| 1 | <Cat> | <rule> | <level> | <file> |

---

**Explanation:**
- <rule> — <explanation>

---

✅/⚠️/🚫 **Conclusion:** <verdict>

════════════════════════════════════════════════════════════════════
```

### Scoring

| Score | Status | Conclusion |
|-------|--------|------------|
| 80-100 | ✅ Safe | Recommended |
| 30-79 | ⚠️ Suspicious | Use with caution |
| 0-29 | 🚫 Dangerous | Not recommended |

---

## 🤖 Agent Behavior Guide

After batch scan, Agent should prompt user for individual scans:

```
✅ Passed: 14
🚫 Attention needed: 5

⚠️ Risky skills:
• crypto-wallet (score: 0)
• solana-wallet (score: 0)
• litcoin (score: 30)

💡 Run individual scan for full report:
   "scan crypto-wallet"
   "check litcoin"
```

---

## Supported File Types

| Type | Extension | Status |
|------|-----------|--------|
| JavaScript | `.js` | ✅ |
| TypeScript | `.ts` | ✅ |
| Python | `.py` | ✅ |
| Shell | `.sh` | ✅ |

---

## Version History

### v1.0.0 (2026-03-18)
- Initial release
- 57 detection rules
- 4 threat categories
- Markdown report format
- Whitelist support
- Batch scanning


path: index.js
content: #!/usr/bin/env node

const { execSync } = require('child_process');
const path = require('path');

const args = process.argv.slice(2).join(' ');
const scriptDir = path.dirname(__filename);
const result = execSync(`"${scriptDir}/scripts/scan.sh" ${args}`, {
    encoding: 'utf8',
    stdio: 'inherit'
});

console.log(result);


path: node/index.js
content: #!/usr/bin/env node

/**
 * Skill Security Scanner CLI
 * 用法: node index.js [skills目录]
 */

const { SecurityDetector } = require('./scanner.js');
const path = require('path');

const args = process.argv.slice(2);
const skillsDir = args[0] || path.join(process.env.HOME, '.openclaw/workspace/skills');

async function main() {
    console.log('\n🔒 Skill Security Scanner v3.0 (Node.js)\n');
    
    const detector = new SecurityDetector();
    const results = await detector.scanAll(skillsDir);
    detector.generateReport(results);
}

main().catch(console.error);


path: node/scanner.js
content: /**
 * Skill Security Scanner - Node.js 版本
 * 扫描所有已安装的 skill
 */

const fs = require('fs').promises;
const path = require('path');

// 检测器（从 bash 规则移植）
class SecurityDetector {
    constructor() {
        this.rules = {
            // 命令注入
            cmdInjection: [
                { pattern: /npx|npm\s+exec|yarn\s+exec|pnpm\s+exec/, level: 'critical', score: 30, desc: '命令注入' },
                { pattern: /curl.*\|.*bash|wget.*\|.*bash/, level: 'critical', score: 30, desc: '远程脚本' },
                { pattern: /`.*\$.*`|\$\(.*\)/, level: 'high', score: 15, desc: '命令替换' }
            ],
            // 凭证访问
            credentials: [
                { pattern: /credentials\/|vault\/(?!crypto\/)/, level: 'critical', score: 25, desc: '凭证访问' },
                { pattern: /0x[a-f0-9]{64}/, level: 'critical', score: 30, desc: '私钥' },
                { pattern: /eth\.wallet|web3\.ethers/, level: 'high', score: 20, desc: '钱包' },
                { pattern: /process\.env\.(AWS_|AZURE_|STRIPE_|GOOGLE_)/, level: 'critical', score: 30, desc: 'API密钥' }
            ],
            // 木马/后门
            trojan: [
                { pattern: /net\.createServer|net\.connect|net\.Socket/, level: 'critical', score: 25, desc: '网络后门' },
                { pattern: /http\.createServer|http\.request/, level: 'high', score: 20, desc: 'HTTP服务器' },
                { pattern: /ws\.Server|socket\.io|WebSocketServer/, level: 'high', score: 20, desc: 'WebSocket' },
                { pattern: /fs\.writeFileSync.*process\.env|fs\.writeFile.*password/, level: 'critical', score: 25, desc: '敏感文件写入' },
                { pattern: /fs\.readFile.*\.ssh|fs\.readFile.*credential/, level: 'critical', score: 25, desc: '凭证读取' },
                { pattern: /child_process\.spawn.*sh.*-c|curl.*http/, level: 'critical', score: 25, desc: '远程命令' },
                { pattern: /setInterval.*1000.*60.*24/, level: 'medium', score: 15, desc: '长时间定时' }
            ],
            // AI投毒
            promptPoison: [
                { pattern: /ignore\s+(all\s+)?(previous|prior|above)\s+(instructions?)/, level: 'high', score: 20, desc: '提示注入' },
                { pattern: /disregard\s+(all\s+)?(your\s+)?(instructions?)/, level: 'high', score: 20, desc: '提示注入' },
                { pattern: /\b(jailbreak|bypass|disable\s+safety)\b/, level: 'critical', score: 25, desc: '越狱攻击' },
                { pattern: /\b(DAN|do\s+anything\s+now)\b/, level: 'critical', score: 25, desc: 'DAN越狱' },
                { pattern: /roleplay\s+(as|with)\s+(evil|malicious)/, level: 'critical', score: 25, desc: '恶意角色' },
                { pattern: /(show|tell|reveal).*(system\s+)?prompt/, level: 'high', score: 20, desc: '提示提取' }
            ],
            // 混淆/间接
            obfuscation: [
                { pattern: /Buffer\.from.*base64|atob\(|btoa\(/, level: 'high', score: 20, desc: 'Base64混淆' },
                { pattern: /\\x[0-9a-f]{2}/, level: 'high', score: 20, desc: 'Hex编码' },
                { pattern: /new\s+Function\(|Function\(|eval\(/, level: 'critical', score: 25, desc: '间接执行' },
                { pattern: /if.*NODE_ENV.*production/, level: 'high', score: 20, desc: '生产触发' }
            ]
        };
        
        // 官方技能白名单
        this.officialSkills = [
            'blogwatcher', 'skill-creator', 'github', 'weather', 
            'video-frames', 'openai-whisper', 'healthcheck'
        ];
    }
    
    // 扫描文件
    async scanFile(filePath) {
        const issues = [];
        try {
            const content = await fs.readFile(filePath, 'utf8');
            
            for (const [category, rules] of Object.entries(this.rules)) {
                for (const rule of rules) {
                    if (rule.pattern.test(content)) {
                        issues.push({
                            category,
                            ...rule,
                            file: path.basename(filePath)
                        });
                    }
                }
            }
        } catch (e) {
            // 忽略读取错误
        }
        return issues;
    }
    
    // 扫描 skill 目录
    async scanSkill(skillPath) {
        const skillName = path.basename(skillPath);
        const allIssues = [];
        let filesScanned = 0;
        
        // 递归扫描所有 JS 文件
        const scanDir = async (dir) => {
            try {
                const entries = await fs.readdir(dir, { withFileTypes: true });
                for (const entry of entries) {
                    const fullPath = path.join(dir, entry.name);
                    if (entry.isDirectory() && !entry.name.startsWith('.')) {
                        await scanDir(fullPath);
                    } else if (entry.name.endsWith('.js')) {
                        filesScanned++;
                        const issues = await this.scanFile(fullPath);
                        allIssues.push(...issues);
                    }
                }
            } catch (e) {}
        };
        
        await scanDir(skillPath);
        
        // 计算评分
        let score = 100;
        const criticalCount = allIssues.filter(i => i.level === 'critical').length;
        const highCount = allIssues.filter(i => i.level === 'high').length;
        const mediumCount = allIssues.filter(i => i.level === 'medium').length;
        
        score -= criticalCount * 25;
        score -= highCount * 15;
        score -= mediumCount * 10;
        score = Math.max(0, score);
        
        // 评估可信度
        let trustLevel = 'low';
        if (this.officialSkills.includes(skillName)) {
            trustLevel = 'high';
        } else if (skillPath.includes('.openclaw/workspace/skills')) {
            trustLevel = 'medium';
        }
        
        return {
            skillName,
            skillPath,
            score,
            trustLevel,
            issues: allIssues,
            filesScanned,
            criticalCount,
            highCount,
            mediumCount
        };
    }
    
    // 扫描 skills 目录
    async scanAll(skillsDir) {
        const results = [];
        
        try {
            const entries = await fs.readdir(skillsDir, { withFileTypes: true });
            const skillDirs = entries.filter(e => e.isDirectory() && !e.name.includes('skill-security-scanner'));
            
            console.log(`🔍 扫描目录: ${skillsDir}`);
            console.log(`📦 发现 ${skillDirs.length} 个 skills\n`);
            
            for (const entry of skillDirs) {
                const skillPath = path.join(skillsDir, entry.name);
                const result = await this.scanSkill(skillPath);
                results.push(result);
                
                // 打印进度
                const status = result.score < 70 ? '⚠️' : '✅';
                console.log(`  ${status} ${result.skillName}: ${result.score}/100`);
            }
        } catch (e) {
            console.error('❌ 无法读取目录:', e.message);
        }
        
        return results;
    }
    
    // 生成汇总报告
    generateReport(results) {
        const pass = results.filter(r => r.score >= 70).length;
        const warn = results.filter(r => r.score < 70 && r.score >= 50).length;
        const fail = results.filter(r => r.score < 50).length;
        
        console.log('\n' + '='.repeat(60));
        console.log('                    📊 扫描汇总报告');
        console.log('='.repeat(60));
        console.log(`\n📦 扫描总数: ${results.length}`);
        console.log(`✅ 通过: ${pass}`);
        console.log(`⚠️  建议审查: ${warn}`);
        console.log(`🚫 禁止运行: ${fail}`);
        
        // 高风险列表
        const risky = results.filter(r => r.score < 70).sort((a, b) => a.score - b.score);
        if (risky.length > 0) {
            console.log('\n⚠️  需要关注的 skills:');
            for (const r of risky) {
                const icon = r.score < 50 ? '🚫' : '⚠️';
                console.log(`  ${icon} ${r.skillName}: ${r.score}/100 (${r.issues.length} 个问题)`);
            }
        }
        
        return { pass, warn, fail, results };
    }
}

module.exports = { SecurityDetector };


path: package.json
content: {
  "name": "skill-security-scanner",
  "version": "4.2.0",
  "description": "Enterprise-grade Skill security detection tool with static analysis + LLM semantic analysis",
  "keywords": ["security", "scanner", "skill", "openclaw", "clawhub"],
  "author": "",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/openclaw/skill-security-scanner"
  }
}


path: references/dangerous-commands.md
content: # 危险命令列表

## 🔴 致命风险 (扣 30 分)

| 命令 | 风险描述 | 检测文件 |
|------|----------|----------|
| `rm -rf /` | 根目录删除，毁灭性操作 | .sh, .bash |
| `rm -rf ~` | 用户主目录删除，丢失所有数据 | .sh, .bash |
| `rm -rf $HOME` | 同上，使用环境变量 | .sh, .bash |
| `curl \| bash` | 远程脚本执行，完全不可控 | .sh, .bash |
| `wget -O- \| sh` | 远程脚本执行，完全不可控 | .sh, .bash |
| `curl https://... \| sh` | 同上 | .sh, .bash |
| `wget https://... \| bash` | 同上 | .sh, .bash |
| `passwd` | 修改用户密码 | .sh, .py, .js |

## 🟠 高风险 (扣 20 分)

| 命令/模式 | 风险描述 | 检测文件 |
|-----------|----------|----------|
| `chmod 777` | 权限过于宽松 | .sh, .bash |
| `eval $var` | 动态代码执行 | .sh, .bash |
| `eval "$(...)"` | 同上 | .sh, .bash |
| `elevated: true` | 要求 root 权限 | .yaml, .yml, SKILL.md |
| `sudo` | 提权操作 | .sh, .bash, .py |
| `chown` | 修改文件所有权 | .sh, .bash |
| `chmod +s` | 设置 SUID 位 | .sh, .bash |
| `> /dev/sda` | 直接写入设备 | .sh, .bash |
| `mkfs` | 格式化操作 | .sh, .bash |
| `:(){:|:&};:` | Fork 炸弹 | .sh, .bash |
| `dd if=/dev/zero` | 磁盘写入耗尽 | .sh, .bash |
| JavaScript `eval()` | 动态代码执行 | .js, .mjs |
| JavaScript `exec()` | 子进程执行 | .js, .mjs |
| JavaScript `Function()` | 动态函数创建 | .js, .mjs |
| JavaScript `spawn`/`execSync` | 子进程执行 | .js, .mjs |
| Python `eval()` | 动态代码执行 | .py |
| Python `exec()` | 动态代码执行 | .py |
| Python `subprocess` | 子进程执行 | .py |
| Python `os.system()` | Shell 执行 | .py |

## 🟡 中风险 (扣 10 分)

| 模式 | 风险描述 | 检测文件 |
|------|----------|----------|
| `vault/` | 访问敏感存储 | 所有 |
| `credentials/` | 访问凭据目录 | 所有 |
| `--password` | 命令行传递密码 | .sh, .bash |
| `-p ` | 密码参数 | .sh, .bash |
| API_KEY | 硬编码 API Key | 所有 |
| `wget --post-data` | POST 发送敏感数据 | .sh, .bash |
| `curl -d` | POST 发送敏感数据 | .sh, .bash |
| `curl -X POST` | 同上 | .sh, .bash |
| `.env` | 环境变量文件 | 所有 |
| `~/.ssh/` | SSH 密钥目录 | 所有 |
| `/etc/passwd` | 系统用户文件 | .sh, .bash |
| `$VAR` / `${VAR}` | 环境变量读取 | .sh, .bash |
| `curl` / `wget` | 网络请求 | .sh, .bash |
| `fetch` / `axios` | HTTP 请求 | .js, .mjs |
| `writeFile` / `fs.write` | 文件写入 | .js, .mjs |
| `> file` / `>> file` | Shell 重定向写入 | .sh, .bash |
| `open(..., 'w')` | Python 文件写入 | .py |
| `requests.` / `urllib.` | Python HTTP 请求 | .py |

## 🟢 低风险 (扣 3-5 分)

| 模式 | 风险描述 | 检测文件 |
|------|----------|----------|
| `readFile` | 文件读取 | .js, .mjs |
| `open(..., 'r')` | Python 文件读取 | .py |
| 环境变量引用 (注释除外) | 变量使用 | .sh, .bash |

## 检测优先级

1. 先检测致命风险 - 发现立即终止扫描
2. 再检测高风险 - 累计扣分
3. 最后检测中风险 - 警告但不阻断

## 按语言分类的检测模式

### Shell/Bash (.sh, .bash)

```bash
# 致命
rm\s+-rf\s+[/~$HOME]
curl\s+.*\|\s*(bash|sh)
wget\s+.*\|\s*(bash|sh)
passwd

# 高风险
chmod\s+777
elevated:\s*true
eval\s+
sudo\s+
chown\s+

# 中风险
vault/|credentials/
API_KEY|apikey|password
curl\s+|wget\s+
\$\{?[A-Za-z_]
>.*[/~]|>>.*[/~]
```

### JavaScript (.js, .mjs)

```javascript
// 高风险
eval\s*\(|
exec\s*\(|
Function\s*\(|
spawn\s*\(|
execSync\s*\(|
execFileSync\s*\(

// 中风险
writeFile\s*\(|
writeFileSync\s*\(|
createWriteStream|
\.write\s*\(|
axios\.|
fetch\s*\(
```

### Python (.py)

```python
# 高风险
eval\s*\(|
exec\s*\(|
subprocess\.|
os\.system\s*\(

# 中风险
open\s*\([^)]*,\s*['"]w['"]|
\.write\s*\(|
requests\.|
urllib\.

# 低风险
open\s*\([^)]*,\s*['"]r['"]
```

## 评分算法

```
初始分数: 100

扣分规则:
- 致命风险: -30 分/个
- 高风险:   -20 分/个
- 中风险:   -10 分/个
- 低风险:   -3~5 分/个

加分规则:
- 官方来源 (clawhub): +10 分
- 风险数量 > 5 个: 额外 -5 分
- 风险数量 > 10 个: 额外 -10 分

最终分数范围: 0-100
```

## 正则匹配规则速查

```bash
# 致命
rm\s+-rf\s+[/~$HOME]
curl\s+.*\|\s*(bash|sh)
wget\s+.*\|\s*(bash|sh)
passwd

# 高风险
chmod\s+777
elevated:\s*true
eval\s+|exec\s*\(|Function\s*\(
spawn|execSync|execFileSync
subprocess\.|os\.system\s*\(
sudo\s+|chown\s+

# 中风险
vault/|credentials/
api[_-]?key|password|token|secret
curl|wget|fetch|axios
writeFile|open.*w|>\s*/|>>\s*/
\$\{?[A-Za-z_]
requests\.|urllib\.
```


path: references/rules.md
content: # 检测规则参考文档

## 规则分类

### EXFIL（数据外泄）
检测可能将敏感数据发送到外部的行为。

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| E01 | `credentials/` | critical | 访问凭证目录 |
| E02 | `vault/(?!crypto/)` | critical | 访问 vault（游戏钱包除外） |
| E03 | `0x[a-f0-9]{64}` | critical | 私钥地址 |
| E04 | `process.env.(AWS_|AZURE_|STRIPE_)` | critical | API 密钥 |
| E05 | `net.createServer` | critical | 网络服务器后门 |
| E06 | `http.createServer` | high | HTTP 服务器 |
| E07 | `Buffer.toString.*base64.*http` | high | Base64 外传数据 |
| E08 | `fs.readFile.*\.ssh` | critical | 读取 SSH 密钥 |

### INJECTION（恶意注入）
检测命令注入、代码注入等攻击。

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| I01 | `curl.*\|.*bash` | critical | 远程脚本执行 |
| I02 | `npx\|npm exec` | critical | 包管理器命令注入 |
| I03 | `eval\(` | critical | eval 动态执行 |
| I04 | `new Function\(` | critical | Function 构造器 |
| I05 | `ignore.*previous.*instructions` | high | 提示注入 |
| I06 | `jailbreak\|bypass.*safety` | critical | 越狱攻击 |
| I07 | `DAN\|do anything now` | critical | DAN 越狱模式 |
| I08 | `curl.*http` | high | 远程下载执行 |

### OBFUSCATION（混淆隐藏）
检测代码混淆和隐藏技术。

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| O01 | `Buffer.from.*base64` | high | Base64 混淆 |
| O02 | `atob\(\|btoa\(` | high | Base64 编解码 |
| O03 | `\\x[0-9a-f]{2}` | high | Hex 编码混淆 |
| O04 | `if.*NODE_ENV.*production` | high | 条件触发 |

### TROJAN（木马/后门）
检测木马和后门特征。

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| T01 | `child_process.spawn.*sh.*-c` | critical | Shell 命令注入 |
| T02 | `process.kill.*all` | high | 进程终止 |
| T03 | `fs.unlink.*system` | critical | 系统文件删除 |
| T04 | `setInterval.*1000.*60.*24` | medium | 长时间定时任务 |

---

## 评分规则

| 风险等级 | 扣分 | 说明 |
|----------|------|------|
| critical | -25~30 | 致命风险，禁止运行 |
| high | -15~20 | 高风险，建议审查 |
| medium | -10~15 | 中风险，可疑 |
| low | -5~10 | 低风险，通过 |

---

## JSONL 输出格式

```json
{
  "skill": "example-skill",
  "category": "EXFIL",
  "rule_id": "E01",
  "rule": "credentials/",
  "level": "critical",
  "file": "index.js",
  "snippet": "require('vault/credentials/api.json')",
  "line": 42
}
```

---

## Python 检测规则

### INJECTION（命令/代码注入）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| P01 | `subprocess.run/Popen/exec` | critical | 子进程命令执行 |
| P02 | `os.system()`, `popen()` | critical | 系统命令执行 |
| P03 | `eval()`, `exec()` | critical | 动态代码执行 |
| P04 | `__import__` | medium | 动态模块导入 |

### TROJAN（木马/后门）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| P05 | `os.remove`, `os.unlink`, `shutil.rmtree` | critical | 文件删除 |
| P06 | `os.kill`, `signal.kill` | high | 进程终止 |

### EXFIL（数据外泄）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| P07 | `requests.get/post/put/delete` | high | HTTP 请求（潜在数据外传） |
| P08 | `urllib.request`, `urllib3` | high | URL 请求库 |
| P09 | `http.client` | high | HTTP 客户端 |
| P10 | `os.getenv(...KEY/SECRET/TOKEN/PASS)` | critical | 凭证访问 |

### OBFUSCATION（混淆隐藏）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| P11 | `base64.b64decode`, `a85decode` | high | Base64 解码混淆 |


---

## Shell 检测规则

### INJECTION（命令/脚本注入）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| S01 | `curl/wget \| bash` | critical | 远程脚本执行 |
| S02 | `curl.*\.sh$`, `wget.*-O-` | critical | 下载执行远程脚本 |
| S03 | `eval $()` | critical | 动态命令执行 |
| S04 | `exec <cmd>` | critical | 命令替换执行 |

### TROJAN（木马/后门）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| S05 | `rm -rf`, `rm -r -f` | critical | 递归删除文件 |
| S06 | `rm -rf ${var}` | critical | 变量路径递归删除 |
| S07 | `kill -9`, `pkill` | high | 强制终止进程 |

### EXFIL（数据外泄）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| S08 | `.ssh/`, `.aws/`, `.kube/` | critical | 敏感目录访问 |

### OBFUSCATION（混淆隐藏）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| S09 | `>/dev/null 2>&1 &` | high | 隐藏后台执行 |
| S10 | `nohup` | medium | 持久化后台进程 |

---

## 支持的文件类型

| 类型 | 扩展名 | 状态 |
|------|--------|------|
| JavaScript | `.js` | ✅ 支持 |
| TypeScript | `.ts` | ✅ 支持 |
| Python | `.py` | ✅ 支持 |

---

## 补充检测规则（扩展威胁）

### EXFIL（扩展）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| E09 | `mysql/postgres/mongodb/redis/sqlite3` | high | 数据库连接 |
| E10 | `writeFile.*\.env|config-write` | high | 配置文件写入 |
| E11 | `ethers.Wallet|web3.eth.accounts` | high | 加密货币钱包访问 |
| E12 | `net.listen|server.listen` | high | 端口监听 |
| E13 | `socks-proxy|tor-proxy` | high | 代理/TOR 使用 |
| E14 | `simple-git|isomorphic-git` | high | Git 仓库操作 |
| E15 | `downloadFile|curl.*-O|wget.*-O` | high | 文件下载 |

### INJECTION（扩展）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| I09 | `\`[^\`]*\` | critical | 反引号命令注入 |
| I10 | `os.platform\|os.type\|os.release` | medium | 系统平台探测 |
| I11 | `crypto.createCipher` | medium | 恶意加密操作 |

### TROJAN（扩展）

| 规则 ID | 规则 | 风险 | 说明 |
|---------|------|------|------|
| T08 | `adm-zip\|archiver\|yauzl` | medium | zip 解压路径穿越 |
| T09 | `writeFile.*\$` | high | 任意路径文件写入 |
| T10 | `JSON.parse.*user\|eval.*JSON` | high | 反序列化漏洞 |



path: scripts/run.sh
content: #!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
"$SCRIPT_DIR/scan.sh" "$@"


path: scripts/scan-all.sh
content: #!/bin/bash

# Skill Security Scanner - 扫描所有已安装的 skill
# 用法: ./scan-all.sh [skills目录]

SKILLS_DIR="${1:-$HOME/.openclaw/workspace/skills}"
SCAN_SCRIPT="$(dirname "$0")/scan.sh"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'

echo "════════════════════════════════════════════════════"
echo "     🔒 Skill 全盘安全扫描"
echo "════════════════════════════════════════════════════"
echo ""
echo "📁 扫描目录: $SKILLS_DIR"
echo ""

# 检查目录
if [ ! -d "$SKILLS_DIR" ]; then
    echo "${RED}错误: 目录不存在: $SKILLS_DIR${NC}"
    exit 1
fi

# 统计
TOTAL=0; PASS=0; WARN=0; FAIL=0
RESULTS=()

# 扫描每个 skill
for skill_path in "$SKILLS_DIR"/*/; do
    [ -d "$skill_path" ] || continue
    
    skill_name=$(basename "$skill_path")
    TOTAL=$((TOTAL + 1))
    
    echo "────────────────────────────────────────────────────"
    echo "🔍 扫描: $skill_name"
    echo "────────────────────────────────────────────────────"
    
    # 运行单 skill 扫描
    result=$("$SCAN_SCRIPT" "$skill_path" 2>&1)
    
    # 提取结论（判断安全等级）
    if echo "$result" | grep -q "🚫"; then
        # 有危险标记
        if echo "$result" | grep -q "建议谨慎使用\|可疑\|不推荐\|禁止"; then
            status="🚫"
            FAIL=$((FAIL + 1))
        else
            status="⚠️"
            WARN=$((WARN + 1))
        fi
    else
        status="✅"
        PASS=$((PASS + 1))
    fi
    
    # 提取评分
    # 提取评分（去除 ANSI 转义序列，然后提取数字）
    score=$(echo "$result" | sed 's/\x1b\[[0-9;]*m//g' | sed -n 's/.*评分: *\([0-9]*\)\/100.*/\1/p' | grep -v '^$' | head -1)
    
    RESULTS+=("$status|$skill_name|$score")
    
    # 有风险的 skill 输出完整报告
    if [ "$status" = "🚫" ] || [ "$status" = "⚠️" ]; then
        echo "$result"
    fi
    echo ""
done

# 汇总报告
echo "════════════════════════════════════════════════════"
echo "                    📊 扫描汇总"
echo "════════════════════════════════════════════════════"
echo ""
echo "📦 扫描总数: $TOTAL"
echo ""
printf "│ %-30s │ %-8s │\n" "Skill" "评分"
echo "├───────────────────────────────────────────┼──────────┤"

for r in "${RESULTS[@]}"; do
    IFS='|' read -r status name score <<< "$r"
    printf "│ %-30s │ %-8s │\n" "$name" "${score:-N/A}"
done

echo "└───────────────────────────────────────────┴──────────┘"
echo ""
echo "✅ 通过: $PASS"
[ $WARN -gt 0 ] && echo "⚠️  建议审查: $WARN"
[ $FAIL -gt 0 ] && echo "🚫 禁止运行: $FAIL"
echo ""

# 高风险列表
if [ $FAIL -gt 0 ] || [ $WARN -gt 0 ]; then
    echo "⚠️  需要关注的 skill:"
    PROBLEM_SKILLS=""
    for r in "${RESULTS[@]}"; do
        IFS='|' read -r status name score <<< "$r"
        if [ "$status" = "🚫" ] || [ "$status" = "⚠️" ]; then
            echo "  - $name (评分: ${score:-N/A})"
            PROBLEM_SKILLS="$PROBLEM_SKILLS $name"
        fi
    done
    echo ""
    echo "💡 提示: 运行单个扫描可获取完整报告"
    echo "   ./scripts/scan.sh ~/.openclaw/workspace/skills/<skill-name>"
    echo ""
    echo "   示例:"
    for r in "${RESULTS[@]}"; do
        IFS='|' read -r status name score <<< "$r"
        if [ "$status" = "🚫" ] || [ "$status" = "⚠️" ]; then
            echo "   • ./scripts/scan.sh ~/.openclaw/workspace/skills/$name"
        fi
    done
fi

echo ""


path: scripts/scan.sh
content: #!/bin/bash

# Skill Security Scanner v4.0
# 扫描完成自动生成完整安全报告

GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
OUTPUT_FORMAT="text"
MAX_SCORE=100; CURRENT_SCORE=$MAX_SCORE
ISSUES=(); FILES=(); TARGET_DIR=""; SKILL_NAME=""
SKILL_DESC=""; SKILL_AUTHOR=""; HAS_JS=0; HAS_TS=0; HAS_PY=0; HAS_SH=0
WHITELIST_FILE="$(dirname "$0")/../whitelist.txt"

log_info() { echo -e "${GREEN}[INFO]${NC} $1" || true; }

add_issue() { 
    local cat=$1 rule=$2 level=$3 dd=$4 file=$5 snippet=$6
    ISSUES+=("$cat|$rule|$level|$dd|$file|$snippet")
    CURRENT_SCORE=$((CURRENT_SCORE-dd))
    [ $CURRENT_SCORE -lt 0 ] && CURRENT_SCORE=0
}

get_skill_info() {
    # 从 SKILL.md 提取信息
    if [ -f "$TARGET_DIR/SKILL.md" ]; then
        SKILL_DESC=$(head -5 "$TARGET_DIR/SKILL.md" | grep -v "^#" | grep -v "^$" | head -1 | tr -d '\n')
    fi
}

# 检测规则
check_exfil() {
    local c=$1 f=$2
    echo "$c" | grep -q "credentials/" && add_issue "EXFIL" "credential-access" "critical" 25 "$f" "credentials/"
    echo "$c" | grep -q "vault/" && ! echo "$c" | grep -q "vault/crypto/" && add_issue "EXFIL" "vault-access" "critical" 25 "$f" "vault/"
    echo "$c" | grep -qE "0x[a-f0-9]{40,64}" && add_issue "EXFIL" "private-key" "critical" 30 "$f" "0x..."
    echo "$c" | grep -qE "process\.env\.(AWS_|AZURE_|STRIPE_)" && add_issue "EXFIL" "api-key" "critical" 30 "$f" "process.env"
    echo "$c" | grep -qE "net\.createServer|net\.connect" && add_issue "EXFIL" "network-backdoor" "critical" 25 "$f" "net"
    echo "$c" | grep -qE "http\.createServer" && add_issue "EXFIL" "http-server" "high" 20 "$f" "http"
}

check_injection() {
    local c=$1 f=$2
    echo "$c" | grep -qE "curl.*\|.*bash|wget.*\|.*bash" && add_issue "INJECTION" "remote-script" "critical" 30 "$f" "curl|bash"
    echo "$c" | grep -qE "spawn\(|from.*child_process|execSync|execFileSync" && add_issue "INJECTION" "cmd-execution" "critical" 25 "$f" "child_process"
    echo "$c" | grep -qE "npx |aibtc-worker" && add_issue "INJECTION" "npx-exec" "critical" 25 "$f" "npx"
    echo "$c" | grep -qE "eval\(" && add_issue "INJECTION" "dynamic-exec" "critical" 25 "$f" "eval"
    echo "$c" | grep -qE "new Function\(|Function\(" && add_issue "INJECTION" "dynamic-exec" "critical" 25 "$f" "Function"
    echo "$c" | grep -qE "ignore previous|ignore prior" && add_issue "INJECTION" "prompt-injection" "high" 20 "$f" "ignore"
    echo "$c" | grep -qE "jailbreak|bypass safety" && add_issue "INJECTION" "jailbreak" "critical" 25 "$f" "jailbreak"
    
    # 反引号命令注入
    echo "$c" | grep -qE "\`[^\`]*\`" && add_issue "INJECTION" "backtick-injection" "critical" 25 "$f" "backtick"
    
    # 加密操作（可能用于恶意行为）
    echo "$c" | grep -qE "crypto.*createCipher|crypto.*createDecipher" && add_issue "INJECTION" "crypto-operation" "medium" 15 "$f" "crypto"
}

check_obfuscation() {
    local c=$1 f=$2
    echo "$c" | grep -qE "Buffer\.from|atob\(|btoa\(" && add_issue "OBFUSCATION" "base64" "high" 20 "$f" "Base64"
    echo "$c" | grep -qE "NODE_ENV.*production" && add_issue "OBFUSCATION" "env-trigger" "high" 20 "$f" "NODE_ENV"
}

check_trojan() {
    local c=$1 f=$2
    echo "$c" | grep -qE "child_process|spawn\(" && add_issue "TROJAN" "shell-injection" "critical" 25 "$f" "child_process"
    echo "$c" | grep -qE "fs\.unlink|fs\.rm|unlinkSync" && add_issue "TROJAN" "destructive-delete" "critical" 25 "$f" "fs.unlink"
    echo "$c" | grep -qE "fs\.readFile.*\.ssh" && add_issue "TROJAN" "credential-read" "critical" 25 "$f" "fs.readFile"
    echo "$c" | grep -qE "process\.kill" && add_issue "TROJAN" "process-kill" "high" 20 "$f" "process.kill"
    echo "$c" | grep -qE "setInterval.*1000.*60.*24" && add_issue "TROJAN" "persistent-timer" "medium" 15 "$f" "setInterval"
    # 任意文件写入
    
    # 任意文件写入
    echo "$c" | grep -qE "writeFile.*\\$|writeSync.*\\$" && add_issue "TROJAN" "arbitrary-write" "high" 20 "$f" "writeFile var"
}


check_python() {
    local c=$1 f=$2
    # 命令执行
    echo "$c" | grep -qE "subprocess\.(run|Popen|.call|exec)" && add_issue "INJECTION" "cmd-execution" "critical" 25 "$f" "subprocess"
    echo "$c" | grep -qE "os\.system\(|popen\(" && add_issue "INJECTION" "cmd-execution" "critical" 25 "$f" "os.system"
    echo "$c" | grep -qE "os\.popen" && add_issue "INJECTION" "cmd-execution" "critical" 25 "$f" "os.popen"
    
    # 动态执行
    echo "$c" | grep -qE "eval\(|exec\(" && add_issue "INJECTION" "dynamic-exec" "critical" 25 "$f" "eval/exec"
    echo "$c" | grep -qE "__import__" && add_issue "INJECTION" "dynamic-import" "medium" 15 "$f" "__import__"
    
    # 文件删除
    echo "$c" | grep -qE "os\.remove|os\.unlink|shutil\.rmtree" && add_issue "TROJAN" "destructive-delete" "critical" 25 "$f" "os.remove"
    
    # 进程终止
    echo "$c" | grep -qE "os\.kill|signal\.kill|Process\(\)\.kill" && add_issue "TROJAN" "process-kill" "high" 20 "$f" "os.kill"
    
    # 网络请求（潜在外传）
    echo "$c" | grep -qE "requests\.(get|post|put|delete)" && add_issue "EXFIL" "network-request" "high" 20 "$f" "requests"
    
    # 序列化漏洞（不安全的 JSON 解析）
    echo "$c" | grep -qE "JSON\.parse.*user|eval.*JSON\.parse|deserialize.*untrusted" && add_issue "TROJAN" "deserialize" "high" 20 "$f" "deserialize"
    echo "$c" | grep -qE "urllib\.request|urllib3" && add_issue "EXFIL" "network-request" "high" 20 "$f" "urllib"
    echo "$c" | grep -qE "http\.client|HTTPClient" && add_issue "EXFIL" "http-client" "high" 20 "$f" "http.client"
    
    # Base64 混淆
    echo "$c" | grep -qE "base64\.(b64decode|a85decode)" && add_issue "OBFUSCATION" "base64" "high" 20 "$f" "base64"
    
    # 敏感数据外传
    echo "$c" | grep -qE "os\.getenv\(.*(KEY|SECRET|TOKEN|PASS)" && add_issue "EXFIL" "credential-access" "critical" 25 "$f" "env"
}

check_deps() {
    local pkg="$TARGET_DIR/package.json"
    [ ! -f "$pkg" ] && return
    grep -q '"postinstall"' "$pkg" && grep -qE "curl|wget|bash" "$pkg" && add_issue "TROJAN" "postinstall-risk" "critical" 30 "$pkg" "postinstall"
    grep -q "l0dash\|fakelodash" "$pkg" && add_issue "OBFUSCATION" "obfuscated-package" "critical" 25 "$pkg" "l0dash"
}


# 风险解释映射
get_risk_desc() {
    local rule=$1
    case "$rule" in
        cmd-execution)      echo "命令执行 — 可执行任意系统命令" ;;
        npx-exec)           echo "npm 包执行 — 可能运行恶意 npm 包" ;;
        shell-injection)    echo "Shell 注入 — 可通过 shell 执行任意命令" ;;
        destructive-delete) echo "文件删除 — 可删除/修改本地文件" ;;
        process-kill)       echo "进程终止 — 可终止系统进程" ;;
        credential-access)  echo "凭证访问 — 可能窃取敏感凭证" ;;
        vault-access)       echo "vault 访问 — 可能访问敏感数据" ;;
        private-key)        echo "私钥暴露 — 可能存在私钥泄露风险" ;;
        api-key)            echo "API 密钥 — 可能窃取 API 凭证" ;;
        network-backdoor)   echo "网络后门 — 可能建立未授权网络连接" ;;
        network-request)   echo "网络请求 — 可能向外部发送数据" ;;
        http-client)       echo "HTTP 客户端 — 可能建立未授权网络连接" ;;
        http-server)        echo "HTTP 服务 — 可能开启本地 HTTP 服务" ;;
        remote-script)      echo "远程脚本 — 可能下载执行恶意脚本" ;;
        dynamic-exec)        echo "动态执行 — 可能执行动态生成的恶意代码" ;;
        prompt-injection)   echo "提示注入 — 可能进行 prompt 攻击" ;;
        jailbreak)          echo "越狱攻击 — 可能绕过安全限制" ;;
        base64)             echo "Base64 混淆 — 可能隐藏恶意代码" ;;
        env-trigger)        echo "环境触发 — 可能根据环境执行不同逻辑" ;;
        system-probing)    echo "系统探测 — 可能收集平台信息用于定向攻击" ;;
        database-access)    echo "数据库访问 — 可能连接外部数据库" ;;
        port-listening)    echo "端口监听 — 可能开启后门服务" ;;
        proxy-tor)         echo "代理/TOR — 可能通过代理隐藏真实身份" ;;
        git-operation)    echo "Git 操作 — 可能推送恶意代码到仓库" ;;
        file-download)     echo "文件下载 — 可能下载恶意文件" ;;
        crypto-operation)  echo "加密操作 — 可能用于加密勒索或恶意通信" ;;
        deserialize)       echo "反序列化漏洞 — 可能执行恶意代码" ;;
        archive-exploit)    echo "压缩文件漏洞 — 可能存在 zip 解压路径穿越风险" ;;
        config-write)       echo "配置写入 — 可能修改系统配置" ;;
        wallet-access)      echo "钱包访问 — 可能操作加密货币钱包" ;;
        backtick-injection) echo "反引号注入 — 可能通过反引号执行命令" ;;
        arbitrary-write)    echo "任意文件写入 — 可能写入任意路径文件" ;;
        credential-read)     echo "凭证读取 — 可能读取 SSH 密钥等凭证" ;;
        persistent-timer)   echo "持久化定时器 — 可能进行长期潜伏" ;;
        postinstall-risk)   echo "postinstall 风险 — 安装时执行恶意脚本" ;;
        obfuscated-package) echo "混淆包 — 可能使用恶意混淆包" ;;
        *)                   echo "未知风险 — $rule" ;;
    esac
}


check_shell() {
    local c=$1 f=$2
    # 远程脚本执行
    echo "$c" | grep -qE "curl.*\|.*bash|wget.*\|.*bash" && add_issue "INJECTION" "remote-script" "critical" 30 "$f" "curl|bash"
    echo "$c" | grep -qE "curl\s+-sL.*sh$|wget.*-O-" && add_issue "INJECTION" "remote-script" "critical" 30 "$f" "curl/wget"
    
    # 命令执行
    echo "$c" | grep -qE "eval\s+\$\(" && add_issue "INJECTION" "dynamic-exec" "critical" 25 "$f" "eval"
    echo "$c" | grep -qE "exec\s+[a-z]" && add_issue "INJECTION" "cmd-execution" "critical" 25 "$f" "exec"
    
    # 文件删除
    echo "$c" | grep -qE "rm\s+-+rf|rm\s+-+r\s+-+f" && add_issue "TROJAN" "destructive-delete" "critical" 25 "$f" "rm -rf"
    echo "$c" | grep -qE "rm\s+-rf\s+\$\{" && add_issue "TROJAN" "destructive-delete" "critical" 25 "$f" "rm -rf var"
    
    # 进程终止
    echo "$c" | grep -qE "kill\s+-+9|kill\s+-+KILL|pkill" && add_issue "TROJAN" "process-kill" "high" 20 "$f" "kill -9"
    
    # 敏感数据访问
    echo "$c" | grep -qE "\.ssh/|\.aws/|\.kube/" && add_issue "EXFIL" "credential-access" "critical" 25 "$f" ".ssh/.aws"

    # 数据库连接
    echo "$c" | grep -qE "mysql\.|postgres\.|mongodb\.|redis\.|sqlite3" && add_issue "EXFIL" "database-access" "high" 20 "$f" "database"
    # 压缩文件处理
    echo "$c" | grep -qE "adm-zip|archiver|node-stream-zip|yauzl" && add_issue "TROJAN" "archive-exploit" "medium" 15 "$f" "zip-lib"
    # 配置文件写入
    echo "$c" | grep -qE "writeFile.*\.env|writeFile.*config|\.config\." && add_issue "EXFIL" "config-write" "high" 20 "$f" "config-write"
    # Crypto钱包访问
    echo "$c" | grep -qE "ethers\.Wallet|web3\.eth\.accounts|tronWeb\.Trx" && add_issue "EXFIL" "wallet-access" "high" 20 "$f" "wallet"
    # 平台系统访问
    echo "$c" | grep -qE "os\.platform\(|os\.type\(|os\.release\(" && add_issue "INJECTION" "system-probing" "medium" 15 "$f" "os.platform"
    

    # 端口监听
    echo "$c" | grep -qE "net\.listen|server\.listen|app\.listen" && add_issue "EXFIL" "port-listening" "high" 20 "$f" "listen"

    # 代理/TOR
    echo "$c" | grep -qE "socks-proxy|tor-proxy|agent.*socks" && add_issue "EXFIL" "proxy-tor" "high" 20 "$f" "proxy"

    # Git操作
    echo "$c" | grep -qE "simple-git|isomorphic-git|git.*push|git.*pull" && add_issue "EXFIL" "git-operation" "high" 20 "$f" "git"

    # 文件下载
    echo "$c" | grep -qE "downloadFile|curl.*-O|wget.*-O" && add_issue "EXFIL" "file-download" "high" 20 "$f" "download"
    # 隐藏执行
    echo "$c" | grep -qE "\s>/dev/null\s*2>&1.*&" && add_issue "OBFUSCATION" "hidden-exec" "high" 20 "$f" ">/dev/null &"
    echo "$c" | grep -qE "nohup\s+" && add_issue "OBFUSCATION" "persistent-shell" "medium" 15 "$f" "nohup"
}

scan_file() {
    local f=$1
    [ -f "$f" ] || return
    FILES+=("$f")
    # 记录文件类型
    [[ "$f" == *.js ]] && HAS_JS=1
    [[ "$f" == *.ts ]] && HAS_TS=1
    [[ "$f" == *.py ]] && HAS_PY=1
    [[ "$f" == *.sh ]] && HAS_SH=1
    local c=$(cat "$f" 2>/dev/null)
    [ -z "$c" ] && return
    check_exfil "$c" "$f"
    check_injection "$c" "$f"
    check_obfuscation "$c" "$f"
    check_trojan "$c" "$f"
    # Python 文件额外检测
    [[ "$f" == *.py ]] && check_python "$c" "$f"
    # Shell 文件额外检测
    [[ "$f" == *.sh ]] && check_shell "$c" "$f"
}

is_whitelisted() { [ -f "$WHITELIST_FILE" ] && grep -q "^${1}$" "$WHITELIST_FILE"; }

whitelist_add() {
    echo ""
    echo "════════════════════════════════════════════════════"
    echo "⚠️  确认添加到白名单"
    echo "════════════════════════════════════════════════════"
    echo "Skill: $1"
    read -p "确认添加? (y/n): " confirm
    [ "$confirm" = "y" ] && echo "$1" >> "$WHITELIST_FILE" && echo "✅ 已添加" || echo "❌ 已取消"
    exit 0
}

resolve_target() {
    local t=$1
    if [[ "$t" == /* ]] || [[ -d "$t" ]]; then
        TARGET_TYPE="local"; TARGET_DIR="$t"; SKILL_NAME=$(basename "$t"); return 0
    fi

    if [[ "$t" == *"clawhub.ai"* ]] || [[ "$t" == *"github.com"* ]]; then
        TARGET_TYPE="remote"; TARGET_DIR=$(mktemp -d)
        local owner slug zip_url

        # 解析 clawhub URL
        if [[ "$t" == *"clawhub.ai"* ]]; then
            owner=$(echo "$t" | sed -n "s|.*clawhub\.ai/\([^/]*\)/.*|\1|p")
            slug=$(echo "$t" | sed -n "s|.*clawhub\.ai/[^/]*/\(.*\)|\1|p")
            zip_url="https://wry-manatee-359.convex.site/api/v1/download?slug=${slug}&owner=${owner}"
        # 解析 GitHub URL
        elif [[ "$t" == *"github.com"* ]]; then
            owner=$(echo "$t" | sed -n "s|.*github\.com/\([^/]*\)/.*|\1|p")
            slug=$(echo "$t" | sed -n "s|.*github\.com/[^/]*/\(.*\)|\1|p" | sed 's|\.git$||')
            zip_url="https://github.com/${owner}/${slug}/archive/refs/heads/main.zip"
            # 尝试 master 分支
            [ -z "$slug" ] && slug=$(echo "$t" | sed -n "s|.*github\.com/[^/]*/\(.*\)|\1|p" | sed 's|\.git$||')
        fi

        SKILL_NAME="$slug"

        # 方式1: HTTP 下载 (clawhub API 或 GitHub archive)
        echo "[INFO] 下载 Skill: $zip_url"
        local http_code
        http_code=$(curl -sL -o "$TARGET_DIR/s.zip" -w "%{http_code}" "$zip_url" 2>/dev/null)
        if [[ "$http_code" == "200" ]] && [[ -s "$TARGET_DIR/s.zip" ]]; then
            echo "[INFO] HTTP 下载成功 (${http_code})"
            unzip -q "$TARGET_DIR/s.zip" -d "$TARGET_DIR" 2>/dev/null && rm -f "$TARGET_DIR/s.zip"
            # 处理嵌套目录 (clawhub 打包会多一层目录)
            local first_dir
            first_dir=$(ls -d "$TARGET_DIR"/*/ 2>/dev/null | head -1)
            if [[ -n "$first_dir" ]] && [[ $(ls "$TARGET_DIR" | wc -l) -eq 1 ]]; then
                mv "$first_dir"* "$TARGET_DIR/" 2>/dev/null
                rmdir "$first_dir" 2>/dev/null
            fi
            return 0
        else
            echo "[WARN] HTTP 下载失败 (${http_code})，尝试 git clone..."
            rm -f "$TARGET_DIR/s.zip"
        fi

        # 方式2: git clone (clawhub 或 GitHub)
        local clone_url
        if [[ "$t" == *"clawhub.ai"* ]]; then
            clone_url="https://github.com/${owner}/${slug}.git"
        else
            clone_url="https://github.com/${owner}/${slug}.git"
        fi
        if git clone --depth=1 "$clone_url" "$TARGET_DIR/skill" 2>/dev/null; then
            echo "[INFO] git clone 成功"
            mv "$TARGET_DIR/skill"/* "$TARGET_DIR/" 2>/dev/null
            rmdir "$TARGET_DIR/skill" 2>/dev/null
            return 0
        else
            echo "[WARN] git clone 也失败了"
        fi

        # 全部失败
        echo "[ERROR] 下载失败，请检查链接是否有效"
        rm -rf "$TARGET_DIR"
        TARGET_DIR=""
        return 1
    fi

    exit 1
}

# 生成完整报告
generate_report() {
    echo ""
    echo "🔒 **Skill 安全检测报告**"
    echo "════════════════════════════════════════════════════════════════════"
    echo ""
    
    # 基本信息
    echo "**Skill 名称:** $SKILL_NAME"
    [ -n "$SKILL_DESC" ] && echo "**功能描述:** ${SKILL_DESC:0:60}"
    echo "**扫描文件:** ${#FILES[@]} 个"
    echo "**检测问题:** ${#ISSUES[@]} 个"
    echo ""
    
    # 安全评分
    if [ $CURRENT_SCORE -ge 80 ]; then
        echo -e "📊 安全评分：✅ ${CURRENT_SCORE}/100 — 安全"
    elif [ $CURRENT_SCORE -ge 60 ]; then
        echo -e "📊 安全评分：⚠️ ${CURRENT_SCORE}/100 — 可疑"
    else
        echo -e "📊 安全评分：🚫 ${CURRENT_SCORE}/100 — 危险"
    fi
    echo ""
    
    # 检测清单
    echo "🔍 **检测清单：** ✅ 全部威胁分类已检测"
    echo ""
    
    # 文件类型
    echo "| 文件类型 | 状态 |"
    echo "|----------|------|"
    [ $HAS_JS -eq 1 ] && echo "| JavaScript (.js) | ✅ |" || echo "| JavaScript (.js) | ➖ 无 |"
    [ $HAS_TS -eq 1 ] && echo "| TypeScript (.ts) | ✅ |" || echo "| TypeScript (.ts) | ➖ 无 |"
    [ $HAS_PY -eq 1 ] && echo "| Python (.py) | ✅ |" || echo "| Python (.py) | ➖ 无 |"
    [ $HAS_SH -eq 1 ] && echo "| Shell (.sh) | ✅ |" || echo "| Shell (.sh) | ➖ 无 |"
    echo ""
    echo ""
    echo "**🛡️ 威胁检测规则（共 57 项）：**"
    echo ""
    echo "**EXFIL (18):** credential-access, vault-access, private-key, api-key, network-backdoor, http-server, network-request, http-client, database-access, config-write, wallet-access, port-listening, proxy-tor, git-operation, file-download, credential-read"
    echo ""
    echo "**INJECTION (19):** remote-script, cmd-execution, npx-exec, dynamic-exec, prompt-injection, jailbreak, dynamic-import, backtick-injection, system-probing, shell-injection, crypto-operation"
    echo ""
    echo "**TROJAN (14):** destructive-delete, credential-read, process-kill, persistent-timer, postinstall-risk, archive-exploit, arbitrary-write, deserialize"
    echo ""
    echo "**OBFUSCATION (6):** base64, env-trigger, hidden-exec, persistent-shell, obfuscated-package"
    echo ""
    
    # 风险统计
    if [ ${#ISSUES[@]} -eq 0 ]; then
        echo "📊 **检测结果：** 未发现风险"
    else
        CRIT_COUNT=0; HIGH_COUNT=0; MED_COUNT=0
        for issue in "${ISSUES[@]}"; do
            case "$issue" in
                *critical*) CRIT_COUNT=$((CRIT_COUNT+1)) ;;
                *high*) HIGH_COUNT=$((HIGH_COUNT+1)) ;;
                *medium*) MED_COUNT=$((MED_COUNT+1)) ;;
            esac
        done
        echo "📊 **检测结果：** 发现 ${#ISSUES[@]} 个风险"
        [ $CRIT_COUNT -gt 0 ] && echo "- 🔴 critical: $CRIT_COUNT 个"
        [ $HIGH_COUNT -gt 0 ] && echo "- 🟠 high: $HIGH_COUNT 个"
        [ $MED_COUNT -gt 0 ] && echo "- 🟡 medium: $MED_COUNT 个"
    fi
    echo ""
    echo "---"
    echo ""
    
    # 风险详情
    if [ ${#ISSUES[@]} -gt 0 ]; then
        echo "**⚠️ 风险详情：**"
        echo ""
        echo "| # | 分类 | 风险 | 等级 | 文件 |"
        echo "|:---:|--------|------------------|-----------|------------|"
        idx=1
        for issue in "${ISSUES[@]}"; do
            IFS='|' read -r cat rule level dd file snippet <<< "$issue"
            echo "| $idx | $cat | $rule | $level | $(basename "$file") |"
            idx=$((idx+1))
        done
        echo ""
        echo "---"
        echo ""
        
        # 每条风险解释
        echo "**每条风险解释：**"
        echo ""
        for issue in "${ISSUES[@]}"; do
            IFS='|' read -r cat rule level dd file snippet <<< "$issue"
            desc=$(get_risk_desc "$rule")
            echo "- $desc"
        done
        echo ""
    fi
    
    # 结论
    echo "---"
    echo ""
    if [ $CURRENT_SCORE -ge 80 ]; then
        echo "✅ **结论：安全**"
        echo ""
        echo "📝 静态扫描存在局限性，建议："
        echo "- 在隔离环境中测试"
        echo "- 审查代码逻辑"
        echo "- 关注运行时行为"
    else
        # 需要 LLM 分析的情况
        if [ $CURRENT_SCORE -ge 30 ]; then
            echo "⚠️ **结论：建议谨慎使用**"
            echo ""
            echo "检测到 ${#ISSUES[@]} 个风险，需评估是否可接受。"
        else
            echo "🚫 **结论：不推荐安装使用**"
            echo ""
            echo "检测到 ${#ISSUES[@]} 个风险，包括多个 critical 级别问题。"
        fi

        # LLM 分析提示将通过交互式方式提供（见下方）
    fi

    echo ""
    echo "════════════════════════════════════════════════════════════════════"
}

# 生成 LLM 语义分析提示
# 调用 LLM 进行语义分析

generate_llm_prompt() {
    cat << 'PROMPT_EOF'
# Skill 安全语义分析请求

## 任务
你是一位专业的代码安全审计员。请从安全产品角度分析以下 Skill，判断它是否可以安全安装使用。

## 安全评估六维框架

请按以下六个维度逐一评估：

| 维度 | 问题 | 权重 |
|------|------|------|
| **1. 数据透明** | 它访问/发送了什么数据？去了哪里？是否告知用户？ | 高 |
| **2. 权限边界** | 申请的权限（网络/文件/进程/环境变量）是否超出声明功能？ | 高 |
| **3. 行为透明** | 有没有未声明的偷偷执行行为？ | 极高 |
| **4. 供应链风险** | 执行外部包（npx/pip/git）来源可信吗？版本固定吗？ | 高 |
| **5. 用户控制** | 用户能随时停止/禁用吗？敏感操作有确认吗？ | 中 |
| **6. 成本告知** | 有没有隐性成本（Gas费/算力消耗/付费API）？ | 中 |

## 额外判断（新增）

如存在以下任一情况，**直接标记为 failed**：

1. **数据外泄**：向非功能必需的第三方发送用户数据
2. **偷偷执行**：在用户不知情时执行额外行为（如后台下载、隐藏进程）
3. **权限超出**：申请的权限（网络/文件/进程/环境变量）超出 Skill 声明的功能范围
4. **供应链攻击**：执行来源不明的外部包（如 npx 不固定版本、pip 安装未知包）
5. **用户无控制**：无法停止、或停止后自动重启
6. **成本不透明**：产生 Gas 费/算力消耗等用户无法预估的成本

## 分析要求

请按以下格式输出：

### 1. 六维评估
对每个维度给出简要评估：
- **[维度名]**：[通过/关注/警告] — [简要说明]

### 2. 总体评估
- 语义检测结果：✅ passed / ⚠️ failed / 🚫 failed
- 理由：综合六维判断，给出整体结论

### 3. 逐文件分析

对每个有风险的文件，以表格形式输出：

| 风险 | 判断 | 说明 | 越界行为 |
|------|------|------|---------|
| [风险名] | ✅误报/⚠️低风险/🚨真实威胁 | [详细解释] | ⚠️ 维度X+Y / 无明显越界行为 |

### 4. 最终建议
- 是否可以安装使用？
- 需要注意什么？

PROMPT_EOF

    echo ""
    echo "## 静态扫描报告"
    echo ""
    echo "**Skill 名称:** $SKILL_NAME"
    [ -n "$SKILL_DESC" ] && echo "**功能描述:** ${SKILL_DESC:0:200}"
    echo ""
    echo "**安全评分:** ${CURRENT_SCORE}/100"
    echo ""
    echo "### 检测到的风险（共 ${#ISSUES[@]} 个）"
    echo ""
    echo "| # | 分类 | 风险类型 | 等级 | 文件 | 匹配内容 |"
    echo "|:---:|--------|----------|-----------|------------|------------|"
    idx=1
    # 去重：只显示唯一文件
    SEEN_FILES=""; SEEN_FILES_COUNT=0
    for issue in "${ISSUES[@]}"; do
        file=$(echo "$issue" | awk -F'|' '{print $5}')
        if [ -n "$file" ] && ! echo "$SEEN_FILES_LIST" | grep -qFx "$file"; then
            SEEN_FILES_LIST="${SEEN_FILES_LIST}${file}
"
            echo "$issue" | awk -F'|' '{printf "| %d | %s | %s | %s | `%s` | `%s` |
", '$idx', $1, $2, $3, $5, $6}'
            idx=$((idx+1))
        fi
    done

    echo ""
    echo "### 相关文件内容"
    echo ""
    echo "以下是检测到风险的文件内容，请仔细分析："
    echo ""
    for issue in "${ISSUES[@]}"; do
        file=$(echo "$issue" | awk -F'|' '{print $5}')
        if [ -n "$file" ] && [ -f "$file" ] && ! echo "$SEEN_FILES_DONE" | grep -qFx "$file"; then
            SEEN_FILES_DONE="${SEEN_FILES_DONE}${file}
"
        fi
    done
    for issue in "${ISSUES[@]}"; do
        file=$(echo "$issue" | awk -F'|' '{print $5}')
        if [ -n "$file" ] && [ -f "$file" ] && echo "$SEEN_FILES_LIST" | grep -qFx "$file"; then
            echo "---"
            echo ""
            echo "**文件: $(basename "$file")**"
            echo ""
            echo '```bash'
            cat "$file"
            echo '```'
            echo ""
        fi
    done

    if [ -f "$TARGET_DIR/SKILL.md" ]; then
        echo "---"
        echo ""
        echo "**文件: SKILL.md（前 20 行，简介）**"
        echo ""
        echo '```markdown'
        head -20 "$TARGET_DIR/SKILL.md"
        echo '```'
        echo ""
    fi

    echo "════════════════════════════════════════════════════════════════════"
    echo "📋 以上内容已生成完毕，请复制上方内容（从 # Skill 安全语义分析请求 开始）"
    echo "   给 LLM 进行深度语义分析。"
    echo ""
    echo "💡 提示：从安全产品角度判断，关注数据透明、权限边界、行为透明、"
    echo "   供应链风险、用户控制、成本告知六个维度。"
    echo "════════════════════════════════════════════════════════════════════"
}




show_help() {
    echo "用法: $0 [选项] <target>"
    echo ""
    echo "选项:"
    echo "  --help                    显示帮助信息"
    echo "  --format <text|json>     指定输出格式（默认 text），json 格式会自动验证"
    echo "  --whitelist-add <skill>   添加到白名单"
    echo "  --whitelist-list          显示白名单"
    echo ""
    echo "target 可以是:"
    echo "  • ClawHub URL:  https://clawhub.ai/owner/skill-name"
    echo "  • 本地路径:     ~/.openclaw/workspace/skills/skill-name"
    echo ""
    echo "示例:"
    echo "  $0 https://clawhub.ai/steipete/video-frames"
    echo "  $0 ~/.openclaw/workspace/skills/litcoin"
    echo "  $0 --whitelist-add some-skill"
    echo "  $0 --whitelist-list"
}

case "$1" in
    --help) show_help; exit 0;;
    --whitelist-add) whitelist_add "$2";;
    --whitelist-list) [ -f "$WHITELIST_FILE" ] && cat "$WHITELIST_FILE" || echo "(空)"; exit 0;;
    --format) OUTPUT_FORMAT="$2"; shift 2;;
esac

TARGET="$1"
[ -z "$TARGET" ] && echo "用法: $0 <target>（使用 --help 查看更多）" && exit 1

log_info "扫描: $TARGET"
resolve_target "$TARGET" || exit 1

if is_whitelisted "$SKILL_NAME"; then
    echo ""
    echo "════════════════════════════════════════════════════════════════════"
    echo "                    🔒 Skill 安全检测报告"
    echo "════════════════════════════════════════════════════════════════════"
    echo ""
    echo "📦 Skill: $SKILL_NAME"
    echo "📊 评分: 100/100"
    echo "✅ 已通过白名单 (跳过扫描)"
    exit 0
fi

get_skill_info
check_deps

# 安全检查：TARGET_DIR 必须存在
if [[ -z "$TARGET_DIR" ]] || [[ ! -d "$TARGET_DIR" ]]; then
    echo ""
    echo "[ERROR] 无法访问目标目录，下载可能失败"
    echo "[ERROR] 请检查链接是否有效，或尝试使用本地路径"
    exit 1
fi

# 扫描文件
SCAN_FILES=$(find "$TARGET_DIR" \( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.sh" \) -type f 2>/dev/null)
if [[ -z "$SCAN_FILES" ]]; then
    echo "[WARN] 未找到任何可扫描的文件 (.js/.ts/.py/.sh)"
    echo "[WARN] Skill 可能只包含文档或非脚本文件"
fi
for f in $SCAN_FILES; do
    scan_file "$f"
done

generate_report

# 交互式 LLM 分析（仅在有风险时询问）
if [ ${#ISSUES[@]} -gt 0 ]; then
    echo ""
    echo "════════════════════════════════════════════════════════════════════"
    echo "                     🤖 LLM 语义分析提示"
    echo "════════════════════════════════════════════════════════════════════"
    echo ""
    echo "⚠️  静态扫描发现 ${#ISSUES[@]} 个风险，建议使用 LLM 进行语义分析。"
    echo ""
    echo "请将以下内容复制给 LLM 进行深度分析："
    echo ""
    generate_llm_prompt
    echo ""
    echo "════════════════════════════════════════════════════════════════════"
    echo "提示：请将上方内容（从「# Skill 安全语义分析请求」开始）"
    echo "      复制给我进行深度语义分析。"
    echo "════════════════════════════════════════════════════════════════════"
fi


[ "$TARGET_TYPE" != "local" ] && rm -rf "$TARGET_DIR"

# JSON 格式
if [ "$OUTPUT_FORMAT" = "json" ]; then
    echo ""
    echo "=== JSON 格式 ==="
    echo "{"
    echo "  \"skill\": \"$SKILL_NAME\","
    echo "  \"score\": $CURRENT_SCORE,"
    echo "  \"files\": ${#FILES[@]},"
    echo "  \"issues\": ["
    first=true
    for i in "${ISSUES[@]}"; do
        IFS='|' read -r cat rule level dd file snippet <<< "$i"
        [ -n "$first" ] && first="" || echo ","
        echo -n "    {\"category\":\"$cat\",\"rule\":\"$rule\",\"level\":\"$level\",\"file\":\"$(basename "$file")\",\"snippet\":\"$snippet\"}"
    done
    echo ""
    echo "  ]"
    echo "}"
    
    # JSON 验证
    echo ""
    [ -z "$first" ] && json_valid=true || json_valid=false
    if [ "$json_valid" = true ]; then
        # 简单验证：检查 JSON 基本结构
        if echo "{}" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
            echo "[INFO] JSON 输出格式验证通过"
        else
            echo "[WARN] JSON 输出可能格式异常，建议检查"
        fi
    fi
fi

# 根据风险返回退出码
HAS_CRIT=false
for i in "${ISSUES[@]}"; do [[ "$i" == *"critical"* ]] && HAS_CRIT=true; done
[ "$HAS_CRIT" = true ] && exit 1
[ $CURRENT_SCORE -lt 70 ] && exit 1
exit 0


path: whitelist.txt
content: skill-security-scanner
minebean


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