Skip to content

How supply-chain security works

We scan our own repositories rather than relying on GitHub’s built-in scanning, because GitHub Advanced Security is not licensed for this organisation. Code scanning, secret scanning and push protection are therefore unavailable to us as platform features. What we run instead is assembled from four repositories, and this page is the map of it.

Layer What it catches How it runs
Dependabot Known vulnerabilities in declared dependencies Alerts, security updates and the dependency graph are enabled per repository by github-iac. Version updates are separate: they need a .github/dependabot.yml in the repo, and each repo opts in
Aikido safe-chain Malicious packages at install time, before their scripts run Installed on CI runners by setup-ci-runner
OpenSSF Scorecard Weak repository practices, including unpinned actions, missing branch protection and stale dependencies The shared static-analysis-scorecard workflow
CodeQL Vulnerabilities in our own code The shared static-analysis-codeql workflow

Dependabot and safe-chain guard the dependencies coming in. Scorecard and CodeQL measure what we have. The two pairs answer different questions and neither substitutes for the other.

Scorecard and CodeQL do not report in place. Each workflow emits an artifact — JSON for Scorecard, SARIF (Static Analysis Results Interchange Format) for CodeQL — and a weekly aggregator in security-dashboard collects them across repositories and renders one static page at scorecard.ajomark.dev (Cloudflare Access, then Auth0).

Not all four layers reach this site’s own repository, and the gap is deliberate rather than an oversight:

  • Scorecard — not yet onboarded. Onboarding is a rollout from security-dashboard (a config/repos.json entry, then scripts/rollout-scorecard.sh), never a hand-edited workflow here — see add a repository to the dashboard.
  • CodeQL — not run. GitHub Advanced Security is unlicensed, so results cannot be uploaded for a non-public repository, and there is little to scan: three .astro components, one Python script, and prose.
  • Aikido safe-chain — not run. This repo’s CI uses plain ubuntu-latest rather than a setup-ci-runner runner, so the layer does not apply to its npm ci.
  • Dependabot — alerts and security updates apply as everywhere. Version updates are configured here for github-actions only, to keep the SHA pins current.

The two are kept in separate S3 namespaces on purpose: a Scorecard score is a number that trends over time, while CodeQL findings are a list diffed snapshot to snapshot. Storing them together would make one of the two views wrong (ADR-0008).

Every run also writes a history snapshot, so a regression can be traced to the week it appeared rather than merely observed (ADR-0006).

The dashboard signals do not block a pull request

Section titled “The dashboard signals do not block a pull request”

Deliberately. A Scorecard regression is informational, not a gate (ADR-0007).

The reasoning is that a Scorecard score moves for reasons unrelated to the pull request in front of you — a dependency ages, a check is added upstream — and a gate that fires on things the author cannot fix gets routed around within a week. An informational signal that people actually read beats a blocking one they learn to bypass.

That is a decision about this signal, not a general position. Required checks in your own repository’s CI still block, and org policy is explicit that they must be green before merge.

Two conventions that are really supply-chain controls

Section titled “Two conventions that are really supply-chain controls”

Both look like CI style rules and are not:

  • Third-party actions pinned by SHA. A tag can be moved; a SHA cannot. Nothing enforces this: the org-level Actions allowlist in github-iac controls which actions may run, not how a workflow references them. Scorecard scores it, and review is what catches it, so check the pin when you review a workflow.
  • Internal reusable workflows referenced at @main. The trust boundary is the organisation, so an internal reference is allowed to move.

Why those two are consistent rather than contradictory, and what follows from it, is on how CI runs across our repos — that page owns the reasoning.

The package-manager choice in m1-web belongs in the same category: the migration from npm to pnpm was made for supply-chain hardening rather than speed (m1-web ADR-0010).

Adding Semgrep CE and Trivy alongside CodeQL is a Proposed ADR, not current behaviour (ADR-0009). If someone tells you Trivy runs on our repositories, check that ADR’s status before believing it.

Private vulnerability reporting is a GitHub Advanced Security feature, so we do not have it. Use the channels in .github/SECURITY.md instead, and never a public issue.