ci(scans): voorgebakken scan-image i.p.v. per-PR scanner-downloads #1150

Merged
brenno merged 2 commits from ci/scans-prebaked-image into main 2026-08-03 11:18:55 +00:00
Owner

Wat

De scans-poort (secrets + SAST, per PR) haalde bij élke pull request drie beveiligingsscanners van het net — gitleaks + trufflehog als release-tarball, semgrep uit een verse pip-venv. Gemeten ~3 minuten per run waarvan amper 19 seconden scannen (#778), plus drie kansen per run op een netstoring die niets met de repo te maken heeft. Precies het per-run-installatiewerk dat de Linux-poorten sinds #1141 al voorbakken; scans.yml bleef er bewust buiten ("een aparte, iets delicatere stap voor een volgende keer"). Dit is die volgende keer.

De scanners zitten nu voorgebakken in een eigen image ocideck-scans:<pins>; de per-run-installatie én het net vervallen.

Waarom dit mag terwijl scanners cachen bewust werd geweigerd

CHECKS.md noteerde het bezwaar tegen een actions/cache op de scanner-binaries: een restore verving een sha256-geverifieerde download van een beveiligingsscanner door een artefact van een eerdere run, zónder de hercontrole die check-toolchain de Flutter-toolchain wél geeft — "groen" zou stil iets anders kunnen gaan betekenen. Een image neemt dat op twee punten weg, en pas dán is het verdedigbaar:

  1. De sha256-verificatie is niet weg, maar naar bouwtijd verplaatst (in scans.Dockerfile).
  2. scans.yml krijgt de ontbrekende hercontrole terug: een eerste poortstap "Ingebakken scanner-versies == de pins" toetst fail-closed dat het image draagt wat de pins zeggen — het equivalent van check-toolchain. Een achtergebleven of verkeerd getagd image valt daar om vóór er iets gescand is.

Vorm

  • Apart image (ocideck-scans), niet toegevoegd aan ocideck-ci — zo herbouwt het niet bij elke Flutter-bump en sleept het geen Flutter/cmake/gtk mee. Tag ís de drie pins: gl<gitleaks>-th<trufflehog>-sg<semgrep>.
  • De drie versies komen uit de enige bron .github/pinned-ci-versions.json (ci-image-scans.yml leest ze met jq), niet als tweede kopie in de workflow. De *_VERSION-pins blijven in scans.yml als verwachte waarde, dus pinned_versions_manifest_test klopt onveranderd — het manifest hoeft niet te wijzigen.
  • Twee losse commits (produceer-image / switch-over), dezelfde fase-1/fase-2-discipline als het Flutter-image: het toevoegen van het image kan de CI niet op zichzelf rood maken.

Eenmalige uitrolstap (alleen jij kunt dit)

Na merge vuurt ci-image-scans.yml en publiceert het image mits CI_IMAGE_TOKEN/CI_IMAGE_USER zijn ingericht (zelfde als het Flutter-image). Zet het gepubliceerde package daarna op publiek, anders kan scans.yml het niet anoniem pullen. Tot dat moment kan een scans-run rood staan — dat blokkeert niet (de vereiste check is static-gate), maar zet het package snel publiek. Wil je het waterdicht: merge eerst commit 1 (produceer-image), controleer de publicatie + zet publiek, land dan commit 2.

Getoetst

  • Gate-versiecheck lokaal tegen de echte binaries — pass én de fail-closed mismatch-tak.
  • Scanner-uitvoerformaten matchen de grep -F: gitleaks version8.30.1, trufflehog --versiontrufflehog 3.95.9 (stderr), semgrep --version1.171.0.
  • make ci-image-scans-publish (dry-run) levert het tag gl8.30.1-th3.95.9-sg1.171.0, gelijk aan de literal in scans.yml.
  • pinned_versions_manifest_test-invarianten met de hand nagelopen: geen *_VERSION:-regel lekte de nieuwe workflow in; de drie pins staan in scans.yml; manifest byte-onveranderd.

De download/sha256/venv-stappen in de Dockerfile zijn een getrouwe port van de commando's die vandaag groen in scans.yml draaien; een echte amd64-buildx-build onder QEMU is niet gedraaid.

## Wat De `scans`-poort (secrets + SAST, per PR) haalde bij **élke** pull request drie beveiligingsscanners van het net — gitleaks + trufflehog als release-tarball, semgrep uit een verse pip-venv. Gemeten ~3 minuten per run waarvan amper 19 seconden scannen (#778), plus drie kansen per run op een netstoring die niets met de repo te maken heeft. Precies het per-run-installatiewerk dat de Linux-poorten sinds #1141 al voorbakken; `scans.yml` bleef er bewust buiten ("een aparte, iets delicatere stap voor een volgende keer"). Dit is die volgende keer. De scanners zitten nu voorgebakken in een eigen image `ocideck-scans:<pins>`; de per-run-installatie én het net vervallen. ## Waarom dit mag terwijl scanners *cachen* bewust werd geweigerd CHECKS.md noteerde het bezwaar tegen een `actions/cache` op de scanner-binaries: een restore verving een sha256-geverifieerde download van een **beveiligings**scanner door een artefact van een eerdere run, zónder de hercontrole die `check-toolchain` de Flutter-toolchain wél geeft — "groen" zou stil iets anders kunnen gaan betekenen. Een image neemt dat op twee punten weg, en pas dán is het verdedigbaar: 1. De sha256-verificatie is niet weg, maar naar **bouwtijd** verplaatst (in `scans.Dockerfile`). 2. `scans.yml` krijgt de ontbrekende hercontrole terug: een eerste poortstap **"Ingebakken scanner-versies == de pins"** toetst fail-closed dat het image draagt wat de pins zeggen — het equivalent van `check-toolchain`. Een achtergebleven of verkeerd getagd image valt daar om vóór er iets gescand is. ## Vorm - **Apart** image (`ocideck-scans`), niet toegevoegd aan `ocideck-ci` — zo herbouwt het niet bij elke Flutter-bump en sleept het geen Flutter/cmake/gtk mee. Tag ís de drie pins: `gl<gitleaks>-th<trufflehog>-sg<semgrep>`. - De drie versies komen uit de **enige** bron `.github/pinned-ci-versions.json` (`ci-image-scans.yml` leest ze met `jq`), niet als tweede kopie in de workflow. De `*_VERSION`-pins blijven in `scans.yml` als verwachte waarde, dus `pinned_versions_manifest_test` klopt onveranderd — **het manifest hoeft niet te wijzigen**. - Twee losse commits (produceer-image / switch-over), dezelfde fase-1/fase-2-discipline als het Flutter-image: het toevoegen van het image kan de CI niet op zichzelf rood maken. ## Eenmalige uitrolstap (alleen jij kunt dit) Na merge vuurt `ci-image-scans.yml` en publiceert het image **mits** `CI_IMAGE_TOKEN`/`CI_IMAGE_USER` zijn ingericht (zelfde als het Flutter-image). Zet het gepubliceerde package daarna op **publiek**, anders kan `scans.yml` het niet anoniem pullen. Tot dat moment kan een `scans`-run rood staan — dat blokkeert niet (de vereiste check is `static-gate`), maar zet het package snel publiek. Wil je het waterdicht: merge eerst commit 1 (produceer-image), controleer de publicatie + zet publiek, land dan commit 2. ## Getoetst - Gate-versiecheck lokaal tegen de echte binaries — pass én de fail-closed mismatch-tak. - Scanner-uitvoerformaten matchen de `grep -F`: `gitleaks version`→`8.30.1`, `trufflehog --version`→`trufflehog 3.95.9` (stderr), `semgrep --version`→`1.171.0`. - `make ci-image-scans-publish` (dry-run) levert het tag `gl8.30.1-th3.95.9-sg1.171.0`, gelijk aan de literal in `scans.yml`. - `pinned_versions_manifest_test`-invarianten met de hand nagelopen: geen `*_VERSION:`-regel lekte de nieuwe workflow in; de drie pins staan in `scans.yml`; manifest byte-onveranderd. De download/sha256/venv-stappen in de Dockerfile zijn een getrouwe port van de commando's die vandaag groen in `scans.yml` draaien; een echte amd64-buildx-build onder QEMU is niet gedraaid.
De scans-poort haalde bij élke PR drie beveiligingsscanners van het net
(gitleaks + trufflehog als release-tarball, semgrep uit een pip-venv) —
~3 minuten per run waarvan amper 19 seconden scannen, plus drie kansen op
een netstoring die niets met de repo te maken heeft. Hetzelfde
per-run-installatiewerk dat de Linux-poorten sinds #1141 voorbakken.

Dit is de "produceer het image"-helft, gescheiden van de switch-over zodat
het toevoegen van het image de CI niet op zichzelf rood kan maken (zelfde
discipline als het Flutter-image, fase 1/fase 2):

- .forgejo/ci-image/scans.Dockerfile — bakt de drie scanners sha256-
  geverifieerd op bouwtijd in (exact de controle die de workflow bij het
  downloaden deed, nu verplaatst). Apart image i.p.v. in ocideck-ci, zodat
  het niet herbouwt bij elke Flutter-bump en geen Flutter/cmake/gtk meesleept.
- .forgejo/workflows/ci-image-scans.yml — bouwt+pusht naar de eigen registry,
  getagd op de drie pins (gl<>-th<>-sg<>). Leest de versies uit de ENIGE bron
  .github/pinned-ci-versions.json (jq), niet als tweede kopie in de workflow,
  zodat pinned_versions_manifest_test niet klaagt. Fail-closed publish-guard:
  zonder registry-token slaat de run groen over.
- Makefile: ci-image-scans-publish als handmatige buildx-route.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci(scans): draai de scans-poort op het voorgebakken image
Some checks failed
scans / scans (pull_request) Failing after 2s
static-gate / static-gate (pull_request) Successful in 4m8s
631f5820e3
De switch-over-helft: scans.yml draait niet meer op kaal ubuntu:24.04 met
per-run-installatie, maar op ocideck-scans:<pins> met de scanners ingebakken.
De per-run-installatie én de drie downloads vervallen.

Waarom dit mag terwijl scanners CACHEN bewust werd geweigerd (CHECKS.md): een
cache-restore verving een sha256-geverifieerde download van een beveiligings-
scanner door een eerder artefact, zónder de hercontrole die check-toolchain de
Flutter-toolchain wél geeft. Een image neemt dat op twee punten weg:
(1) de sha256-verificatie is naar bouwtijd verplaatst, en (2) een nieuwe
poortstap "Ingebakken scanner-versies == de pins" toetst fail-closed dat het
image draagt wat de pins zeggen — het equivalent van check-toolchain. Een
achtergebleven of verkeerd getagd image valt daar om vóór er iets gescand is.

De drie *_VERSION-pins blijven in scans.yml (nu als verwachte waarde i.p.v.
download-parameter), zodat pinned_versions_manifest_test onveranderd klopt en
het manifest niet hoeft te wijzigen.

docs/CHECKS.md: het scans-hoofdstuk herschreven (het oude "cachen mag niet"-
bezwaar is nu beantwoord i.p.v. ontweken) + een hoofdstuk voor ci-image-scans.yml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 3b1d77ae82 into main 2026-08-03 11:18:55 +00:00
brenno deleted branch ci/scans-prebaked-image 2026-08-03 11:18:56 +00:00
Sign in to join this conversation.
No description provided.