name: security-ci

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - master
      - main
  schedule:
    - cron: "17 2 * * 1"

permissions:
  contents: read
  security-events: write

env:
  BANDIT_MAX_HIGH: "0"
  DEP_MAX_VULNS: "0"
  TRIVY_MAX_HIGH: "0"
  TRIVY_MAX_CRITICAL: "0"

jobs:
  sast-and-dependencies:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install scanners
        run: |
          python -m pip install --upgrade pip
          pip install bandit pip-audit

      - name: Prepare reports directory
        run: mkdir -p reports/security

      - name: Run Bandit (SAST)
        run: |
          bandit -r app scripts -f json -o reports/security/bandit.json || true

      - name: Run pip-audit (dependencies)
        run: |
          pip-audit -r requirements.txt --format json --output reports/security/pip-audit.json || true

      - name: Build SAST/dependency summary
        id: sast_deps_summary
        run: |
          set -euo pipefail

          BANDIT_HIGH=$(jq '[.results[]? | select(.issue_severity == "HIGH")] | length' reports/security/bandit.json)
          DEP_VULNS=$(jq '
            if type == "array" then length
            elif has("vulnerabilities") then (.vulnerabilities | length)
            elif has("dependencies") then ([.dependencies[]?.vulns[]?] | length)
            else 0
            end
          ' reports/security/pip-audit.json)

          {
            echo "SAST/Dependency Security Summary"
            echo "bandit.high=${BANDIT_HIGH}"
            echo "deps.vulns=${DEP_VULNS}"
            echo "threshold.bandit.high=${BANDIT_MAX_HIGH}"
            echo "threshold.deps.vulns=${DEP_MAX_VULNS}"
          } | tee reports/security/sast-deps-summary.txt

          echo "bandit_high=${BANDIT_HIGH}" >> "$GITHUB_OUTPUT"
          echo "dep_vulns=${DEP_VULNS}" >> "$GITHUB_OUTPUT"

      - name: Upload SAST/dependency reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: security-sast-deps-reports
          path: |
            reports/security/bandit.json
            reports/security/pip-audit.json
            reports/security/sast-deps-summary.txt
          if-no-files-found: error
          retention-days: 30

      - name: Enforce SAST/dependency thresholds
        run: |
          set -euo pipefail
          BANDIT_HIGH="${{ steps.sast_deps_summary.outputs.bandit_high }}"
          DEP_VULNS="${{ steps.sast_deps_summary.outputs.dep_vulns }}"

          if [ "${BANDIT_HIGH}" -gt "${BANDIT_MAX_HIGH}" ]; then
            echo "Bandit HIGH findings: ${BANDIT_HIGH} (max ${BANDIT_MAX_HIGH})"
            exit 1
          fi
          if [ "${DEP_VULNS}" -gt "${DEP_MAX_VULNS}" ]; then
            echo "Dependency vulnerabilities: ${DEP_VULNS} (max ${DEP_MAX_VULNS})"
            exit 1
          fi

  container-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Build backend image
        run: |
          docker build -t law-backend-security:${{ github.sha }} .

      - name: Prepare reports directory
        run: mkdir -p reports/security

      - name: Run Trivy image scan (JSON report)
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: law-backend-security:${{ github.sha }}
          format: json
          output: reports/security/trivy-image.json
          severity: HIGH,CRITICAL
          exit-code: "0"

      - name: Run Trivy image scan (SARIF)
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: law-backend-security:${{ github.sha }}
          format: sarif
          output: reports/security/trivy-image.sarif
          severity: HIGH,CRITICAL
          exit-code: "0"

      - name: Build container scan summary
        id: trivy_summary
        run: |
          set -euo pipefail
          TRIVY_HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' reports/security/trivy-image.json)
          TRIVY_CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' reports/security/trivy-image.json)

          {
            echo "Container Security Summary"
            echo "trivy.high=${TRIVY_HIGH}"
            echo "trivy.critical=${TRIVY_CRITICAL}"
            echo "threshold.trivy.high=${TRIVY_MAX_HIGH}"
            echo "threshold.trivy.critical=${TRIVY_MAX_CRITICAL}"
          } | tee reports/security/trivy-summary.txt

          echo "trivy_high=${TRIVY_HIGH}" >> "$GITHUB_OUTPUT"
          echo "trivy_critical=${TRIVY_CRITICAL}" >> "$GITHUB_OUTPUT"

      - name: Upload Trivy reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: security-container-reports
          path: |
            reports/security/trivy-image.json
            reports/security/trivy-image.sarif
            reports/security/trivy-summary.txt
          if-no-files-found: error
          retention-days: 30

      - name: Upload SARIF to Security tab
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: reports/security/trivy-image.sarif

      - name: Enforce container scan thresholds
        run: |
          set -euo pipefail
          TRIVY_HIGH="${{ steps.trivy_summary.outputs.trivy_high }}"
          TRIVY_CRITICAL="${{ steps.trivy_summary.outputs.trivy_critical }}"

          if [ "${TRIVY_HIGH}" -gt "${TRIVY_MAX_HIGH}" ]; then
            echo "Trivy HIGH findings: ${TRIVY_HIGH} (max ${TRIVY_MAX_HIGH})"
            exit 1
          fi
          if [ "${TRIVY_CRITICAL}" -gt "${TRIVY_MAX_CRITICAL}" ]; then
            echo "Trivy CRITICAL findings: ${TRIVY_CRITICAL} (max ${TRIVY_MAX_CRITICAL})"
            exit 1
          fi
