Skip to content

ci: add an OpenSSF Scorecard workflow and README badge - #71

Open
doublewhy wants to merge 2 commits into
devfrom
53-scorecard-workflow
Open

doublewhy wants to merge 2 commits into
devfrom
53-scorecard-workflow

Conversation

@doublewhy

@doublewhy doublewhy commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

Adds an OpenSSF Scorecard workflow that runs weekly and on each push to main. It publishes the result to the Scorecard API, which backs the badge this PR adds under the README title.

Stacked on #74, so merge it after #74. The branch now carries #74's uv.lock fix (commit 6ee2564; #74 is the PR for #76) under this PR's own commit 744e90a, which is unchanged. That clears CI's audit job, which failed here only on dev's lock. Once #74 is merged, the squash of this PR adds only the two files below.

Requirement UIDs

Related Issues

Closes #53

ADR Impact

  • None. No architecture decision is affected.

Changes

  • New .github/workflows/scorecard.yml. It is the shape of the OpenRAE/lilrae and upstream ossf/scorecard Scorecard workflows: checkout, ossf/scorecard-action with publish_results: true, SARIF artifact (5-day retention, as upstream and rae use; lilrae keeps 7), and SARIF upload to code scanning.
    • Triggers: push to main and a weekly schedule (43 4 * * 1, Mondays 04:43 UTC). These are the triggers the scorecard-action README lists as supported. Scorecard publishes only from the default branch.
    • Pinning: every action is pinned to a full commit SHA with a version comment, like pr-title.yml, release-please.yml and sync-main-to-dev.yml. The SHAs match lilrae's and upstream's: checkout 3d3c42e5… (v7.0.1), scorecard-action 2d114668… (v2.4.4), upload-artifact 043fb46d… (v7.0.1), upload-sarif 2892aa5e… (v4.38.2).
    • Permissions: the workflow defaults to contents: read, as ci.yml, docs.yml, pr-title.yml and release-please.yml do (sync-main-to-dev.yml keeps its workflow-level write permissions; this PR does not change it). The job adds only security-events: write (code scanning upload) and id-token: write (OIDC proof of origin for publishing). There is no PAT, no secret and no run: step. The header comment records Scorecard's publisher restrictions (no top-level env/defaults, no workflow-level write, Ubuntu runner, approved actions only) so later edits do not break publishing.
  • uv.lock: carried from chore(deps): update locked cryptography, django, pyjwt and sqlparse for published advisories #74 (6ee2564), not edited by this PR's own commit. It moves cryptography, django, pyjwt and sqlparse to releases that fix the advisories audit reports on dev; chore(deps): update locked cryptography, django, pyjwt and sqlparse for published advisories #74 has the details.
  • README.md: Scorecard badge under the title. It sits in the same place as rae's badge row and links to the Scorecard viewer. It uses the api.scorecard.dev host that the scorecard-action README and lilrae use. rae's legacy securityscorecards.dev host serves the same badge.

