name: Security Scanning

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    # Run daily at 05:43 UTC — catch new CVEs early
    - cron: "43 5 * * *"

concurrency:
  group: security-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read
  security-events: write

jobs:
  # ── Dependency Audit ─────────────────────────────────────────────
  dependency-audit:
    name: Dependency Audit (pnpm)
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run pnpm audit
        run: |
          echo "## Dependency Audit Report" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          # Run audit, capture output and exit code
          set +e
          AUDIT_OUTPUT=$(pnpm audit --json 2>&1)
          AUDIT_EXIT=$?
          set -e

          # Parse and display summary
          echo "$AUDIT_OUTPUT" | jq -r '
            if .metadata then
              "| Severity | Count |\n|----------|-------|\n" +
              "| Critical | \(.metadata.vulnerabilities.critical // 0) |\n" +
              "| High     | \(.metadata.vulnerabilities.high // 0) |\n" +
              "| Moderate | \(.metadata.vulnerabilities.moderate // 0) |\n" +
              "| Low      | \(.metadata.vulnerabilities.low // 0) |\n" +
              "| Info     | \(.metadata.vulnerabilities.info // 0) |"
            else
              "No vulnerabilities found ✅"
            end
          ' >> $GITHUB_STEP_SUMMARY 2>/dev/null || echo "Audit completed" >> $GITHUB_STEP_SUMMARY

          # Also run human-readable output
          pnpm audit 2>&1 || true

          # Fail on critical or high vulnerabilities only
          pnpm audit --audit-level=critical
        continue-on-error: false

  # ── Container Scanning (Trivy) ───────────────────────────────────
  trivy-api:
    name: Trivy Scan — API Image
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build API image for scanning
        uses: docker/build-push-action@v6
        with:
          context: .
          file: apps/api/Dockerfile
          push: false
          load: true
          tags: goodgo-api:scan
          cache-from: type=gha,scope=api-scan
          cache-to: type=gha,mode=max,scope=api-scan

      - name: Run Trivy vulnerability scanner (API)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-api:scan"
          format: "sarif"
          output: "trivy-api-results.sarif"
          severity: "CRITICAL,HIGH"
          # Ignore unfixed vulns to reduce noise
          ignore-unfixed: true

      - name: Upload Trivy SARIF (API)
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: "trivy-api-results.sarif"
          category: "trivy-api"

      - name: Trivy table output (API)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-api:scan"
          format: "table"
          severity: "CRITICAL,HIGH,MEDIUM"
          ignore-unfixed: true

  trivy-web:
    name: Trivy Scan — Web Image
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build Web image for scanning
        uses: docker/build-push-action@v6
        with:
          context: .
          file: apps/web/Dockerfile
          push: false
          load: true
          tags: goodgo-web:scan
          cache-from: type=gha,scope=web-scan
          cache-to: type=gha,mode=max,scope=web-scan

      - name: Run Trivy vulnerability scanner (Web)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-web:scan"
          format: "sarif"
          output: "trivy-web-results.sarif"
          severity: "CRITICAL,HIGH"
          ignore-unfixed: true

      - name: Upload Trivy SARIF (Web)
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: "trivy-web-results.sarif"
          category: "trivy-web"

      - name: Trivy table output (Web)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-web:scan"
          format: "table"
          severity: "CRITICAL,HIGH,MEDIUM"
          ignore-unfixed: true

  trivy-ai:
    name: Trivy Scan — AI Services Image
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build AI Services image for scanning
        uses: docker/build-push-action@v6
        with:
          context: ./libs/ai-services
          file: libs/ai-services/Dockerfile
          push: false
          load: true
          tags: goodgo-ai:scan
          cache-from: type=gha,scope=ai-scan
          cache-to: type=gha,mode=max,scope=ai-scan

      - name: Run Trivy vulnerability scanner (AI)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-ai:scan"
          format: "sarif"
          output: "trivy-ai-results.sarif"
          severity: "CRITICAL,HIGH"
          ignore-unfixed: true

      - name: Upload Trivy SARIF (AI)
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: "trivy-ai-results.sarif"
          category: "trivy-ai"

      - name: Trivy table output (AI)
        uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: "goodgo-ai:scan"
          format: "table"
          severity: "CRITICAL,HIGH,MEDIUM"
          ignore-unfixed: true

  # ── Filesystem / IaC Scanning ────────────────────────────────────
  trivy-fs:
    name: Trivy Filesystem Scan
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Run Trivy filesystem scanner
        uses: aquasecurity/trivy-action@0.28.0
        with:
          scan-type: "fs"
          scan-ref: "."
          format: "sarif"
          output: "trivy-fs-results.sarif"
          severity: "CRITICAL,HIGH"
          ignore-unfixed: true
          scanners: "vuln,secret,misconfig"

      - name: Upload Trivy SARIF (filesystem)
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: "trivy-fs-results.sarif"
          category: "trivy-filesystem"

      - name: Trivy filesystem table output
        uses: aquasecurity/trivy-action@0.28.0
        with:
          scan-type: "fs"
          scan-ref: "."
          format: "table"
          severity: "CRITICAL,HIGH,MEDIUM"
          scanners: "vuln,secret,misconfig"

  # ── Summary Gate ─────────────────────────────────────────────────
  security-gate:
    name: Security Gate
    runs-on: ubuntu-latest
    needs: [dependency-audit, trivy-api, trivy-web, trivy-ai, trivy-fs]
    if: always()

    steps:
      - name: Check security scan results
        run: |
          echo "## Security Scan Summary" >> $GITHUB_STEP_SUMMARY

          # Check each job result
          FAILED=false

          if [ "${{ needs.dependency-audit.result }}" != "success" ]; then
            echo "❌ Dependency audit: ${{ needs.dependency-audit.result }}" >> $GITHUB_STEP_SUMMARY
            FAILED=true
          else
            echo "✅ Dependency audit: passed" >> $GITHUB_STEP_SUMMARY
          fi

          if [ "${{ needs.trivy-api.result }}" != "success" ]; then
            echo "❌ Trivy API scan: ${{ needs.trivy-api.result }}" >> $GITHUB_STEP_SUMMARY
            FAILED=true
          else
            echo "✅ Trivy API scan: passed" >> $GITHUB_STEP_SUMMARY
          fi

          if [ "${{ needs.trivy-web.result }}" != "success" ]; then
            echo "❌ Trivy Web scan: ${{ needs.trivy-web.result }}" >> $GITHUB_STEP_SUMMARY
            FAILED=true
          else
            echo "✅ Trivy Web scan: passed" >> $GITHUB_STEP_SUMMARY
          fi

          if [ "${{ needs.trivy-ai.result }}" != "success" ]; then
            echo "❌ Trivy AI scan: ${{ needs.trivy-ai.result }}" >> $GITHUB_STEP_SUMMARY
            FAILED=true
          else
            echo "✅ Trivy AI scan: passed" >> $GITHUB_STEP_SUMMARY
          fi

          if [ "${{ needs.trivy-fs.result }}" != "success" ]; then
            echo "❌ Trivy filesystem scan: ${{ needs.trivy-fs.result }}" >> $GITHUB_STEP_SUMMARY
            FAILED=true
          else
            echo "✅ Trivy filesystem scan: passed" >> $GITHUB_STEP_SUMMARY
          fi

          if [ "$FAILED" = true ]; then
            echo ""
            echo "⚠️ One or more security scans failed. Review the Security tab for details." >> $GITHUB_STEP_SUMMARY
            exit 1
          fi

          echo ""
          echo "🎉 All security scans passed!" >> $GITHUB_STEP_SUMMARY
