diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 26331828..2bd61710 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -14,7 +14,6 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: '1.26.0' GOTOOLCHAIN: auto permissions: @@ -60,9 +59,32 @@ jobs: if: matrix.language == 'go' uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: backend/go.mod cache-dependency-path: backend/go.sum + - name: Verify Go toolchain and build + if: matrix.language == 'go' + run: | + set -euo pipefail + cd backend + go version + MOD_GO_VERSION="$(awk '/^go / {print $2; exit}' go.mod)" + ACTIVE_GO_VERSION="$(go env GOVERSION | sed 's/^go//')" + + case "$ACTIVE_GO_VERSION" in + "$MOD_GO_VERSION"|"$MOD_GO_VERSION".*) + ;; + *) + echo "::error::Go toolchain mismatch: go.mod requires ${MOD_GO_VERSION}, active is ${ACTIVE_GO_VERSION}" + exit 1 + ;; + esac + + go build ./... + + - name: Prepare SARIF output directory + run: mkdir -p sarif-results + - name: Autobuild uses: github/codeql-action/autobuild@9e907b5e64f6b83e7804b09294d44122997950d6 # v4 @@ -70,12 +92,13 @@ jobs: uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4 with: category: "/language:${{ matrix.language }}" + output: sarif-results/${{ matrix.language }}.sarif - name: Check CodeQL Results if: always() run: | - # Find SARIF file (CodeQL action creates it in various locations) - SARIF_FILE=$(find "${{ runner.temp }}" -name "*${{ matrix.language }}*.sarif" -type f 2>/dev/null | head -1) + set -euo pipefail + SARIF_FILE="sarif-results/${{ matrix.language }}.sarif" { echo "## 🔒 CodeQL Security Analysis Results" @@ -85,34 +108,36 @@ jobs: echo "" } >> "$GITHUB_STEP_SUMMARY" - if [ -f "$SARIF_FILE" ]; then - echo "Found SARIF file: $SARIF_FILE" - ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE" 2>/dev/null || echo 0) - WARNING_COUNT=$(jq '[.runs[].results[] | select(.level == "warning")] | length' "$SARIF_FILE" 2>/dev/null || echo 0) - NOTE_COUNT=$(jq '[.runs[].results[] | select(.level == "note")] | length' "$SARIF_FILE" 2>/dev/null || echo 0) - - { - echo "**Findings:**" - echo "- 🔴 Errors: $ERROR_COUNT" - echo "- 🟡 Warnings: $WARNING_COUNT" - echo "- 🔵 Notes: $NOTE_COUNT" - echo "" - - if [ "$ERROR_COUNT" -gt 0 ]; then - echo "❌ **CRITICAL:** High-severity security issues found!" - echo "" - echo "### Top Issues:" - echo '```' - jq -r '.runs[].results[] | select(.level == "error") | "\(.ruleId): \(.message.text)"' "$SARIF_FILE" 2>/dev/null | head -5 - echo '```' - else - echo "✅ No high-severity issues found" - fi - } >> "$GITHUB_STEP_SUMMARY" - else - echo "⚠️ SARIF file not found - check analysis logs" >> "$GITHUB_STEP_SUMMARY" + if [ ! -r "$SARIF_FILE" ]; then + echo "::error::Expected SARIF file is missing or unreadable: $SARIF_FILE" + echo "❌ **ERROR:** SARIF file is missing or unreadable: $SARIF_FILE" >> "$GITHUB_STEP_SUMMARY" + exit 1 fi + echo "Found SARIF file: $SARIF_FILE" + ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE") + WARNING_COUNT=$(jq '[.runs[].results[] | select(.level == "warning")] | length' "$SARIF_FILE") + NOTE_COUNT=$(jq '[.runs[].results[] | select(.level == "note")] | length' "$SARIF_FILE") + + { + echo "**Findings:**" + echo "- 🔴 Errors: $ERROR_COUNT" + echo "- 🟡 Warnings: $WARNING_COUNT" + echo "- 🔵 Notes: $NOTE_COUNT" + echo "" + + if [ "$ERROR_COUNT" -gt 0 ]; then + echo "❌ **CRITICAL:** High-severity security issues found!" + echo "" + echo "### Top Issues:" + echo '```' + jq -r '.runs[].results[] | select(.level == "error") | "\(.ruleId): \(.message.text)"' "$SARIF_FILE" | head -5 + echo '```' + else + echo "✅ No high-severity issues found" + fi + } >> "$GITHUB_STEP_SUMMARY" + { echo "" echo "View full results in the [Security tab](https://github.com/${{ github.repository }}/security/code-scanning)" @@ -121,13 +146,17 @@ jobs: - name: Fail on High-Severity Findings if: always() run: | - SARIF_FILE=$(find "${{ runner.temp }}" -name "*${{ matrix.language }}*.sarif" -type f 2>/dev/null | head -1) + set -euo pipefail + SARIF_FILE="sarif-results/${{ matrix.language }}.sarif" - if [ -f "$SARIF_FILE" ]; then - ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE" 2>/dev/null || echo 0) - - if [ "$ERROR_COUNT" -gt 0 ]; then - echo "::error::CodeQL found $ERROR_COUNT high-severity security issues. Fix before merging." - exit 1 - fi + if [ ! -r "$SARIF_FILE" ]; then + echo "::error::Expected SARIF file is missing or unreadable: $SARIF_FILE" + exit 1 + fi + + ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE") + + if [ "$ERROR_COUNT" -gt 0 ]; then + echo "::error::CodeQL found $ERROR_COUNT high-severity security issues. Fix before merging." + exit 1 fi