From 2c01b5d703b67dbedd1d38d7def51035ea8355ae Mon Sep 17 00:00:00 2001 From: npasquin Date: Mon, 8 Dec 2025 13:48:13 +0100 Subject: [PATCH 1/2] feat(ci): add security scan for PRs, Trivy as quality gate, update CodeQL to v4, remove redundant test artifacts --- .github/workflows/ci.yml | 83 ++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73e920d..b5f843c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,13 +81,6 @@ jobs: files: '**/build/test-results/test/TEST-*.xml' check_name: 'Unit Test Results' - - name: Upload Test Reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: unit-test-reports - path: '**/build/reports/tests/' - retention-days: 7 # ============================================================================== # Job 3: Integration Tests @@ -122,16 +115,75 @@ jobs: files: '**/build/test-results/integrationTest/TEST-*.xml' check_name: 'Integration Test Results' - - name: Upload Integration Test Reports + + # ============================================================================== + # Job 4: Security Scan (PRs only - build locally and scan) + # ============================================================================== + security-scan: + name: Security Scan + runs-on: ubuntu-latest + needs: [detekt, unit-tests, integration-tests] + if: github.event_name == 'pull_request' + permissions: + contents: read + security-events: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + cache: 'gradle' + + - name: Build application JAR + run: ./gradlew assembly:buildFatJar --no-daemon --parallel -x test -x detekt + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image (local) + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: false + load: true + tags: se-api:pr-scan + + - name: Scan image with Trivy + uses: aquasecurity/trivy-action@master + with: + image-ref: 'se-api:pr-scan' + format: 'table' + severity: 'CRITICAL,HIGH' + ignore-unfixed: true + exit-code: '1' + + - name: Scan image with Trivy (SARIF) if: always() - uses: actions/upload-artifact@v4 + uses: aquasecurity/trivy-action@master with: - name: integration-test-reports - path: '**/build/reports/tests/integrationTest/' - retention-days: 7 + image-ref: 'se-api:pr-scan' + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + ignore-unfixed: true + exit-code: '0' + + - name: Upload Trivy results to GitHub Security + if: always() + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: 'trivy-results.sarif' # ============================================================================== - # Job 4: Build and Push Docker Image (only on main) + # Job 5: Build and Push Docker Image (only on main) # ============================================================================== build: name: Build & Push Image @@ -213,11 +265,12 @@ jobs: format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' - exit-code: '0' + ignore-unfixed: true + exit-code: '1' - name: Upload Trivy results to GitHub Security if: always() - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: 'trivy-results.sarif' From 986f1b4ce042e198762261cc8f542a6a81bf355f Mon Sep 17 00:00:00 2001 From: npasquin Date: Mon, 8 Dec 2025 14:37:22 +0100 Subject: [PATCH 2/2] feat(ci): add Trivy scan results as PR comment --- .github/workflows/ci.yml | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5f843c..15762d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,10 +157,12 @@ jobs: tags: se-api:pr-scan - name: Scan image with Trivy + id: trivy-scan uses: aquasecurity/trivy-action@master with: image-ref: 'se-api:pr-scan' format: 'table' + output: 'trivy-table.txt' severity: 'CRITICAL,HIGH' ignore-unfixed: true exit-code: '1' @@ -176,6 +178,70 @@ jobs: ignore-unfixed: true exit-code: '0' + - name: Post Trivy scan results as PR comment + if: always() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + let trivyOutput = ''; + try { + trivyOutput = fs.readFileSync('trivy-table.txt', 'utf8'); + } catch (e) { + trivyOutput = 'No vulnerabilities found or scan did not produce output.'; + } + + const scanPassed = '${{ steps.trivy-scan.outcome }}' === 'success'; + const statusEmoji = scanPassed ? '✅' : '❌'; + const statusText = scanPassed ? 'PASSED' : 'FAILED'; + + const body = `## ${statusEmoji} Security Scan ${statusText} + + **Scan Configuration:** + - Severity: \`CRITICAL, HIGH\` + - Ignore unfixed: \`true\` + +
+ 📋 Trivy Scan Results (click to expand) + + \`\`\` + ${trivyOutput} + \`\`\` + +
+ + ${!scanPassed ? '⚠️ **This PR cannot be merged until the vulnerabilities above are fixed.**' : ''} + `; + + // Find existing comment to update + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('Security Scan') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } + - name: Upload Trivy results to GitHub Security if: always() uses: github/codeql-action/upload-sarif@v4