Test Plan

  • uvx --from actionlint-py actionlint (actionlint-py 1.7.12.25) over every workflow in the repo: exit 0, no findings.
  • uvx zizmor .github/workflows/scorecard.yml (zizmor 1.30.1, online audits with a GitHub token): "No findings to report". With --persona=pedantic one low finding is left, concurrency-limits. I did not add a concurrency group: none of the reference Scorecard workflows (rae, lilrae, upstream) uses one, and two overlapping runs on main only publish twice.
  • uvx pre-commit run --files .github/workflows/scorecard.yml README.md (pre-commit 4.6.2, this repo's .pre-commit-config.yaml): every commit-stage hook passed or was skipped for file type (whitespace, end-of-file, check-yaml, large files, merge conflict, private key, gitleaks). I did not run the pre-push pytest hook locally because no Python changed; CI test runs the suite.
  • Pin check: gh api repos/<action>/git/ref/tags/<tag> (dereferencing annotated tags) resolves each tag to the commit SHA pinned above.
  • Reference runs: upstream ossf/scorecard run 37802317844 (push to main, success, 2026-10-08) uses exactly these four SHAs. The OpenRAE scheduled runs on 2026-10-05, lilrae 37276564889 and rae 37296600139 (both success), used the same checkout, scorecard-action and upload-artifact SHAs. Their upload-sarif was one patch behind (v4.38.1, 1c5b6756…); lilrae and rae dev now pin v4.38.2.
  • Restack onto chore(deps): update locked cryptography, django, pyjwt and sqlparse for published advisories #74 (head 6ee2564): git rebase --onto origin/uv-lock-vulnerability-fixes origin/dev replayed the one commit, and git range-diff reports it unchanged (d8cfc1d = 744e90a). uv.lock is byte-identical to chore(deps): update locked cryptography, django, pyjwt and sqlparse for published advisories #74's. uv lock --check resolves 93 packages (exit 0).
  • Audit, as CI runs it: uv export --frozen --no-dev --all-extras --no-emit-project --no-hashes --format requirements-txt, then uvx pip-audit --strict --no-deps on the export printed "No known vulnerabilities found" (exit 0). Locally I added --disable-pip, because pip-audit's temporary virtual environment aborts in ensurepip on this machine; with --no-deps it audits the same pins. The same commands on dev's pyproject.toml and uv.lock print "Found 27 known vulnerabilities in 4 packages" (exit 1), CI's dev result.
  • After the restack: actionlint over every workflow (exit 0); uvx pre-commit run --files .github/workflows/scorecard.yml README.md uv.lock (exit 0); uvx ruff@0.15.21 check . ("All checks passed!") and format --check . ("47 files already formatted").
  • CI at 744e90a: audit passes ("No known vulnerabilities found") and test passes ("148 passed", TOTAL 1958 statements, 96%), both in run 38196288297. PR title guard (38196288247), CodeQL actions, javascript-typescript and python (38196285443) and GitGuardian also pass. CodeQL (actions) scanned the new workflow.
  • sonar is the only red check, and no code change can fix it: it stops before analysis because SonarCloud rejects the repository SONAR_TOKEN ("HTTP 403 Forbidden" in run 38196288297, as in the earlier run 37899310319). Only the maintainer can replace the secret; tracked in ci: SonarCloud rejects the repository SONAR_TOKEN #75. This PR changes no file under Sonar's sonar.sources=src.
  • Not verifiable before merge: the workflow runs only on main (push and schedule), so its first run is the push that promotes dev to main. After that push, the "OpenSSF Scorecard" run on main should succeed and the badge should show a score. Until that run publishes, the badge renders "invalid repo path" (checked 2026-10-09). The same URL pattern (api.scorecard.dev/projects/github.com/OpenRAE/<repo>/badge) renders scores for rae (6.4) and lilrae (7). This badge check is Add OpenSSF Scorecard workflow and README badge #53's last acceptance item ("badge renders in the README"), and Add OpenSSF Scorecard workflow and README badge #53 tracks it: it can only be confirmed after the promotion run, before Add OpenSSF Scorecard workflow and README badge #53 is closed. main is the default branch, so Add OpenSSF Scorecard workflow and README badge #53 stays open when this PR merges into dev.

Ground Control Checks

  • No Ground Control MCP gate (gc_evaluate_quality_gates, gc_run_sweep) was run from this environment, and the repository has no make policy target. CI's audit, test and sonar jobs and the PR title guard run on this PR. No Python or test file changed.

Traceability

Checklist

Documentation

Updated: README.md (badge line). No other documentation surface is in scope.

…or published advisories

Upgrade only these four uv.lock entries, within the existing pyproject.toml
constraints:

- cryptography 49.0.0 -> 50.0.2 (PYSEC-2026-3552)
- django 5.2.16 -> 5.2.18 (PYSEC-2026-3717, PYSEC-2026-4035)
- pyjwt 2.13.0 -> 2.15.1 (PYSEC-2026-4140 to PYSEC-2026-4152, PYSEC-2026-4183)
- sqlparse 0.5.5 -> 0.6.0 (PYSEC-2026-3696 to PYSEC-2026-3699, PYSEC-2026-3923)

CI's audit job runs pip-audit --strict over the locked runtime dependencies
and fails on dev with 27 known vulnerabilities in these packages (22 distinct
advisories: pip-audit lists PYSEC-2026-3552 and PYSEC-2026-3696 to
PYSEC-2026-3699 twice); with this lock it reports none. No other package is
added, removed or moved.

Closes #76
Run ossf/scorecard-action on each push to main and weekly, publish the
result to api.scorecard.dev, keep the SARIF as a short-lived artifact, and
upload it to code scanning. Actions are pinned to the same commit SHAs as
the OpenRAE/lilrae and ossf/scorecard workflows. The workflow defaults to
read-only; only the job gains security-events and id-token write.

Show the Scorecard badge under the README title, as OpenRAE/rae does.

Closes #53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant