fix: enforce fresh nightly promotion quality gates Ensure promotion decisions are based on current nightly HEAD evidence instead of stale workflow history. Add native CodeQL branch triggers so security analysis runs on nightly/main promotion paths. Convert nightly and weekly automation to dispatch required checks only when missing for the exact HEAD commit, preventing duplicate/racing runs while guaranteeing check presence. Harden weekly health verification with retry polling so transient scheduling delays do not produce false negatives. This reduces false blocking and ensures nightly-to-main promotion uses current, deterministic CI state. Refs: #712
129 lines
4.5 KiB
YAML
129 lines
4.5 KiB
YAML
name: CodeQL - Analyze
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main, nightly]
|
|
push:
|
|
branches: [main, nightly, development]
|
|
workflow_dispatch:
|
|
schedule:
|
|
- cron: '0 3 * * 1' # Mondays 03:00 UTC
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
GO_VERSION: '1.26.0'
|
|
GOTOOLCHAIN: auto
|
|
|
|
permissions:
|
|
contents: read
|
|
security-events: write
|
|
actions: read
|
|
pull-requests: read
|
|
|
|
jobs:
|
|
analyze:
|
|
name: CodeQL analysis (${{ matrix.language }})
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
security-events: write
|
|
actions: read
|
|
pull-requests: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
language: [ 'go', 'javascript-typescript' ]
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
with:
|
|
ref: ${{ github.sha }}
|
|
|
|
- name: Initialize CodeQL
|
|
uses: github/codeql-action/init@015d8c7cbcbb8e7252a7dccfe81a90aa176260b2 # v4
|
|
with:
|
|
languages: ${{ matrix.language }}
|
|
# Use CodeQL config to exclude documented false positives
|
|
# Go: Excludes go/request-forgery for url_testing.go (has 4-layer SSRF defense)
|
|
# See: .github/codeql/codeql-config.yml for full justification
|
|
config-file: ./.github/codeql/codeql-config.yml
|
|
|
|
- name: Setup Go
|
|
if: matrix.language == 'go'
|
|
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache-dependency-path: backend/go.sum
|
|
|
|
- name: Autobuild
|
|
uses: github/codeql-action/autobuild@015d8c7cbcbb8e7252a7dccfe81a90aa176260b2 # v4
|
|
|
|
- name: Perform CodeQL Analysis
|
|
uses: github/codeql-action/analyze@015d8c7cbcbb8e7252a7dccfe81a90aa176260b2 # v4
|
|
with:
|
|
category: "/language:${{ matrix.language }}"
|
|
|
|
- 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)
|
|
|
|
{
|
|
echo "## 🔒 CodeQL Security Analysis Results"
|
|
echo ""
|
|
echo "**Language:** ${{ matrix.language }}"
|
|
echo "**Query Suite:** security-and-quality"
|
|
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"
|
|
fi
|
|
|
|
{
|
|
echo ""
|
|
echo "View full results in the [Security tab](https://github.com/${{ github.repository }}/security/code-scanning)"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- 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)
|
|
|
|
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
|
|
fi
|