Skip to content

ossf-scorecard reads api.securityscorecards.dev, which has diverged from api.scorecard.dev #12117

Description

@rhalbersma

Summary

The ossf-scorecard service queries https://api.securityscorecards.dev. OpenSSF's own README now uses https://api.scorecard.dev — for its own badge, for the REST API, and in the copy-paste badge template it gives users:

[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/{owner}/{repo}/badge)](https://scorecard.dev/viewer/?uri=github.com/{owner}/{repo})

That /badge path is a redirect to img.shields.io/ossf-scorecard/..., so the snippet OpenSSF hands out renders through the Shields route — which then queries the other host. Those two hosts no longer serve the same data.

The badge shows stale scores

The legacy host lags by about a week, and the drift is visible in the score itself, not just the timestamp. As of 2026-08-28:

repo api.scorecard.dev api.securityscorecards.dev
nodejs/node 5.9 — 2026-08-28 6.3 — 2026-08-21
kubernetes/kubernetes 7.2 — 2026-08-24 7.5 — 2026-08-17
python/cpython 7.9 — 2026-08-24 7.8 — 2026-08-17
badges/shields 6.4 — 2026-08-24 6.4 — 2026-08-17
ossf/scorecard 9 — 2026-08-22 9 — 2026-08-15

This is the quiet failure mode: the badge renders fine and shows a number that is days out of date, and for nodejs/node it is currently overstating the score by 0.4.

Repos published only to the new host render as an error badge

The hosts also have different indexes, so a repository whose results were first published after the split answers 200 on api.scorecard.dev and 404 on api.securityscorecards.dev. The httpErrors: { 404: 'invalid repo path' } mapping in the service then paints the badge in the error red — which is hard to distinguish at a glance from a genuinely terrible score, given the colour scale in that same file only reaches red below 2.

I hit this on a repository of my own and left it out of the table above since it isn't a project you'd recognise; happy to supply the name if a concrete case is useful.

Suggested fix

One URL in services/ossf-scorecard/ossf-scorecard.service.js:

   async fetch({ host, orgName, repoName }) {
     return this._requestJson({
       schema,
-      url: `https://api.securityscorecards.dev/projects/${host}/${orgName}/${repoName}`,
+      url: `https://api.scorecard.dev/projects/${host}/${orgName}/${repoName}`,
       httpErrors: {
         404: 'invalid repo path',
       },
     })
   }

The response shape is unchanged — both hosts return the same JSON, and the existing Joi schema (score) validates against either — so this should be a drop-in swap. Happy to open a PR if that's welcome.

One caveat on the deprecation status

I could not find an explicit deprecation notice for api.securityscorecards.dev, and ossf/scorecard-webapp's own README still uses it, so the migration may not be finished upstream. But regardless of which host is formally "current", the data above shows they have diverged, and the badge template OpenSSF publishes to users routes into this service expecting the new one.

Reproduction

$ curl -sL https://img.shields.io/ossf-scorecard/github.com/nodejs/node | grep -o 'aria-label="[^"]*"'
aria-label="score: 6.3"

Expected 5.9, which is what the new host reports and what the generic JSON badge shows when pointed at it directly:

$ curl -sL 'https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.scorecard.dev%2Fprojects%2Fgithub.com%2Fnodejs%2Fnode&query=%24.score&label=openssf%20scorecard' | grep -o 'aria-label="[^"]*"'
aria-label="openssf scorecard: 5.9"

Scores move, so the exact numbers will drift after filing; the divergence between the two hosts is the reproducible part.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions