name: Container CVE Scan (development)

on:
  push:
    branches: ["development"]
  workflow_dispatch:

jobs:
  cve-scan:
    name: Scan backend/frontend images for CVEs
    runs-on: ubuntu-latest

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

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

      - name: Docker Hub login (for Scout)
        if: ${{ secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != '' }}
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build backend image (local)
        uses: docker/build-push-action@v6
        with:
          context: ./backend
          file: ./backend/Dockerfile
          push: false
          load: true
          tags: nexapg-backend:dev-scan
          provenance: false
          sbom: false

      - name: Build frontend image (local)
        uses: docker/build-push-action@v6
        with:
          context: ./frontend
          file: ./frontend/Dockerfile
          push: false
          load: true
          tags: nexapg-frontend:dev-scan
          build-args: |
            VITE_API_URL=/api/v1
          provenance: false
          sbom: false

      - name: Trivy scan (backend)
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: nexapg-backend:dev-scan
          format: json
          output: trivy-backend.json
          severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
          ignore-unfixed: false
          exit-code: 0

      - name: Trivy scan (frontend)
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: nexapg-frontend:dev-scan
          format: json
          output: trivy-frontend.json
          severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
          ignore-unfixed: false
          exit-code: 0

      - name: Summarize Trivy severities
        run: |
          python - <<'PY'
          import json
          from collections import Counter
          
          def summarize(path):
              c = Counter()
              with open(path, "r", encoding="utf-8") as f:
                  data = json.load(f)
              for result in data.get("Results", []):
                  for v in result.get("Vulnerabilities", []) or []:
                      c[v.get("Severity", "UNKNOWN")] += 1
              for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "UNKNOWN"]:
                  c.setdefault(sev, 0)
              return c
          
          for label, path in [("backend", "trivy-backend.json"), ("frontend", "trivy-frontend.json")]:
              s = summarize(path)
              print(f"===== Trivy {label} =====")
              print(f"CRITICAL={s['CRITICAL']} HIGH={s['HIGH']} MEDIUM={s['MEDIUM']} LOW={s['LOW']} UNKNOWN={s['UNKNOWN']}")
              print()
          PY

      - name: Docker Scout scan (backend)
        continue-on-error: true
        run: |
          if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
            echo "Docker Hub Scout scan skipped: DOCKERHUB_USERNAME/DOCKERHUB_TOKEN not set." > scout-backend.txt
            exit 0
          fi
          if [ ! -f "$HOME/.docker/config.json" ]; then
            echo "Docker Hub Scout scan skipped: docker login config not found in runner." > scout-backend.txt
            exit 0
          fi
          docker run --rm \
            -v /var/run/docker.sock:/var/run/docker.sock \
            -v "$HOME/.docker:/home/scout/.docker:ro" \
            -e DOCKER_CONFIG=/home/scout/.docker \
            -e DOCKER_SCOUT_HUB_USER="${{ secrets.DOCKERHUB_USERNAME }}" \
            -e DOCKER_SCOUT_HUB_PAT="${{ secrets.DOCKERHUB_TOKEN }}" \
            docker/scout-cli:latest cves nexapg-backend:dev-scan \
            --only-severity critical,high,medium,low > scout-backend.txt 2>&1 || {
              echo "" >> scout-backend.txt
              echo "Docker Scout backend scan failed (non-blocking)." >> scout-backend.txt
            }

      - name: Docker Scout scan (frontend)
        continue-on-error: true
        run: |
          if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
            echo "Docker Hub Scout scan skipped: DOCKERHUB_USERNAME/DOCKERHUB_TOKEN not set." > scout-frontend.txt
            exit 0
          fi
          if [ ! -f "$HOME/.docker/config.json" ]; then
            echo "Docker Hub Scout scan skipped: docker login config not found in runner." > scout-frontend.txt
            exit 0
          fi
          docker run --rm \
            -v /var/run/docker.sock:/var/run/docker.sock \
            -v "$HOME/.docker:/home/scout/.docker:ro" \
            -e DOCKER_CONFIG=/home/scout/.docker \
            -e DOCKER_SCOUT_HUB_USER="${{ secrets.DOCKERHUB_USERNAME }}" \
            -e DOCKER_SCOUT_HUB_PAT="${{ secrets.DOCKERHUB_TOKEN }}" \
            docker/scout-cli:latest cves nexapg-frontend:dev-scan \
            --only-severity critical,high,medium,low > scout-frontend.txt 2>&1 || {
              echo "" >> scout-frontend.txt
              echo "Docker Scout frontend scan failed (non-blocking)." >> scout-frontend.txt
            }

      - name: Print scan summary
        run: |
          echo "===== Docker Scout backend ====="
          test -f scout-backend.txt && cat scout-backend.txt || echo "scout-backend.txt not available"
          echo
          echo "===== Docker Scout frontend ====="
          test -f scout-frontend.txt && cat scout-frontend.txt || echo "scout-frontend.txt not available"

      - name: Upload scan reports
        uses: actions/upload-artifact@v3
        with:
          name: container-cve-scan-reports
          path: |
            trivy-backend.json
            trivy-frontend.json
            scout-backend.txt
            scout-frontend.txt
