fix(security): close GitHub code scanning alerts #316
Workflow file for this run
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 | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| # 03:00 UTC every Monday so security issues surface before a workweek. | |
| - cron: "0 3 * * 1" | |
| # Public-repo security gate. | |
| # | |
| # Hard, blocking jobs (npm/pnpm audit, pip-audit) fail the workflow on | |
| # high/critical advisories. gosec / trivy stay report-only (artifacts + | |
| # job summary) and, when the repo is public, also upload SARIF to GitHub | |
| # code scanning. CodeQL is public-only and report-oriented — it is not | |
| # part of the required "Security Success" aggregator so a first-time | |
| # CodeQL config issue cannot wedge every PR. | |
| # | |
| # Branch protection should require the single "Security Success" check. | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| concurrency: | |
| group: security-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| gosec: | |
| name: gosec (Go runtime + CLI) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Run gosec | |
| uses: securego/gosec@master | |
| with: | |
| # Standard rule set; the high-noise rules (G104 for unchecked | |
| # errors) are excluded so the report is meaningful. -no-fail keeps | |
| # this report-only — the SARIF artifact below carries the findings. | |
| args: "-fmt sarif -out gosec.sarif -no-fail -exclude G104 ./services/..." | |
| - name: Summarize high-severity findings | |
| if: always() | |
| run: | | |
| if [ -f gosec.sarif ]; then | |
| high=$(python3 -c "import json;d=json.load(open('gosec.sarif'));r=d['runs'][0];print(sum(1 for x in r['results'] if (x.get('level')=='error')))" 2>/dev/null || echo '?') | |
| echo "### gosec — $high error-level finding(s)" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Full SARIF is attached as the \`gosec-sarif\` artifact." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Upload SARIF artifact | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: gosec-sarif | |
| path: gosec.sarif | |
| if-no-files-found: ignore | |
| - name: Upload SARIF to code scanning | |
| if: always() && github.event.repository.private != true | |
| continue-on-error: true | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: gosec.sarif | |
| category: gosec | |
| pnpm-audit: | |
| name: pnpm audit (workspace) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: pnpm | |
| - name: Install | |
| run: pnpm install --frozen-lockfile | |
| - name: Audit production dependencies | |
| # --audit-level=high gates on high/critical only; moderate + | |
| # below flow through Dependabot rather than blocking the build. | |
| # --prod matches what we ship (dashboard, customer-app, sdk-ts). | |
| run: pnpm audit --audit-level=high --prod | |
| npm-audit-docs: | |
| name: npm audit (docs-site) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| cache-dependency-path: docs-site/package-lock.json | |
| - name: Install | |
| working-directory: docs-site | |
| run: npm ci --omit=dev | |
| - name: Audit | |
| working-directory: docs-site | |
| run: npm audit --audit-level=high --omit=dev | |
| pip-audit: | |
| name: pip-audit (sdk-py) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install pip-audit | |
| run: pip install pip-audit | |
| - name: Audit | |
| working-directory: packages/sdk-py | |
| # --vulnerability-service=osv preferred — has wider coverage | |
| # than PyPI's index alone. | |
| run: pip-audit --vulnerability-service=osv | |
| trivy: | |
| name: trivy fs scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Trivy (table to log + summary) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| severity: "HIGH,CRITICAL" | |
| scanners: vuln | |
| format: table | |
| ignore-unfixed: true | |
| output: trivy.txt | |
| - name: Trivy (SARIF artifact) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| severity: "HIGH,CRITICAL" | |
| scanners: vuln | |
| format: sarif | |
| ignore-unfixed: true | |
| output: trivy.sarif | |
| - name: Job summary | |
| if: always() | |
| run: | | |
| { | |
| echo "### trivy fs scan (HIGH/CRITICAL, fixed-only)" | |
| echo '```' | |
| cat trivy.txt 2>/dev/null || echo "(no report produced)" | |
| echo '```' | |
| echo "Full SARIF is attached as the \`trivy-sarif\` artifact." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload SARIF artifact | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: trivy-sarif | |
| path: trivy.sarif | |
| if-no-files-found: ignore | |
| - name: Upload SARIF to code scanning | |
| if: always() && github.event.repository.private != true | |
| continue-on-error: true | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: trivy.sarif | |
| category: trivy | |
| codeql: | |
| name: CodeQL | |
| # Code scanning is free on public repos. Skip on private checkouts | |
| # without GitHub Advanced Security so the upload API cannot 403. | |
| if: ${{ github.event.repository.private != true }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| language: ["go", "javascript", "python"] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: github/codeql-action/init@v4 | |
| with: | |
| languages: ${{ matrix.language }} | |
| - uses: github/codeql-action/autobuild@v4 | |
| - uses: github/codeql-action/analyze@v4 | |
| with: | |
| category: "/language:${{ matrix.language }}" | |
| security-success: | |
| name: Security Success | |
| needs: | |
| [gosec, pnpm-audit, npm-audit-docs, pip-audit, trivy] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check required security jobs | |
| run: | | |
| # Aggregator so branch protection can require one check. | |
| # CodeQL is intentionally omitted — it is report-only. | |
| results='${{ toJSON(needs) }}' | |
| echo "Job results: $results" | |
| echo "$results" | jq -e 'all(.[]; .result == "success" or .result == "skipped")' |