Security Scanning #1088
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security Scanning | |
| # GitHub Advanced Security (GHAS) enabled - SARIF uploads active | |
| # Features: | |
| # - CodeQL code scanning (Go language) | |
| # - Trivy vulnerability scanning with SARIF upload | |
| # - Secret scanning (via GitHub settings, not workflow) | |
| # - Dependency review for PRs | |
| on: | |
| # Pull requests: Run detect-changes and Security Scan Summary only | |
| # This ensures the required Security Scan Summary check is present for merge queue entry. | |
| pull_request: | |
| branches: | |
| - main | |
| - develop | |
| # Push to main/develop: Run security scans with path filters | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| paths: | |
| - 'platform/**/*.go' | |
| - 'ee/platform/**/*.go' | |
| - 'platform/**/go.mod' | |
| - 'platform/**/go.sum' | |
| - 'ee/platform/**/go.mod' | |
| - 'ee/platform/**/go.sum' | |
| # Maven manifests: the Trivy FS scan covers Java example poms, so a | |
| # dependency CVE in a pom must re-trigger the scan on push (otherwise | |
| # the fix is only validated by the nightly schedule). | |
| - '**/pom.xml' | |
| - '**/Dockerfile*' | |
| - '.github/workflows/security.yml' | |
| # Merge queue: Run security scans with path filtering | |
| merge_group: | |
| branches: | |
| - main | |
| - develop | |
| schedule: | |
| # Run daily at 2 AM UTC to catch new vulnerabilities | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| security-events: write # Required for SARIF uploads | |
| actions: read # Required for CodeQL | |
| # Cancel orphaned PR runs when a new commit is pushed. Pushes to main / merge_group | |
| # / scheduled runs use the SHA so they never share a group and never cancel | |
| # each other (the daily 02:00 UTC cron is unaffected). | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| env: | |
| # Disable platform startup telemetry on CI runs (#2092) — security scans run | |
| # against agent + orchestrator binaries which would otherwise emit | |
| # telemetry_type=platform pings polluting the external customer-adoption | |
| # dataset with GH Actions Azure egress IPs. | |
| AXONFLOW_TELEMETRY: 'off' | |
| jobs: | |
| # ============================================================================= | |
| # Change Detection (pull_request and merge_group only) | |
| # ============================================================================= | |
| detect-changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' || github.event_name == 'merge_group' | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| security-relevant: ${{ steps.filter.outputs.security-relevant }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Detect security-relevant changes | |
| uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| security-relevant: | |
| - 'platform/**/*.go' | |
| - 'ee/platform/**/*.go' | |
| - '**/go.mod' | |
| - '**/go.sum' | |
| # Maven manifests so a Java dependency CVE is caught at PR / | |
| # merge-queue time rather than only by the nightly schedule. | |
| - '**/pom.xml' | |
| - '**/Dockerfile*' | |
| - '.github/workflows/security.yml' | |
| # CodeQL Analysis - Static code analysis for security vulnerabilities | |
| # Only runs on nightly schedule - too slow (~7 min) for PR/merge queue workflow | |
| # Trivy scans provide sufficient security coverage for PRs | |
| codeql-analysis: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| # Only run on nightly schedule - saves ~7 minutes on every PR and merge queue entry | |
| if: github.event_name == 'schedule' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v4 | |
| with: | |
| languages: go | |
| queries: security-and-quality | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.25' | |
| cache: false | |
| - name: Build for CodeQL | |
| run: | | |
| cd platform | |
| go build ./... | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v4 | |
| with: | |
| category: "/language:go" | |
| # Dependency Review - Block PRs that introduce vulnerable dependencies | |
| dependency-review: | |
| name: Dependency Review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Dependency Review | |
| uses: actions/dependency-review-action@v5 | |
| with: | |
| fail-on-severity: high | |
| deny-licenses: GPL-3.0, AGPL-3.0 | |
| # Trivy Filesystem Scan | |
| trivy-filesystem-scan: | |
| name: Trivy Filesystem Scan | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| # Trivy's Java analyzer resolves remote parent POMs by default. On | |
| # ee/examples/compliance/rbi-sebi/java/pom.xml that network call cancels | |
| # ("semaphore acquire: context canceled") and FATAL-aborts the WHOLE fs | |
| # scan -> no SARIF -> Security Scan Summary red on every PR. Offline scan | |
| # disables remote dependency resolution (the crash class); skip-dirs below | |
| # also excludes example projects, which are not shipped artifacts. | |
| env: | |
| TRIVY_OFFLINE_SCAN: 'true' | |
| # Run if: push/schedule/workflow_dispatch OR (PR/merge_group with security-relevant changes) | |
| if: | | |
| always() && | |
| (github.event_name == 'push' || | |
| github.event_name == 'schedule' || | |
| github.event_name == 'workflow_dispatch' || | |
| (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.security-relevant == 'true')) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Run Trivy vulnerability scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| format: 'sarif' | |
| output: 'trivy-fs-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| skip-dirs: 'ee/examples' | |
| - name: Upload Trivy results to Security tab | |
| uses: github/codeql-action/upload-sarif@v4 | |
| # Best-effort dashboard upload. Code scanning (GitHub Advanced Security) | |
| # is not enabled on every repo this workflow is synced to (e.g. the public | |
| # community mirror), so upload-sarif 403s there. The Trivy scan still runs; | |
| # this upload is observability only and must never fail CI. | |
| continue-on-error: true | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-fs-results.sarif' | |
| category: 'trivy-filesystem' | |
| - name: Run Trivy (table for logs) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '1' | |
| skip-dirs: 'ee/examples' | |
| # Trivy Configuration Scan | |
| trivy-config-scan: | |
| name: Trivy Configuration Scan | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| # Run if: push/schedule/workflow_dispatch OR (PR/merge_group with security-relevant changes) | |
| if: | | |
| always() && | |
| (github.event_name == 'push' || | |
| github.event_name == 'schedule' || | |
| github.event_name == 'workflow_dispatch' || | |
| (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.security-relevant == 'true')) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Run Trivy config scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'config' | |
| scan-ref: '.' | |
| format: 'sarif' | |
| output: 'trivy-config-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| - name: Upload Trivy config results | |
| uses: github/codeql-action/upload-sarif@v4 | |
| # Best-effort dashboard upload (see trivy-filesystem note above): must not | |
| # fail CI where code scanning is unavailable (e.g. the public mirror). | |
| continue-on-error: true | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-config-results.sarif' | |
| category: 'trivy-config' | |
| - name: Run Trivy config (table) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'config' | |
| scan-ref: '.' | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '0' | |
| # Trivy Secret Scan | |
| trivy-secret-scan: | |
| name: Trivy Secret Scan | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| # Run if: push/schedule/workflow_dispatch OR (PR/merge_group with security-relevant changes) | |
| if: | | |
| always() && | |
| (github.event_name == 'push' || | |
| github.event_name == 'schedule' || | |
| github.event_name == 'workflow_dispatch' || | |
| (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.security-relevant == 'true')) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Run Trivy secret scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| scanners: 'secret' | |
| format: 'table' | |
| exit-code: '0' # Informational (demo tokens trigger false positives) | |
| timeout: '15m' | |
| skip-dirs: 'node_modules,vendor,.cache' | |
| # Skip Maven pom.xml files: even with `scanners: secret`, Trivy's | |
| # language-specific analyzers still try to resolve parent POMs from | |
| # Maven Central (e.g. examples/singapore-pii/java triggers a fetch | |
| # of slf4j-parent), which can hang past the 15m timeout on slow | |
| # mirrors and bring down the whole job. POMs are dependency | |
| # manifests, not credential stores — skipping them does not | |
| # weaken secret detection. Same logic for build.gradle*. | |
| skip-files: '**/pom.xml,**/build.gradle,**/build.gradle.kts' | |
| # Docker Image Scans - Skip on PRs for speed | |
| trivy-docker-agent: | |
| name: Trivy Scan - Agent Docker Image | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' && github.event_name != 'schedule' | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.25' | |
| cache: false | |
| - name: Build agent Docker image | |
| run: | | |
| docker build -t axonflow-agent:${{ github.sha }} -f platform/agent/Dockerfile . | |
| - name: Run Trivy scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'image' | |
| image-ref: 'axonflow-agent:${{ github.sha }}' | |
| format: 'sarif' | |
| output: 'trivy-agent-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| - name: Upload Trivy agent results | |
| uses: github/codeql-action/upload-sarif@v4 | |
| # Skip SARIF upload in merge queue - branch ref gets deleted before upload completes | |
| if: always() && github.event_name != 'merge_group' | |
| with: | |
| sarif_file: 'trivy-agent-results.sarif' | |
| category: 'trivy-docker-agent' | |
| trivy-docker-orchestrator: | |
| name: Trivy Scan - Orchestrator Docker Image | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' && github.event_name != 'schedule' | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.25' | |
| cache: false | |
| - name: Build orchestrator Docker image | |
| run: | | |
| docker build -t axonflow-orchestrator:${{ github.sha }} -f platform/orchestrator/Dockerfile . | |
| - name: Run Trivy scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'image' | |
| image-ref: 'axonflow-orchestrator:${{ github.sha }}' | |
| format: 'sarif' | |
| output: 'trivy-orchestrator-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| - name: Upload Trivy orchestrator results | |
| uses: github/codeql-action/upload-sarif@v4 | |
| # Skip SARIF upload in merge queue - branch ref gets deleted before upload completes | |
| if: always() && github.event_name != 'merge_group' | |
| with: | |
| sarif_file: 'trivy-orchestrator-results.sarif' | |
| category: 'trivy-docker-orchestrator' | |
| # Security Summary | |
| security-summary: | |
| name: Security Scan Summary | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes, codeql-analysis, trivy-filesystem-scan, trivy-config-scan, trivy-secret-scan, trivy-docker-agent, trivy-docker-orchestrator] | |
| if: always() | |
| steps: | |
| - name: Check security scan results | |
| run: | | |
| echo "Security Scan Complete (GHAS Enabled)" | |
| echo "" | |
| echo "Scan Results:" | |
| echo " Detect Changes: ${{ needs.detect-changes.result }}" | |
| # CodeQL only runs on nightly schedule for performance | |
| if [[ "${{ github.event_name }}" == "schedule" ]]; then | |
| echo " CodeQL Analysis: ${{ needs.codeql-analysis.result }}" | |
| else | |
| echo " CodeQL Analysis: skipped (runs nightly)" | |
| fi | |
| echo " Filesystem Scan: ${{ needs.trivy-filesystem-scan.result }}" | |
| echo " Configuration Scan: ${{ needs.trivy-config-scan.result }}" | |
| echo " Secret Scan: ${{ needs.trivy-secret-scan.result }}" | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| echo " Docker Scans: skipped (runs in merge queue)" | |
| else | |
| echo " Agent Docker Scan: ${{ needs.trivy-docker-agent.result }}" | |
| echo " Orchestrator Docker Scan: ${{ needs.trivy-docker-orchestrator.result }}" | |
| fi | |
| echo "" | |
| echo "View detailed results in the Security tab" | |
| - name: Determine overall status | |
| # Accept 'success' or 'skipped' (skipped = no security-relevant changes) | |
| # detect-changes is 'skipped' for push/schedule/workflow_dispatch events | |
| run: | | |
| CRITICAL_FAILED=0 | |
| # Check if detect-changes failed | |
| if [[ "${{ needs.detect-changes.result }}" == "failure" ]]; then | |
| echo "Change detection failed" | |
| CRITICAL_FAILED=1 | |
| fi | |
| # CodeQL failure only counts on nightly schedule (when it actually runs) | |
| if [[ "${{ github.event_name }}" == "schedule" && "${{ needs.codeql-analysis.result }}" == "failure" ]]; then | |
| echo "CodeQL analysis failed" | |
| CRITICAL_FAILED=1 | |
| fi | |
| # Accept success or skipped for Trivy scans | |
| FS_RESULT="${{ needs.trivy-filesystem-scan.result }}" | |
| if [[ "$FS_RESULT" != "success" && "$FS_RESULT" != "skipped" ]]; then | |
| echo "Filesystem scan failed" | |
| CRITICAL_FAILED=1 | |
| fi | |
| SECRET_RESULT="${{ needs.trivy-secret-scan.result }}" | |
| if [[ "$SECRET_RESULT" != "success" && "$SECRET_RESULT" != "skipped" ]]; then | |
| echo "Secret scan failed - CRITICAL" | |
| CRITICAL_FAILED=1 | |
| fi | |
| if [[ $CRITICAL_FAILED -eq 1 ]]; then | |
| echo "" | |
| echo "Critical security scans failed - review Security tab" | |
| exit 1 | |
| else | |
| echo "" | |
| echo "All critical security scans passed" | |
| fi |