Case Study // Tool
Audit-Scanner: Nachtlauf über meine eigenen Repos
Jede Nacht prüft ein Container meine eigenen Repositories auf CVEs, floatende Pins und veraltete Images – und legt die Fix-PRs gleich selbst an.
- TypeScript
- Node 26
- Trivy
- GitHub REST
- Docker

01Ausgangslage
Die Update-Pflicht stand in meinem eigenen Regelwerk, die Umsetzung blieb aber Handarbeit: jeden Container-Tag prüfen, jeden Range im Blick behalten, jede Advisory einzeln nachlesen. Das hielt nur so lange, wie ich daran dachte.
Also ein Batch-Job, der genau das übernimmt – nachts, unbeaufsichtigt, mit einer Telegram-Nachricht am Morgen.
02Wie es gebaut ist
Das Inventar kommt von der GitHub-API: alle Repositories des Accounts, öffentliche und private, Forks ausgenommen, archivierte gar nicht erst mitgezählt. Jedes Repo wird als Tarball gezogen, damit der Scan keinen Cache und keine Arbeitskopie braucht.
Trivy läuft in zwei Durchgängen: `trivy fs` gegen die Abhängigkeiten, `trivy image` gegen jeden FROM-Ref in Dockerfiles und jede image-Referenz im Compose. Die gefundenen CVEs werden gegen die GitHub-Advisories gematcht, inklusive semver-Vergleich und first_patched_version – ohne eine gepatchte Version gibt es keinen Fix-PR, sondern nur einen Hinweis.
Renovate läuft zusätzlich als reine Analyse (--dry-run=lookup), weil es floatende oder veraltete Tags anders bewertet als ein CVE-Scan. Gefundene Ranges werden auf exakte Versionen gezogen, Image-Tags auf Version plus Digest gepinnt.
Fix-PRs entstehen über die REST-API ohne git clone, auf getrennten Branches je Art, ein persistenter State verhindert Duplikate über Tage. Geprüft wird vor dem Merge genauso wie bei jeder anderen Änderung: Lint, Typecheck, Tests, Build.
03Was ich gelernt habe
Ein Fehler vor dem Report ist ein stiller Ausfall: eine root-eigene digests.json ließ den Lauf zwei Nächte mit EACCES abbrechen, ohne Report und ohne Telegram-Nachricht – von außen sah das aus wie 'nichts gefunden'. Seitdem gibt es einen täglichen Beleg und einen Exit-Trap, der den Abbruch meldet.