#!/bin/bash
# =============================================================================
# 安全审计脚本
# =============================================================================
# 用途: 跑 5 大安全工具,生成审计报告
#   1. bandit     - Python 代码静态分析
#   2. safety     - Python 依赖漏洞
#   3. pip-audit  - Python 依赖漏洞(更准)
#   4. npm audit  - JS 依赖漏洞
#   5. gitleaks   - 仓库 secret 扫描
#
# 用法:
#   bash scripts/security-audit.sh                  # 跑全部
#   bash scripts/security-audit.sh --python         # 只跑 Python 套件
#   bash scripts/security-audit.sh --js             # 只跑 JS 套件
#   bash scripts/security-audit.sh --secrets        # 只跑 secret 扫描
#   bash scripts/security-audit.sh --output FILE    # 自定义报告路径
#
# 退出码:
#   0 = 全过 / 仅 INFO
#   1 = 有 LOW
#   2 = 有 MEDIUM
#   3 = 有 HIGH/CRITICAL
# =============================================================================

set -e

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

info()  { echo -e "${BLUE}[INFO]${NC}  $1"; }
ok()    { echo -e "${GREEN}[OK]${NC}    $1"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }

# 路径
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_ROOT"
REPORT="docs/审计报告/security_audit_$(date +%Y%m%d).md"
LOG_DIR="/tmp/security-audit-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$LOG_DIR" "$(dirname "$REPORT")"

# 参数
RUN_PYTHON=true
RUN_JS=true
RUN_SECRETS=true
for arg in "$@"; do
    case $arg in
        --python) RUN_PYTHON=true; RUN_JS=false; RUN_SECRETS=false ;;
        --js) RUN_PYTHON=false; RUN_JS=true; RUN_SECRETS=false ;;
        --secrets) RUN_PYTHON=false; RUN_JS=false; RUN_SECRETS=true ;;
        --output) REPORT="$2" ;;
    esac
done

# 计数器
PASS=0
WARN=0
FAIL=0
CRITICAL=0

# 报告头
cat > "$REPORT" <<EOF
# 安全审计报告

**审计日期**: $(date +%Y-%m-%d)
**审计人**: Claude(自动化跑批)
**工具**: bandit / safety / pip-audit / npm audit / gitleaks
**关联**: [[风险跟踪表]]

---

## 1. 跑批概览

| 工具 | 范围 | 结果 |
|---|---|---|
EOF

