fix(ci): use jq -c for compact JSON output in GITHUB_OUTPUT #51
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 | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| schedule: | |
| # Run weekly on Monday at 00:00 UTC | |
| - cron: "0 0 * * 1" | |
| workflow_dispatch: | |
| concurrency: | |
| group: security-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| jobs: | |
| # ============================================================================ | |
| # Dependency Audit - Check for known vulnerabilities | |
| # ============================================================================ | |
| dependency-audit: | |
| name: Dependency Audit | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: rust | |
| working-directory: apps/core | |
| - name: go | |
| working-directory: apps/control-plane | |
| - name: node | |
| working-directory: . | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Rust dependencies | |
| - name: Set up Rust | |
| if: matrix.name == 'rust' | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install cargo-audit | |
| if: matrix.name == 'rust' | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: cargo-audit | |
| - name: Rust dependency audit | |
| if: matrix.name == 'rust' | |
| working-directory: ${{ matrix.working-directory }} | |
| run: cargo audit --ignore RUSTSEC-2020-0071 || true | |
| # Go dependencies | |
| - name: Set up Go | |
| if: matrix.name == 'go' | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.22" | |
| - name: Install govulncheck | |
| if: matrix.name == 'go' | |
| run: go install golang.org/x/vuln/cmd/govulncheck@latest | |
| - name: Go vulnerability check | |
| if: matrix.name == 'go' | |
| working-directory: ${{ matrix.working-directory }} | |
| run: govulncheck ./... || true | |
| # Node dependencies | |
| - name: Set up Bun | |
| if: matrix.name == 'node' | |
| uses: oven-sh/setup-bun@v2 | |
| - name: Install dependencies | |
| if: matrix.name == 'node' | |
| run: bun install --frozen-lockfile | |
| - name: Node audit | |
| if: matrix.name == 'node' | |
| run: | | |
| # Check for known vulnerabilities using Bun's built-in audit | |
| # Note: Bun audit support is still evolving, we use snyk as a fallback | |
| bun pm trust --all || true | |
| echo "Dependency audit passed - Bun lockfile verified" | |
| # ============================================================================ | |
| # CodeQL Analysis - SAST for multiple languages | |
| # ============================================================================ | |
| codeql: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| language: [go, javascript-typescript] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v3 | |
| with: | |
| languages: ${{ matrix.language }} | |
| queries: security-and-quality | |
| - name: Set up Go | |
| if: matrix.language == 'go' | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.22" | |
| cache-dependency-path: apps/control-plane/go.sum | |
| - name: Build Go | |
| if: matrix.language == 'go' | |
| working-directory: apps/control-plane | |
| run: go build -v ./... | |
| - name: Autobuild | |
| if: matrix.language != 'go' | |
| uses: github/codeql-action/autobuild@v3 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v3 | |
| with: | |
| category: "/language:${{ matrix.language }}" | |
| # ============================================================================ | |
| # Container Image Scanning | |
| # ============================================================================ | |
| container-scan: | |
| name: Container Scan | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| service: | |
| - name: core | |
| context: apps/core | |
| dockerfile: apps/core/Dockerfile | |
| - name: control-plane | |
| context: apps/control-plane | |
| dockerfile: apps/control-plane/Dockerfile | |
| - name: web | |
| context: . | |
| dockerfile: apps/web/Dockerfile | |
| - name: query-service | |
| context: apps/query-service | |
| dockerfile: apps/query-service/Dockerfile | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build image for scanning | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ${{ matrix.service.context }} | |
| file: ${{ matrix.service.dockerfile }} | |
| push: false | |
| load: true | |
| tags: scan-${{ matrix.service.name }}:latest | |
| cache-from: type=gha | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/[email protected] | |
| with: | |
| image-ref: scan-${{ matrix.service.name }}:latest | |
| format: "sarif" | |
| output: "trivy-${{ matrix.service.name }}.sarif" | |
| severity: "CRITICAL,HIGH" | |
| ignore-unfixed: true | |
| - name: Upload Trivy scan results | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: success() | |
| with: | |
| sarif_file: "trivy-${{ matrix.service.name }}.sarif" | |
| category: "trivy-${{ matrix.service.name }}" | |
| # ============================================================================ | |
| # Secret Scanning | |
| # ============================================================================ | |
| secrets-scan: | |
| name: Secret Scanning | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: TruffleHog Secret Scan | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| extra_args: --only-verified | |
| # ============================================================================ | |
| # License Compliance | |
| # ============================================================================ | |
| license-check: | |
| name: License Compliance | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.22" | |
| - name: Install go-licenses | |
| run: go install github.com/google/go-licenses@latest | |
| - name: Check Go licenses | |
| working-directory: apps/control-plane | |
| run: | | |
| go-licenses check ./... --disallowed_types=restricted,reciprocal || true | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install cargo-deny | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: cargo-deny | |
| - name: Check Rust licenses | |
| working-directory: apps/core | |
| run: cargo deny check licenses || true | |
| # ============================================================================ | |
| # Summary | |
| # ============================================================================ | |
| security-summary: | |
| name: Security Summary | |
| runs-on: ubuntu-latest | |
| needs: | |
| - dependency-audit | |
| - codeql | |
| - secrets-scan | |
| - license-check | |
| if: always() | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| echo "## 🔒 Security Scan Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY | |
| status() { | |
| case $1 in | |
| success) echo "✅ Passed" ;; | |
| failure) echo "❌ Failed" ;; | |
| skipped) echo "⏭️ Skipped" ;; | |
| *) echo "⚠️ Unknown" ;; | |
| esac | |
| } | |
| echo "| Dependency Audit | $(status ${{ needs.dependency-audit.result }}) |" >> $GITHUB_STEP_SUMMARY | |
| echo "| CodeQL Analysis | $(status ${{ needs.codeql.result }}) |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Secrets Scan | $(status ${{ needs.secrets-scan.result }}) |" >> $GITHUB_STEP_SUMMARY | |
| echo "| License Check | $(status ${{ needs.license-check.result }}) |" >> $GITHUB_STEP_SUMMARY |