name: Security

# 独立安全扫描工作流:深度安全扫描
# - 定时:每周一凌晨 3 点执行
# - 手动触发:workflow_dispatch(可指定扫描目标)
on:
  schedule:
    - cron: "0 3 * * 1"  # 每周一凌晨 3 点
  workflow_dispatch:
    inputs:
      target_url:
        description: "DAST 扫描目标 URL(留空则使用 NEXTAUTH_URL secret 或 localhost:8015)"
        required: false
        default: ""
      skip_dast:
        description: "跳过 DAST 扫描"
        type: boolean
        required: false
        default: false

jobs:
  deep-security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      # 1. 依赖扫描:npm audit
      - name: Dependency scan (npm audit)
        run: |
          echo "::group::npm audit"
          npm audit --audit-level=moderate || true
          npm audit --json > audit-report.json || true
          echo "::endgroup::"
        continue-on-error: true

      # 2. 深度依赖分析 + 静态分析:Snyk
      - name: Snyk dependency & code scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=medium --sarif-file-output=snyk.sarif
        continue-on-error: true

      # 3. 文件系统扫描:Trivy FS(代码 + 依赖)
      - name: Trivy filesystem scan
        run: |
          echo "::group::Trivy FS scan"
          trivy fs --format json --output trivy-fs-report.json --exit-code 0 .
          trivy fs --format table --exit-code 0 .
          echo "::endgroup::"
        continue-on-error: true

      # 4. 容器镜像扫描:构建 nextjs-app 镜像并扫描
      - name: Build & scan container image
        run: |
          echo "::group::Build Next.js standalone"
          SKIP_ENV_VALIDATION=1 NEXT_TELEMETRY_DISABLED=1 npm run build
          mkdir -p .next/standalone/public
          mkdir -p .next/standalone/.next/static
          cp -r public/* .next/standalone/public/ || true
          cp -r .next/static/* .next/standalone/.next/static/ || true
          cp Dockerfile .next/standalone/Dockerfile
          echo "::endgroup::"

          echo "::group::Build Docker image"
          docker build -t nextjs-app:scan .next/standalone
          echo "::endgroup::"

          echo "::group::Trivy image scan"
          trivy image --format json --output trivy-image-report.json --exit-code 0 nextjs-app:scan
          trivy image --format table --exit-code 0 nextjs-app:scan
          echo "::endgroup::"
        continue-on-error: true

      # 5. DAST:OWASP ZAP 基线扫描
      - name: OWASP ZAP Baseline Scan (DAST)
        if: ${{ github.event.inputs.skip_dast != 'true' }}
        uses: zaproxy/action-baseline@v0.10.0
        with:
          target: ${{ github.event.inputs.target_url || secrets.NEXTAUTH_URL || 'http://localhost:8015' }}
          cmd_options: '-a -j'
        continue-on-error: true

      # 6. 生成汇总报告
      - name: Generate summary report
        if: always()
        run: |
          echo "# 安全扫描汇总报告" > security-summary.md
          echo "" >> security-summary.md
          echo "- 扫描时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> security-summary.md
          echo "- 触发方式: ${{ github.event_name }}" >> security-summary.md
          echo "- 运行编号: ${{ github.run_id }}" >> security-summary.md
          echo "" >> security-summary.md

          echo "## 扫描结果" >> security-summary.md
          echo "" >> security-summary.md
          echo "| 扫描类型 | 状态 | 详情 |" >> security-summary.md
          echo "|---------|------|------|" >> security-summary.md

          # npm audit 汇总
          if [ -f audit-report.json ]; then
            AUDIT_SUMMARY=$(jq -r '.metadata.vulnerabilities | "critical:\(.critical) high:\(.high) moderate:\(.moderate) low:\(.low) info:\(.info)"' audit-report.json 2>/dev/null || echo "解析失败")
            echo "| npm audit | 完成 | ${AUDIT_SUMMARY} |" >> security-summary.md
          else
            echo "| npm audit | 未生成报告 | - |" >> security-summary.md
          fi

          # Trivy FS 汇总
          if [ -f trivy-fs-report.json ]; then
            FS_COUNT=$(jq -r '[.Results[]?.Vulnerabilities[]?] | length' trivy-fs-report.json 2>/dev/null || echo "0")
            echo "| Trivy FS | 完成 | 漏洞数: ${FS_COUNT} |" >> security-summary.md
          else
            echo "| Trivy FS | 未生成报告 | - |" >> security-summary.md
          fi

          # Trivy Image 汇总
          if [ -f trivy-image-report.json ]; then
            IMG_COUNT=$(jq -r '[.Results[]?.Vulnerabilities[]?] | length' trivy-image-report.json 2>/dev/null || echo "0")
            echo "| Trivy Image | 完成 | 漏洞数: ${IMG_COUNT} |" >> security-summary.md
          else
            echo "| Trivy Image | 未生成报告 | - |" >> security-summary.md
          fi

          # Snyk 汇总
          if [ -f snyk.sarif ]; then
            SNYK_COUNT=$(jq -r '[.runs[]?.results[]?] | length' snyk.sarif 2>/dev/null || echo "0")
            echo "| Snyk | 完成 | 问题数: ${SNYK_COUNT} |" >> security-summary.md
          else
            echo "| Snyk | 未生成报告(可能缺少 SNYK_TOKEN) | - |" >> security-summary.md
          fi

          echo "" >> security-summary.md
          echo "## 处理建议" >> security-summary.md
          echo "" >> security-summary.md
          echo "- **Critical**: 24 小时内修复或缓解" >> security-summary.md
          echo "- **High**: 7 天内修复" >> security-summary.md
          echo "- **Medium**: 30 天内修复" >> security-summary.md
          echo "- **Low**: 90 天内评估处理" >> security-summary.md
          echo "" >> security-summary.md
          echo "详细报告见 artifact: security-reports-full" >> security-summary.md

          echo "::notice::安全扫描汇总报告已生成"
          cat security-summary.md

      # 7. 上传所有报告
      - uses: actions/upload-artifact@v3
        if: always()
        with:
          name: security-reports-full
          path: |
            audit-report.json
            trivy-fs-report.json
            trivy-image-report.json
            snyk.sarif
            security-summary.md
