From 45374f9115de7d929465fa028d9d58fea456dc05 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 30 May 2026 18:09:51 +0100 Subject: [PATCH] ci(scorecard-enforcer): split score-threshold from publish job Replace local copy with the post-#304 standards template. The pre-fix shape has the OSSF publish contract violation: webapp: scorecard job must only have steps with uses Post-fix shape: - `scorecard` job: uses-only (now includes upload-artifact for SARIF hand-off) - `check-score` job: `needs: scorecard`, downloads artifact, runs threshold gate Caught 49 estate repos on the 2026-05-30 audit. Detector: hypatia rule WF014. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/scorecard-enforcer.yml | 58 +++++++++++++----------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/.github/workflows/scorecard-enforcer.yml b/.github/workflows/scorecard-enforcer.yml index fb41818..75e2385 100644 --- a/.github/workflows/scorecard-enforcer.yml +++ b/.github/workflows/scorecard-enforcer.yml @@ -21,6 +21,16 @@ permissions: contents: read jobs: + # The OSSF Scorecard publish endpoint enforces a hard contract: the job that + # runs `ossf/scorecard-action` with `publish_results: true` must contain + # ONLY steps with `uses:` (no `run:` steps in the same job). If a `run:` + # step is present, the publish step fails with: + # "webapp: scorecard job must only have steps with uses" + # (49 estate repos hit this; see ROADMAP audit 2026-05-30.) + # + # Fix: split the threshold check into a downstream job that depends on + # `scorecard` and consumes the SARIF artifact. The `scorecard` job stays + # uses-only; `check-score` is the gating job that emits the error. scorecard: runs-on: ubuntu-latest permissions: @@ -36,47 +46,41 @@ jobs: with: results_file: results.sarif results_format: sarif - # The Scorecard publish endpoint requires the job to contain only - # `uses` steps. This workflow also uploads SARIF and checks the score, - # so keep publication disabled and rely on the SARIF upload below. - publish_results: false + publish_results: true - name: Upload SARIF uses: github/codeql-action/upload-sarif@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4 with: sarif_file: results.sarif - - name: Check minimum score - run: | - set -euo pipefail - - # Scorecard's SARIF output is useful for code scanning, but some - # versions do not expose an aggregate score in SARIF. Treat missing - # score metadata as "not enforceable" instead of a false zero. - SCORE="$(jq -r ' - .runs[0].invocations[0].properties.score // - .runs[0].tool.driver.properties.score // - empty - ' results.sarif 2>/dev/null || true)" + - name: Persist SARIF for downstream score-gate job + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: scorecard-results + path: results.sarif + retention-days: 1 - if [ -z "$SCORE" ] || [ "$SCORE" = "null" ]; then - echo "::warning::OpenSSF Scorecard SARIF did not expose an aggregate score; SARIF upload succeeded, skipping numeric threshold." - exit 0 - fi + check-score: + needs: scorecard + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Download SARIF from scorecard job + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v5.0.0 + with: + name: scorecard-results - case "$SCORE" in - ''|*[!0-9.]*) - echo "::warning::OpenSSF Scorecard score '$SCORE' is not numeric; skipping numeric threshold." - exit 0 - ;; - esac + - name: Check minimum score + run: | + SCORE=$(jq -r '.runs[0].tool.driver.properties.score // 0' results.sarif 2>/dev/null || echo "0") echo "OpenSSF Scorecard Score: $SCORE" # Minimum acceptable score (0-10 scale) MIN_SCORE=5 - if awk -v score="$SCORE" -v min="$MIN_SCORE" 'BEGIN { exit !(score < min) }'; then + if [ "$(echo "$SCORE < $MIN_SCORE" | bc -l)" = "1" ]; then echo "::error::Scorecard score $SCORE is below minimum $MIN_SCORE" exit 1 fi