# =============================================================================
# 1. bandit (Python 静态分析)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
    info "── 1/5 bandit: Python 静态分析"

    if ! command -v bandit &> /dev/null; then
        warn "bandit 未安装,跑: pip install bandit"
        echo "| bandit | Python 静态 | ⚠️ 工具未安装 |" >> "$REPORT"
    else
        if bandit -r backend/ -f json -o "$LOG_DIR/bandit.json" 2> "$LOG_DIR/bandit.err" ; then
            ok "bandit: 无问题"
            PASS=$((PASS+1))
            echo "| bandit | Python 静态 | ✅ 无问题 |" >> "$REPORT"
        else
            # 解析 bandit JSON 报告
            if command -v jq &> /dev/null; then
                HIGH=$(jq '[.results[] | select(.issue_severity=="HIGH")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
                MED=$(jq '[.results[] | select(.issue_severity=="MEDIUM")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
                LOW=$(jq '[.results[] | select(.issue_severity=="LOW")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
            else
                HIGH=0; MED=0; LOW=0
            fi
            warn "bandit: HIGH=$HIGH MED=$MED LOW=$LOW"
            [ $HIGH -gt 0 ] && FAIL=$((FAIL+HIGH)) || true
            [ $MED -gt 0 ] && WARN=$((WARN+MED)) || true
            [ $LOW -gt 0 ] && WARN=$((WARN+LOW)) || true
            echo "| bandit | Python 静态 | ⚠️ HIGH=$HIGH MED=$MED LOW=$LOW |" >> "$REPORT"

            # 列出问题
            if [ $HIGH -gt 0 ] || [ $MED -gt 0 ]; then
                cat >> "$REPORT" <<EOR

### bandit 详情

| 文件 | 行 | 严重度 | 问题 |
|---|---|---|---|
EOR
                jq -r '.results[] | "| \(.filename) | \(.line_number) | \(.issue_severity) | \(.issue_text | gsub("\n"; " ")) |"' "$LOG_DIR/bandit.json" >> "$REPORT" 2>/dev/null || true
            fi
        fi
    fi
fi

# =============================================================================
# 2. safety (Python 依赖漏洞)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
    info "── 2/5 safety: Python 依赖漏洞"

    if ! command -v safety &> /dev/null; then
        warn "safety 未安装,跑: pip install safety"
        echo "| safety | Python 依赖 | ⚠️ 工具未安装 |" >> "$REPORT"
    else
        if safety check --file=backend/requirements.txt --output=text > "$LOG_DIR/safety.txt" 2>&1; then
            ok "safety: 无漏洞"
            PASS=$((PASS+1))
            echo "| safety | Python 依赖 | ✅ 无漏洞 |" >> "$REPORT"
        else
            VULN_COUNT=$(grep -c "VULNERABLE" "$LOG_DIR/safety.txt" 2>/dev/null || echo 0)
            warn "safety: $VULN_COUNT 个漏洞"
            [ $VULN_COUNT -gt 0 ] && FAIL=$((FAIL+VULN_COUNT)) || true
            echo "| safety | Python 依赖 | 🔴 $VULN_COUNT 个漏洞 |" >> "$REPORT"
            cat >> "$REPORT" <<EOR

### safety 详情

\`\`\`
$(cat "$LOG_DIR/safety.txt" | head -30)
\`\`\`
EOR
        fi
    fi
fi

# =============================================================================
# 3. pip-audit (Python 依赖漏洞,更准)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
    info "── 3/5 pip-audit: Python 依赖漏洞(精确)"

    if ! command -v pip-audit &> /dev/null; then
        warn "pip-audit 未安装,跑: pip install pip-audit"
        echo "| pip-audit | Python 依赖 | ⚠️ 工具未安装 |" >> "$REPORT"
    else
        if pip-audit -r backend/requirements.txt --format=json > "$LOG_DIR/pip-audit.json" 2>&1; then
            ok "pip-audit: 无漏洞"
            PASS=$((PASS+1))
            echo "| pip-audit | Python 依赖 | ✅ 无漏洞 |" >> "$REPORT"
        else
            VULN_COUNT=$(python3 -c "import json; d=json.load(open('$LOG_DIR/pip-audit.json')); print(len(d.get('vulnerabilities', [])))" 2>/dev/null || echo 0)
            warn "pip-audit: $VULN_COUNT 个漏洞"
            [ $VULN_COUNT -gt 0 ] && FAIL=$((FAIL+VULN_COUNT)) || true
            echo "| pip-audit | Python 依赖 | 🔴 $VULN_COUNT 个漏洞 |" >> "$REPORT"
        fi
    fi
fi

# =============================================================================
# 4. npm audit (JS 依赖漏洞)
# =============================================================================
if [ "$RUN_JS" = true ]; then
    info "── 4/5 npm audit: JS 依赖漏洞"

    for d in frontend-admin frontend-agent frontend-h5 frontend-portal; do
        if [ -d "$d" ]; then
            info "  → $d"
            if [ -f "$d/package-lock.json" ]; then
                cd "$d"
                if npm audit --json > "$LOG_DIR/npm-$d.json" 2>&1; then
                    ok "  $d: 无漏洞"
                    PASS=$((PASS+1))
                else
                    VULN=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('total', 0))" 2>/dev/null || echo 0)
                    CRIT=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('critical', 0))" 2>/dev/null || echo 0)
                    HIGH=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('high', 0))" 2>/dev/null || echo 0)
                    warn "  $d: total=$VULN critical=$CRIT high=$HIGH"
                    [ $CRIT -gt 0 ] && CRITICAL=$((CRITICAL+CRIT)) || true
                    [ $HIGH -gt 0 ] && FAIL=$((FAIL+HIGH)) || true
                    [ $VULN -gt 0 ] && WARN=$((WARN+VULN)) || true
                    echo "| npm-audit-$d | JS 依赖 | ⚠️ total=$VULN crit=$CRIT high=$HIGH |" >> "$REPORT"
                fi
                cd "$PROJECT_ROOT"
            fi
        fi
    done
fi

# =============================================================================
# 5. gitleaks (Secret 扫描)
# =============================================================================
if [ "$RUN_SECRETS" = true ]; then
    info "── 5/5 gitleaks: Secret 扫描"

    if ! command -v gitleaks &> /dev/null; then
        warn "gitleaks 未安装,跑(可选):"
        echo "  brew install gitleaks   # Mac"
        echo "  scoop install gitleaks  # Windows"
        echo "  docker run -v \$(pwd):/repo zricethezav/gitleaks:latest detect --source /repo --no-git -v"
        echo "| gitleaks | Secret 扫描 | ⚠️ 工具未安装 |" >> "$REPORT"
    else
        if gitleaks detect --source . --no-git -v > "$LOG_DIR/gitleaks.txt" 2>&1; then
            ok "gitleaks: 无 secret 泄露"
            PASS=$((PASS+1))
            echo "| gitleaks | Secret 扫描 | ✅ 无泄露 |" >> "$REPORT"
        else
            LEAK_COUNT=$(grep -c "Finding:" "$LOG_DIR/gitleaks.txt" 2>/dev/null || echo 0)
            if [ "$LEAK_COUNT" -gt 0 ]; then
                warn "gitleaks: 发现 $LEAK_COUNT 个 secret"
                CRITICAL=$((CRITICAL+LEAK_COUNT))
                echo "| gitleaks | Secret 扫描 | 🔴 $LEAK_COUNT 个 secret |" >> "$REPORT"

                cat >> "$REPORT" <<EOR

### gitleaks 详情

\`\`\`
$(head -50 "$LOG_DIR/gitleaks.txt")
\`\`\`

> 🚨 **CRITICAL**:发现 secret 泄露,立即:
> 1. 撤销泄露的 token / 密钥
> 2. 创新凭据
> 3. 加进 .gitignore(防二次泄露)
> 4. 改所有引用
EOR
            fi
        fi
    fi
fi

# =============================================================================
# 总结
# =============================================================================
cat >> "$REPORT" <<EOF

---

## 2. 总结

| 等级 | 数量 |
|---|---|
| ✅ PASS | $PASS |
| ⚠️ WARN | $WARN |
| 🔴 FAIL | $FAIL |
| 🚨 CRITICAL | $CRITICAL |

EOF

if [ $CRITICAL -gt 0 ]; then
    cat >> "$REPORT" <<EOF
## 🚨 阻断

发现 **$CRITICAL** 个 CRITICAL 问题,**必须**:
1. 撤销所有泄露的 secret(token / 密钥 / 凭据)
2. 创新凭据 + 配新引用
3. 加进 .gitignore
4. 评审所有引用 + 改

## 下一步

1. 修所有 CRITICAL(立即)
2. 修所有 FAIL(本周末)
3. 评估 WARN(下迭代)
4. 加 CI 自动化跑(本季度)
EOF
    echo ""
    error "🚨 CRITICAL: $CRITICAL 个 secret 泄露或 CRITICAL 漏洞,必须立即处理"
    exit 3
fi

if [ $FAIL -gt 0 ]; then
    cat >> "$REPORT" <<EOF
## 🛑 FAIL

发现 **$FAIL** 个 FAIL(HIGH)级问题,本周末修。

## 下一步

1. 修 FAIL(本周末)
2. 评估 WARN(下迭代)
3. 加 CI 自动化跑
EOF
    echo ""
    warn "🛑 FAIL: $FAIL 个 HIGH 级问题,本周末修"
    exit 2
fi

if [ $WARN -gt 0 ]; then
    cat >> "$REPORT" <<EOF
## ⚠️ WARN

发现 **$WARN** 个 MEDIUM/LOW 级问题,下迭代评估。

## 下一步

1. 评估 WARN(下迭代)
2. 加 CI 自动化跑
3. 跑批频率:每周一次
EOF
    echo ""
    warn "⚠️ WARN: $WARN 个问题,下迭代评估"
    exit 1
fi

cat >> "$REPORT" <<EOF
## ✅ 全部通过

无 CRITICAL / FAIL / WARN,健康度 100%。

## 后续

1. 加 CI 自动化跑(每周 + 推送触发)
2. 跑批频率:每周一次 + 重大变更后
EOF

echo ""
ok "✅ 全部通过,健康度 100%"
ok "报告: $REPORT"
exit 0
