Bump github/codeql-action/analyze from 4.37.0 to 4.37.4 #77
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
| # Central multi-language SAST gate for every ContextualWisdomLab repo. | |
| # | |
| # Fills a governance gap left when the duplicate LOCAL Semgrep workflow was | |
| # removed (xtrmLLMBatchPython) in favour of the central required workflows. | |
| # Semgrep auto-detects the languages present, so this runs everywhere and is a | |
| # no-op on repos with no supported source. | |
| # | |
| # semgrep multi-language SAST -> SARIF uploaded under category "semgrep" | |
| # | |
| # Gating is by the JOB result (high sensitivity: fail on WARNING/ERROR, i.e. | |
| # Medium+), ref-independent, exactly like trivy-fs in security-scan.yml. The | |
| # SARIF is uploaded under a DISTINCT category ("semgrep") and is NOT added to | |
| # the code_scanning ruleset rule, so it does not affect auto-merge. The SARIF | |
| # upload is best-effort (continue-on-error) so a repo that has not enabled code | |
| # scanning still gets the gate without a JOB_STATUS_CONFIGURATION_ERROR. | |
| # | |
| # Engine license: Semgrep OSS CLI is LGPL-2.1 (a containerized CLI invoked in | |
| # CI, not linked) — acceptable under the commercial-only OSS policy. Registry | |
| # ruleset p/default is the Semgrep community pack. | |
| name: SAST Semgrep | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review, closed] | |
| branches: [main, master, develop] | |
| push: | |
| branches: [main, master, develop] | |
| schedule: | |
| - cron: "23 3 * * 1" | |
| repository_dispatch: | |
| types: [sast-semgrep-scan] | |
| concurrency: | |
| group: sast-semgrep-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| cancel-closed-pr-runs: | |
| if: github.event.action == 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: echo "PR closed; this run only cancels older runs through workflow concurrency." | |
| semgrep: | |
| name: Semgrep (multi-language SAST) | |
| if: github.event.action != 'closed' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| env: | |
| # Deterministic, no telemetry: registry rules are fetched but no scan data | |
| # is sent back. | |
| SEMGREP_SEND_METRICS: "off" | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - name: Run Semgrep (SARIF) | |
| id: semgrep | |
| run: | | |
| set +e | |
| echo "Using semgrep/semgrep:1.169.0@sha256:2b33f46ba66cf8cc2ad59ccfa7d22951fd00c632c38f1339e84ec8e6e641a942" | |
| docker run --rm \ | |
| -v "${GITHUB_WORKSPACE}:/src" \ | |
| -w /src \ | |
| -e SEMGREP_SEND_METRICS=off \ | |
| --entrypoint semgrep \ | |
| semgrep/semgrep@sha256:2b33f46ba66cf8cc2ad59ccfa7d22951fd00c632c38f1339e84ec8e6e641a942 \ | |
| scan \ | |
| --config=p/default \ | |
| --severity=WARNING \ | |
| --severity=ERROR \ | |
| --exclude=.github/workflows \ | |
| --exclude='docs/research/**/standards' \ | |
| --error \ | |
| --sarif \ | |
| --output=semgrep-results.raw.sarif \ | |
| --metrics=off | |
| echo "rc=$?" >> "$GITHUB_OUTPUT" | |
| set -e | |
| - name: Remove explicitly suppressed findings from Semgrep SARIF | |
| id: semgrep_sarif | |
| if: always() && hashFiles('semgrep-results.raw.sarif') != '' | |
| run: | | |
| set -euo pipefail | |
| suppressed_count=$(jq '[.runs[]?.results[]? | select(((.suppressions // []) | length) > 0)] | length' semgrep-results.raw.sarif) | |
| jq '(.runs[]? | .results) |= ((. // []) | map(select(((.suppressions // []) | length) == 0)))' \ | |
| semgrep-results.raw.sarif > semgrep-results.sarif | |
| finding_count=$(jq '[.runs[]?.results[]?] | length' semgrep-results.sarif) | |
| echo "suppressed_count=$suppressed_count" >> "$GITHUB_OUTPUT" | |
| echo "finding_count=$finding_count" >> "$GITHUB_OUTPUT" | |
| echo "SEMGREP_SUPPRESSED_COUNT=$suppressed_count SEMGREP_FINDING_COUNT=$finding_count" | |
| - name: Upload Semgrep SARIF to code scanning | |
| if: always() && hashFiles('semgrep-results.sarif') != '' | |
| continue-on-error: true | |
| uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 | |
| with: | |
| sarif_file: semgrep-results.sarif | |
| category: semgrep | |
| - name: Report every Semgrep finding in the job log | |
| if: always() && hashFiles('semgrep-results.sarif') != '' | |
| env: | |
| SEMGREP_RC: ${{ steps.semgrep.outputs.rc }} | |
| SEMGREP_SUPPRESSED_COUNT: ${{ steps.semgrep_sarif.outputs.suppressed_count }} | |
| run: | | |
| set -euo pipefail | |
| finding_count=$(jq '[.runs[]?.results[]?] | length' semgrep-results.sarif) | |
| echo "SEMGREP_FINDING_COUNT=${finding_count} SEMGREP_SUPPRESSED_COUNT=${SEMGREP_SUPPRESSED_COUNT:-missing} SEMGREP_RC=${SEMGREP_RC:-missing}" | |
| jq -r ' | |
| .runs[]? as $run | |
| | ($run.tool.driver.rules // [] | |
| | map({key: .id, value: (.defaultConfiguration.level // "unknown")}) | |
| | from_entries) as $levels | |
| | $run.results[]? | |
| | (.locations[0].physicalLocation // {}) as $location | |
| | "SEMGREP_FINDING rule=\(.ruleId // "unknown")" | |
| + " level=\(.level // $levels[.ruleId] // "unknown")" | |
| + " path=\($location.artifactLocation.uri // "unknown")" | |
| + " line=\($location.region.startLine // 0)" | |
| + " message=\((.message.text // "no message") | gsub("[\r\n]+"; " "))" | |
| ' semgrep-results.sarif | |
| if [ "$finding_count" -eq 0 ] && [ "${SEMGREP_RC:-missing}" != "0" ]; then | |
| echo "SEMGREP_ENGINE_FAILURE rc=${SEMGREP_RC:-missing}: Semgrep failed without a WARNING/ERROR SARIF result; inspect the scan command output above." | |
| fi | |
| - name: Enforce Semgrep gate (fail on Medium+ findings) | |
| if: always() && (steps.semgrep_sarif.outputs.finding_count != '0' || steps.semgrep.outputs.rc != '0') | |
| env: | |
| SEMGREP_RC: ${{ steps.semgrep.outputs.rc }} | |
| SEMGREP_FINDING_COUNT: ${{ steps.semgrep_sarif.outputs.finding_count }} | |
| run: | | |
| if [ "${SEMGREP_FINDING_COUNT:-missing}" != "0" ]; then | |
| echo "::error::Semgrep found WARNING/ERROR (Medium+) findings. Every rule, path, line, and message is listed in the preceding report step and the 'semgrep' code scanning category." | |
| else | |
| echo "::error::Semgrep engine/configuration failed with rc=${SEMGREP_RC}. The concrete scan output and SARIF report are logged above." | |
| fi | |
| exit 1 |