Skip to content

Commit 95517f5

Browse files
claudehyperpolymath
authored andcommitted
ci: fix Scorecard score-gate to read JSON .score (SARIF lacks the aggregate)
The score-gate job was already split out (uses-only publish job + separate gate), but it parsed the score from SARIF at .runs[0].tool.driver.properties.score, which SARIF does not carry. The gate therefore read 0 via the `// 0` fallback and failed the < 5 check on every push to main (verified live: scorecard-enforcer run 27468148000, score-gate job failed with "Scorecard score 0 is below minimum 5"). Fix: run the gate's scorecard in JSON mode (results_format: json, publish_results: false) and read the aggregate at top-level `.score`. Converged onto the estate-canonical enforcer (bounded timeouts, SHA pins). https://claude.ai/code/session_01VwbFNQJw23tW8tqM7utWku
1 parent 3f03f17 commit 95517f5

1 file changed

Lines changed: 17 additions & 9 deletions

File tree

.github/workflows/scorecard-enforcer.yml

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ permissions:
2121
contents: read
2222

2323
jobs:
24+
# Publish job. The OSSF attestation flow requires that a job invoking
25+
# ossf/scorecard-action with publish_results: true contain ONLY `uses:`
26+
# steps (no `run:`). A `run:` step in this job makes the OSSF publish
27+
# endpoint reject the upload with HTTP 400 ("scorecard job must only have
28+
# steps with `uses`"), failing the whole workflow. Score enforcement is
29+
# therefore split into the separate unprivileged `score-gate` job below.
2430
scorecard:
2531
runs-on: ubuntu-latest
2632
timeout-minutes: 15
@@ -45,11 +51,13 @@ jobs:
4551
sarif_file: results.sarif
4652

4753
# Unprivileged enforcement gate. Re-derives the score read-only (no
48-
# publish, no id-token) and fails on a low score. Kept separate from the
49-
# privileged `scorecard` publish job above so that arbitrary run: steps
50-
# never execute in a job that holds id-token: write
51-
# (Hypatia: scorecard_publish_with_run_step). publish_results does not
52-
# affect the computed score, so the gate behaviour is unchanged.
54+
# publish, no id-token), so it may legally contain a `run:` step.
55+
# NOTE: uses JSON output deliberately. The SARIF format does NOT carry the
56+
# aggregate score at .runs[0].tool.driver.properties.score (it is absent
57+
# there, so a SARIF-based gate reads the `// 0` fallback and ALWAYS fails
58+
# the < MIN_SCORE check); the JSON format exposes the aggregate at the
59+
# top-level `.score`. publish_results is false here and does not affect the
60+
# computed score, so enforcement behaviour is unchanged.
5361
score-gate:
5462
runs-on: ubuntu-latest
5563
timeout-minutes: 15
@@ -63,14 +71,14 @@ jobs:
6371
- name: Run Scorecard (analysis only)
6472
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
6573
with:
66-
results_file: results.sarif
67-
results_format: sarif
74+
results_file: results.json
75+
results_format: json
6876
publish_results: false
6977

7078
- name: Check minimum score
7179
run: |
72-
# Parse score from results
73-
SCORE=$(jq -r '.runs[0].tool.driver.properties.score // 0' results.sarif 2>/dev/null || echo "0")
80+
# JSON output carries the aggregate score at the top-level `.score`.
81+
SCORE=$(jq -r '.score // 0' results.json 2>/dev/null || echo "0")
7482
7583
echo "OpenSSF Scorecard Score: $SCORE"
7684

0 commit comments

Comments
 (0)