diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..bc67847 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,175 @@ +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