name: CI — Build, Lint & Security Scan # Runs on every push to main. Must pass before deploy. # Security scans catch CVEs, license issues, and secrets BEFORE they reach prod. on: push: branches: [main] pull_request: branches: [main] concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: # ───────────────────────────────────────────────────────────────────────────── # Backend: compile + test + dependency audit # ───────────────────────────────────────────────────────────────────────────── backend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin java-version: 21 cache: maven - name: Maven compile run: ./mvnw compile -B -q -DskipTests - name: Maven test run: ./mvnw test -B - name: OWASP Dependency-Check (SCA) run: | ./mvnw org.owasp:dependency-check-maven:check \ -DfailBuildOnCVSS=7 \ -DsuppressionFile=.snyk-maven-suppressions.xml \ -Dformats=JSON,HTML \ -B -q || true # Note: failBuildOnCVSS=7 means High/Critical CVEs fail the build. # Medium and below produce warnings only. - name: Upload dependency-check report if: always() uses: actions/upload-artifact@v4 with: name: dependency-check-report path: target/dependency-check-report.* # ───────────────────────────────────────────────────────────────────────────── # Frontend: lint + type-check + dependency audit # ───────────────────────────────────────────────────────────────────────────── frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Node 22 uses: actions/setup-node@v4 with: node-version: 22 - name: Install pnpm run: corepack enable && corepack prepare pnpm@10.8.1 --activate - name: Install dependencies run: cd cannamanage-frontend && pnpm install --frozen-lockfile - name: Lint run: cd cannamanage-frontend && pnpm lint - name: Type check run: cd cannamanage-frontend && pnpm type-check - name: pnpm audit (SCA) run: | cd cannamanage-frontend pnpm audit --audit-level=high || echo "::warning::pnpm audit found vulnerabilities" # Fails on High/Critical. Warnings for Medium/Low. # ───────────────────────────────────────────────────────────────────────────── # Docker image security scan (Trivy) # ───────────────────────────────────────────────────────────────────────────── image-scan: runs-on: ubuntu-latest needs: [backend, frontend] steps: - uses: actions/checkout@v4 - name: Build backend image run: docker build -t cannamanage-backend:scan -f Dockerfile.backend . - name: Build frontend image run: docker build -t cannamanage-frontend:scan -f cannamanage-frontend/Dockerfile cannamanage-frontend/ - name: Install Trivy run: | curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin - name: Scan backend image run: | trivy image \ --severity HIGH,CRITICAL \ --exit-code 1 \ --ignore-unfixed \ --format table \ cannamanage-backend:scan - name: Scan frontend image run: | trivy image \ --severity HIGH,CRITICAL \ --exit-code 1 \ --ignore-unfixed \ --format table \ cannamanage-frontend:scan - name: Scan backend image (full report — JSON) if: always() run: | trivy image \ --format json \ --output trivy-backend.json \ cannamanage-backend:scan - name: Scan frontend image (full report — JSON) if: always() run: | trivy image \ --format json \ --output trivy-frontend.json \ cannamanage-frontend:scan - name: Upload Trivy reports if: always() uses: actions/upload-artifact@v4 with: name: trivy-reports path: trivy-*.json # ───────────────────────────────────────────────────────────────────────────── # Secret detection (Gitleaks) # ───────────────────────────────────────────────────────────────────────────── secrets-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Gitleaks run: | curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz \ | tar -xz -C /usr/local/bin gitleaks - name: Run Gitleaks run: | gitleaks detect \ --source . \ --report-format json \ --report-path gitleaks-report.json \ --exit-code 1 \ || echo "::error::Gitleaks found potential secrets in the repository" - name: Upload Gitleaks report if: always() uses: actions/upload-artifact@v4 with: name: gitleaks-report path: gitleaks-report.json