Il Mito dello "Shift Left" — e Perché il Tuo Pipeline è Ancora Rotto
Lo "shift left" come venduto dai vendor di sicurezza significa: sposta la sicurezza prima nel ciclo di sviluppo. Come implementato dalla maggior parte delle organizzazioni significa: aggiungi uno scanner al pipeline di CI, ignora i risultati, dichiara vittoria.
Il problema non è lo strumento. Il problema è il modello mentale. Aggiungere Semgrep a un GitHub Actions workflow non ti rende una DevSecOps organization — te ne rende una solo se hai un processo per triaggiare i findings, una policy di severità che fa senso, e developer che capiscono perché la security automation esiste.
La buona notizia: l'automazione della sicurezza nel CI/CD cambia l'economia della sicurezza in modo radicale. Quello che richiede 3 giorni di penetration test manuale può girare in 90 secondi su ogni commit. Ogni PR può ricevere feedback di sicurezza contestuale prima di toccare main. Le vulnerability class sistemate una volta possono essere prevenute per sempre con una custom rule. Per chi deve dimostrare conformità al CRA, un pipeline CI/CD documentato e auditabile è anche la prova operativa dell'Art. 13(2) — security by design — che non puoi ottenere in altro modo.
"Security automation in CI/CD is not about running tools. It's about creating a feedback loop fast enough that security becomes part of the development habit, not a phase that happens after."
Modello Mentale: Dove Va Ogni Scanner nel Pipeline
Prima di installare qualsiasi strumento, devi rispondere a una domanda: dove nel pipeline questa analisi produce il feedback più utile con il minor overhead? Ogni tipo di scan ha requisiti diversi in termini di ambiente, tempo di esecuzione e tipo di findings prodotti.
La regola generale: scans veloci (secret detection, SAST fast rules, linting) appartengono al pre-commit e al PR gate. Scans che richiedono un'applicazione in esecuzione (DAST) appartengono all'ambiente di staging. Scans pesanti (CodeQL, full dependency audit) possono girare su schedule notturni e non bloccare ogni PR.
Secret Detection: L'Unico Caso di Zero Tolerance
I segreti nei repository git sono l'unica categoria dove la tolleranza zero è giustificata. A differenza di una vulnerabilità nel codice che richiede exploit, una API key committata è già un incident. Il motivo è banale: git non dimentica. Puoi rimuovere il file nel commit successivo, ma la chiave è nella history — e se il repository è mai stato clonato, forked, o crawlato da GitHub, quella chiave è fuori. Per sempre.
Gitleaks
Secrets · Git history · Pre-commit · CIIl migliore per velocità e configurabilità. Supporta scansione della history completa (--log-opts="--all") e della staging area (--staged) per i pre-commit hooks. Configurabile con .gitleaks.toml per custom patterns e allowlist. Output in JSON, SARIF, CSV. Si integra nativamente con GitHub Actions come action dedicata.
TruffleHog
Secrets · High signal · Verified detectionSi distingue per la verified detection: per alcune categorie di segreti (AWS, GitHub tokens, Stripe, ecc.) verifica attivamente se il segreto è ancora valido prima di riportarlo. Riduce drasticamente il rumore. Utile in combinazione con Gitleaks: Gitleaks per la velocità nel pre-commit, TruffleHog per la verifica nei pipeline CI. Supporta scan di repository git, filesystem, S3, Docker images e GitHub organization-wide.
Il problema della history git: Se un segreto è stato committato e poi rimosso, il segreto è ancora nella history. L'unica risoluzione è git filter-repo per riscrivere la history (distruttivo, richiede coordinamento con tutti i contributor) e, soprattutto, la rotation immediata del segreto compromesso. La rotation non è opzionale — è l'unica azione di remediation che conta.
SAST: Static Analysis Fatta Bene
Il SAST analizza il codice sorgente senza eseguirlo, cercando pattern di vulnerabilità noti. Il problema storico era il rapporto segnale/rumore: troppi falsi positivi, developer che imparano a ignorare i report, strumento disabilitato silenziosamente entro un mese dall'installazione.
La chiave è la calibrazione progressiva: non partire con zero tolerance su tutti i severity level il primo giorno. Parti con solo CRITICAL/HIGH, stabilisci una baseline, risolvi quello che c'è, poi espandi gradualmente. Questo approccio a ratchet è l'unico che sopravvive all'impatto con una codebase reale.
Semgrep
Multi-language · Fast · CustomizableIl backbone del SAST moderno. Gira in meno di 60 secondi sulla maggioranza delle codebase, supporta 30+ linguaggi, e l'output SARIF si carica direttamente nel GitHub Security tab. Il valore reale sta nella capacità di scrivere custom rules: se hai avuto una vulnerability class una volta, scrivi una regola e quella classe non entrerà mai più in produzione. Per la CRA, le custom rules Semgrep sono tracciabili come requisiti di sicurezza verificabili — un artefatto di compliance concreto.
CodeQL
Deep semantic analysis · GitHub-native · Java, C/C++, Python, JSAnalisi semantica profonda del flusso di dati: traccia come i dati si muovono dal punto di input fino al sink. Eccelle nel trovare vulnerability class complesse — taint analysis, path-sensitive issues, inter-procedural bugs. Prezzo: build time significativo. Appropriato per scheduled scans notturni o PR su branch di release, non per ogni commit. Nativo su GitHub Actions tramite github/codeql-action.
Tool specializzati per linguaggio
Bandit · Gosec · SpotBugs+FindSecBugs · BrakemanBandit per Python: analizza AST, trova hardcoded passwords, uso di eval(), MD5/SHA1. Gosec per Go: G-rules specifiche per il runtime Go, TLS misconfiguration, SQL injection via fmt.Sprintf. SpotBugs + FindSecBugs per Java/Kotlin: 80+ security bug patterns inclusi XXE e insecure deserialization. Brakeman per Ruby on Rails: analisi con comprensione profonda del framework, trova mass assignment vulnerabilities e SQL injection specifici di Rails.
DAST: Il Problema Difficile
Il DAST è la categoria che la maggior parte dei team implementa male nel CI/CD perché richiede un'applicazione in esecuzione. Il pattern corretto: spin up in Docker Compose (app + database + dipendenze), health check, scan DAST, tear down. L'intero ciclo dovrebbe stare in 5-10 minuti. Se ci vuole di più, il DAST appartiene a un job separato su schedule.
Baseline vs. Active Scan: OWASP ZAP ha due modalità. La baseline scan esegue solo check passivi — analizza le risposte HTTP senza inviare payload di attacco. È CI-safe, completamente sicura su staging condiviso, e prende 2-5 minuti. L'active scan genera traffico di attacco reale — NON deve essere eseguita su database reali o ambienti condivisi. Usare active scan in production è auto-DoS.
OWASP ZAP
DAST · Web Application · Passive + Active · GitHub ActionLo standard de facto per il DAST open source in CI/CD. La GitHub Action zaproxy/action-baseline esegue la baseline scan in modo hands-off: spin-up headless, scan, report HTML/JSON/SARIF, tear-down. Configurabile con file di regole YAML per escludere path noti. Supporta script di login per applicazioni con autenticazione. Il report HTML è leggibile per i developer.
Nuclei
DAST · Template-based · High signal · CI-friendlyTemplate-based scanner con oltre 9.000 template community-maintained. Si distingue per la qualità del segnale: i template sono specifici per CVE, misconfiguration, exposed credentials e default logins — raramente producono falsi positivi. Ideale per CI perché puoi selezionare esattamente quali categorie girare: -t exposures/ -t misconfiguration/ -t default-logins/. Sempre impostare -rate-limit 50 per evitare self-DoS.
Wapiti
DAST · Web · Python · LightweightAlternativa più leggera a ZAP per applicazioni con surface ridotta. Scritto in Python, facile da estendere con moduli custom. Testa le principali categorie OWASP Top 10: SQL injection, XSS, File inclusion, XXE, SSRF, Open redirect, CSRF. Utile quando ZAP è sovradimensionato o quando serve integrazione programmatica in pipeline Python-based.
SCA: Dove Vivono le Tue CVE Reali
Se dovessi indicare la singola categoria di scan con il più alto ROI, sarebbe la Software Composition Analysis. Più dell'80% delle CVE che trovo durante security review provengono da dipendenze di terze parti. Log4Shell, Spring4Shell, innumerevoli CVE critical di componenti npm: vulnerability esistite nelle codebase per mesi senza che nessuno lo sapesse. L'SCA risolve questo strutturalmente — ed è anche il prerequisito tecnico per soddisfare Annex I, Part I, §2 del CRA in modo continuativo.
Trivy
Multi-purpose · OS + App + Container + IaC + SBOMIl coltellino svizzero della security automation. Un singolo strumento che copre: dipendenze applicative (npm, pip, Maven, Gradle, Go modules, Cargo, Composer, NuGet), pacchetti OS nell'immagine container, misconfiguration IaC, segreti, e generazione SBOM in formato CycloneDX o SPDX-JSON. La generazione SBOM integrata nel build pipeline è la delivery mechanism per il requisito CRA Art. 13(8). Il flag --ignore-unfixed è critico: non fallire il build su CVE senza fix disponibile.
Grype + Syft
SCA + SBOM · Composable · AnchoreSyft genera SBOM da filesystem, immagini container o directory — output in SPDX-JSON, CycloneDX, o formato Syft nativo. Grype prende l'SBOM e lo confronta con Grype DB per trovare CVE. La separazione tra generazione SBOM e vulnerability matching è utile per la CRA: puoi condividere l'SBOM con clienti e autorità di market surveillance come documento di trasparenza autonomo.
OWASP Dependency-Check
SCA · Java/Maven · Deep analysis · NVDIl veterano del SCA, particolarmente forte su ecosistemi Java e .NET. Confronta le dipendenze contro il NVD e il database OSS Index. Più lento di Trivy (scarica l'intero NVD al primo run — 10-15 minuti), ma coverage eccellente per componenti enterprise Java. Appropriato per scans schedulati settimanali. Supporta Maven, Gradle, MSBuild, npm, Python, Ruby.
Container e IaC Security
Container image scanning con Trivy (trivy image <name>) va eseguito sul layer finale dell'immagine built nel pipeline CI. Le vulnerability possono essere introdotte nei layer superiori. Il pattern corretto: docker build → trivy image → push al registry solo se clean.
Hadolint per i Dockerfile verifica best practice: no apt-get update senza apt-get install nella stessa RUN, no tag latest per le immagini base, no processi running come root. Integrazione in 3 righe di GitHub Actions.
Checkov o trivy config . per l'IaC (Terraform, Kubernetes, Helm, CloudFormation): security groups troppo permissivi, S3 bucket pubblici, volumi non cifrati. Per la CRA, le misconfiguration IaC che espongono la superficie di attacco dell'infrastruttura sono direttamente rilevanti per i requisiti di protezione delle interfacce (Annex I, §3).
Integrare Tutto: Pattern GitHub Actions
Pattern pratici e production-ready. Ogni job produce output SARIF caricato nel GitHub Security tab — il tuo unified security dashboard senza strumenti aggiuntivi. Per la CRA, l'audit trail dei run di questi workflow è anche evidenza documentale dei processi di vulnerability testing.
Secrets Detection con Gitleaks
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history required
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}SAST con Semgrep
semgrep:
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- run: |
semgrep scan \
--config=auto \
--sarif \
--output=semgrep.sarif \
--severity=ERROR \
--error
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep.sarifSCA + Container + SBOM con Trivy
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy — filesystem scan (SCA)
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy-fs.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: 1
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Trivy — image scan
uses: aquasecurity/trivy-action@master
with:
scan-type: image
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-image.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: 1
# CRA Art. 13(8) — SBOM generation
- name: Trivy — generate SBOM (CycloneDX)
uses: aquasecurity/trivy-action@master
with:
scan-type: image
image-ref: app:${{ github.sha }}
format: cyclonedx
output: sbom.cdx.json
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-fs.sarif
- uses: actions/upload-artifact@v4
with:
name: sbom-${{ github.sha }}
path: sbom.cdx.jsonDAST con OWASP ZAP (ambiente effimero)
zap-baseline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start application stack
run: docker compose -f docker-compose.test.yml up -d
- name: Wait for health
run: |
timeout 60 sh -c \
'until curl -sf http://localhost:8080/health; do sleep 2; done'
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: http://localhost:8080
fail_action: false
artifact_name: zap-report
rules_file_name: .zap/rules.tsv
- name: Tear down
if: always()
run: docker compose -f docker-compose.test.yml down -vDAST con Nuclei (staging dedicato)
nuclei:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: projectdiscovery/nuclei-action@main
with:
target: ${{ vars.STAGING_URL }}
flags: >-
-t exposures
-t misconfiguration
-t default-logins
-severity medium,high,critical
-rate-limit 50
-sarif-export nuclei.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: nuclei.sarifGestione delle Soglie e dei Falsi Positivi
Il formato SARIF è lo standard che unifica tutto. Caricato in GitHub tramite codeql-action/upload-sarif, appare nel tab "Security → Code scanning alerts" — un dashboard unificato senza strumenti aggiuntivi. Per la CRA, questo dashboard è anche l'audit trail delle vulnerabilità rilevate e della loro remediation.
- Semgrep:
# nosemgrep: rule-idinline nel codice. Ogni soppressione deve avere un commento che spiega il perché — per la CRA, le soppressioni non giustificate sono un rischio di audit. - Trivy: file
.trivyignorenella root del repository con CVE ID e commento. - ZAP: file
.zap/rules.tsvper la baseline scan — mappa alert ID a IGNORE/WARN con nota. - Ratchet progressivo: inizia con CRITICAL, risolvi tutto, poi aggiungi HIGH, poi MEDIUM. Non partire con tutto abilitato su una codebase legacy.
Il suppression rate è una metrica di salute: se più del 20-30% dei finding viene soppresso piuttosto che risolto, il tuo strumento ha un problema di calibrazione o il tuo team ha un problema di ownership. Per la CRA, un suppression rate alto non giustificato è un flag di rischio in un audit.
Anti-Pattern che Uccidono i Programmi di Security Automation
Zero tolerance dal primo giorno su una codebase legacy
Abilitare tutti i tool al massimo severity level su una codebase senza security automation. Risultato garantito: 800 finding il primo giorno, developer che aggiungono --exit-code 0 silenziosamente, strumento disabilitato in due settimane. Il ratchet progressivo non è un compromesso — è l'unica strategia che funziona.
Tutti gli scanner su ogni commit
CodeQL, full DAST, OWASP Dependency-Check completo su ogni push, incluse feature branch con 3 commit "fix typo". Il pipeline diventa lento, i developer smettono di aspettare il verde, i tool diventano decorativi. Abbina lo scope dello scan all'evento del pipeline.
DAST contro produzione
Puntare uno scanner DAST — specialmente con active scan — contro l'ambiente di produzione. Ho visto questo causare self-DoS, dati di test nel database prod, e rate limiting che ha bloccato utenti reali. Il DAST appartiene ad ambienti effimeri isolati con dati sintetici.
SCA senza un processo di patching
Trovare CVE CRITICAL nelle dipendenze senza un SLA di remediation. Per la CRA, sapere di una vulnerabilità critica non fixata senza agire è una violazione dell'obbligo di gestione delle vulnerability (Annex I, Part II, §1). Il "non sapevo" non è più una difesa valida con l'SCA attivo.
Security automation senza developer buy-in
Aggiungere scanner al pipeline senza spiegare ai developer cosa trovano e come fixarli. Risultato: il developer vede "build failed: security scan" senza contesto, fa un rebase per ignorarlo, o apre una PR per rimuovere lo step. La security automation funziona quando i developer capiscono il valore.
Misurare Quello che Conta
Quanto tempo passa tra l'introduzione di una vulnerability e la sua rilevazione? L'obiettivo è che tenda a zero — rilevazione nel PR, non in produzione.
Con SAST + SCA + DAST funzionanti, il baseline atteso è oltre l'85% di vulnerability catturate prima di produzione.
Un suppression rate alto segnala problemi di calibrazione o ownership. Target: meno del 25% soppressi invece di fixati.
Security automation che copre il 60% dei repository lascia il 40% della superficie non monitorata. Per la CRA, la coverage deve essere totale sui prodotti in scope.
SecDevOps e il Cyber Resilience Act
Il CRA non nomina "CI/CD security automation" nei suoi articoli, ma i suoi obblighi tecnici sono praticamente impossibili da soddisfare senza di essa:
- Art. 13(2) — Security by design: un pipeline CI/CD con security gates documentati e audit trail è la prova operativa che la sicurezza è integrata nel processo di sviluppo. Non esiste altra modalità di dimostrazione scalabile per prodotti software in sviluppo continuo.
- Annex I, Part I, §2 — Nessuna vulnerability nota e sfruttabile: SAST + SCA nel CI è il meccanismo operativo per soddisfare questo requisito in modo continuativo — non solo al momento del rilascio iniziale, ma a ogni aggiornamento.
- Art. 13(8) — SBOM: la generazione automatica di SBOM (Trivy/Syft) integrata nel build pipeline è la delivery mechanism. Ogni release deve produrre un SBOM CycloneDX firmato come artefatto — non un documento creato manualmente prima dell'audit.
- Art. 14 — Notifica delle vulnerabilità (24h per actively exploited): il monitoraggio continuo tramite Trivy con DB refresh schedulato è ciò che permette di rilevare nuove CVE che impattano dipendenze già in produzione — prerequisito per rispettare le timeline di notifica ENISA.
Per i prodotti Class II e Critical, l'assessment da parte di un organismo notificato includerà quasi certamente la verifica di processi documentati di vulnerability testing automatizzato. Un pipeline CI/CD con security scans configurati, log degli esiti, e tracciabilità delle remediation non è solo best practice — è come si supera la due diligence di certificazione.
Costruire un Programma che Sopravvive nel Tempo
Ho visto programmi di security automation costruiti con attenzione decadere nel giro di sei mesi quando il team che li ha implementati si è spostato ad altro. La sostenibilità non è tecnologia — è ownership e cultura.
Tre cose imparate nel modo difficile: primo, ogni scanner deve avere un owner nominato — "il team di sicurezza" non è un owner. Secondo, le regole custom e i file di soppressione devono essere in version control con review process, non gestiti da una singola persona. Terzo, la velocità degli strumenti non è un'opzione: se un developer deve aspettare 20 minuti per il feedback, smette di aspettare. I tool lenti vengono bypassati.
Il team di CRA.tips ha progettato e implementato security automation nei pipeline CI/CD per organizzazioni che producono SaaS enterprise, prodotti IoT embedded e sistemi critici soggetti alla CRA. Se vuoi costruire un programma reale — non un set di tool installati e ignorati — inizia valutando il tuo gap di conformità.
The "Shift Left" Myth — and Why Your Pipeline Is Still Broken
"Shift left" as sold by security vendors means: move security earlier in the development lifecycle. As implemented by most organizations it means: add a scanner to the CI pipeline, ignore the results, declare victory.
The problem isn't the tool. The problem is the mental model. Adding Semgrep to a GitHub Actions workflow doesn't make you a DevSecOps organization — it does only if you have a process to triage findings, a severity policy that makes sense, and developers who understand why security automation exists.
The good news: security automation in CI/CD radically changes the economics of application security. What takes a penetration tester 3 days can run in 90 seconds on every commit. Vulnerability classes fixed once can be prevented forever with a custom rule. For CRA compliance, a documented, auditable CI/CD pipeline with security gates is also the operational proof of Art. 13(2) — security by design — that you cannot achieve any other way.
"Security automation in CI/CD is not about running tools. It's about creating a feedback loop fast enough that security becomes part of the development habit, not a phase that happens after."
Mental Model: Where Does Each Scanner Belong in the Pipeline?
Before installing any tool, answer one question: where in the pipeline does this analysis produce the most useful feedback with the least overhead?
Secret Detection: The One True Zero-Tolerance Case
Secrets in git repositories are the only category where zero tolerance is justified. Unlike a code vulnerability that requires exploitation, a committed API key is already an incident. Git doesn't forget — and if the repository was ever cloned, forked, or crawled, that key is out. Forever.
Gitleaks
Secrets · Git history · Pre-commit · CIBest in class for speed and configurability. Supports full history scanning (--log-opts="--all") and staged area scanning (--staged) for pre-commit hooks. Configurable with .gitleaks.toml for custom patterns and allowlists. Output in JSON, SARIF, CSV. Integrates natively with GitHub Actions.
TruffleHog
Secrets · High signal · Verified detectionDistinguishes itself with verified detection: for certain secret categories (AWS, GitHub tokens, Stripe) it actively verifies whether the secret is still valid before reporting. Dramatically reduces noise. Best used in combination with Gitleaks: Gitleaks for pre-commit speed, TruffleHog for CI verification. Supports git, filesystem, S3, Docker images, and GitHub organization-wide scanning.
The git history problem: If a secret was committed and later removed, it's still in history. The only resolution is git filter-repo to rewrite history (destructive, requires coordination) and, most importantly, immediate rotation of the compromised secret. Rotation is not optional — it is the only remediation action that matters.
SAST: Static Analysis Done Right
The key to SAST that survives contact with reality is progressive calibration: start with only CRITICAL/HIGH, establish a baseline, resolve what's there, then ratchet upward. Don't start with everything enabled on a legacy codebase on day one — it's a guaranteed path to tool disablement.
Semgrep
Multi-language · Fast · CustomizableThe backbone of modern SAST. Runs in under 60 seconds on most codebases, supports 30+ languages, SARIF output goes directly to the GitHub Security tab. The real value is in custom rules: if you've had a vulnerability class once, write a Semgrep rule and that class never enters production again. For CRA compliance, custom Semgrep rules are traceable as verifiable security requirements — concrete compliance artifacts.
CodeQL
Deep semantic analysis · GitHub-native · Java, C/C++, Python, JSDeep data-flow semantic analysis: traces how data moves from input point to sink. Excels at complex vulnerability classes — taint analysis, path-sensitive issues, inter-procedural bugs. The price: significant build time. Appropriate for nightly scheduled scans or release branch PRs, not every commit. Native on GitHub Actions via github/codeql-action.
Language-specific tools
Bandit · Gosec · SpotBugs+FindSecBugs · BrakemanBandit for Python: AST analysis, finds hardcoded passwords, eval(), MD5/SHA1. Gosec for Go: runtime-specific rules, TLS misconfiguration, SQL injection via fmt.Sprintf. SpotBugs + FindSecBugs for Java/Kotlin: 80+ security bug patterns including XXE, insecure deserialization. Brakeman for Ruby on Rails: framework-aware analysis, finds mass assignment vulnerabilities and Rails-specific SQL injection.
DAST: The Hard One
DAST requires a running application. The correct CI pattern: Docker Compose spin-up (app + database + dependencies), health check, DAST scan, tear down. The entire cycle should fit in 5-10 minutes to be sustainable.
Baseline vs. Active Scan: ZAP's baseline scan runs only passive checks — no attack payloads, CI-safe, safe against shared staging environments, 2-5 minutes. The active scan generates real attack traffic — must NOT be run against real databases or shared environments. Active scan against production is self-DoS.
OWASP ZAP
DAST · Web Application · Passive + Active · GitHub ActionThe de facto standard for open source DAST in CI/CD. The zaproxy/action-baseline GitHub Action runs the baseline scan hands-off: headless spin-up, scan, HTML/JSON/SARIF report, tear-down. Configurable with rules files to exclude known-safe paths. Supports login scripts for authenticated applications. HTML reports are developer-readable.
Nuclei
DAST · Template-based · High signal · CI-friendlyTemplate-based scanner with over 9,000 community-maintained templates. Stands out for signal quality: templates are specific to CVEs, misconfigurations, exposed credentials, and default logins — rarely producing false positives. Select exactly what to run in CI: -t exposures/ -t misconfiguration/ -t default-logins/. Always set -rate-limit 50 to prevent self-DoS.
Wapiti
DAST · Web · Python · LightweightA lighter alternative to ZAP for applications with a reduced surface. Written in Python, easy to extend. Tests main OWASP Top 10 categories: SQL injection, XSS, File inclusion, XXE, SSRF, Open redirect, CSRF. Useful when ZAP is oversized or when programmatic Python-based integration is needed.
SCA: Where Your Real CVEs Live
More than 80% of CVEs I find during security reviews come from third-party dependencies, not custom code. Log4Shell, Spring4Shell, countless critical npm CVEs — these vulnerabilities existed in codebases for months or years. SCA solves this structurally, and it's also the technical prerequisite for satisfying CRA Annex I, Part I, §2 on a continuous basis.
Trivy
Multi-purpose · OS + App + Container + IaC + SBOMThe Swiss Army knife of security automation. Covers: application dependencies (npm, pip, Maven, Gradle, Go modules, Cargo, Composer, NuGet), OS packages, IaC misconfigurations, secrets, and SBOM generation in CycloneDX or SPDX-JSON. Automated SBOM generation in the build pipeline is the delivery mechanism for CRA Art. 13(8). Use --ignore-unfixed to avoid failing builds on CVEs with no available fix.
Grype + Syft
SCA + SBOM · Composable · AnchoreSyft generates SBOMs from filesystems, container images, or directories — output in SPDX-JSON, CycloneDX, or native Syft format. Grype matches the SBOM against Grype DB to find CVEs. The separation is valuable for CRA: you can share the SBOM with customers and market surveillance authorities as an independent transparency document.
OWASP Dependency-Check
SCA · Java/Maven · Deep analysis · NVDThe SCA veteran, particularly strong on Java and .NET. Matches dependencies against NVD and OSS Index. Slower than Trivy (downloads entire NVD on first run), but excellent coverage for enterprise Java components. Appropriate for weekly scheduled scans. Supports Maven, Gradle, MSBuild, npm, Python, Ruby.
Container and IaC Security
Container image scanning with Trivy should run on the final built image — vulnerabilities can be introduced in upper layers. The correct pattern: docker build → trivy image → push to registry only if clean.
Hadolint for Dockerfiles verifies best practices: no apt-get update without apt-get install in the same RUN, no latest tags, no processes running as root. Three lines of GitHub Actions.
Checkov or trivy config . for IaC (Terraform, Kubernetes, Helm, CloudFormation): overly permissive security groups, public S3 buckets, unencrypted volumes. For CRA, IaC misconfigurations that expose the infrastructure attack surface are directly relevant to interface protection requirements (Annex I, §3).
Integrating Everything: GitHub Actions Patterns
Practical, production-ready patterns. Every job produces SARIF output uploaded to the GitHub Security tab. For CRA, the audit trail of these workflow runs is also documentary evidence of vulnerability testing processes.
Secrets Detection with Gitleaks
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}SAST with Semgrep
semgrep:
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- run: |
semgrep scan \
--config=auto \
--sarif \
--output=semgrep.sarif \
--severity=ERROR \
--error
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep.sarifSCA + Container + SBOM with Trivy
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy — filesystem scan (SCA)
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy-fs.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: 1
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Trivy — image scan
uses: aquasecurity/trivy-action@master
with:
scan-type: image
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-image.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: 1
# CRA Art. 13(8) — SBOM generation
- name: Trivy — generate SBOM (CycloneDX)
uses: aquasecurity/trivy-action@master
with:
scan-type: image
image-ref: app:${{ github.sha }}
format: cyclonedx
output: sbom.cdx.json
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-fs.sarif
- uses: actions/upload-artifact@v4
with:
name: sbom-${{ github.sha }}
path: sbom.cdx.jsonDAST with OWASP ZAP (ephemeral environment)
zap-baseline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start application stack
run: docker compose -f docker-compose.test.yml up -d
- name: Wait for health
run: |
timeout 60 sh -c \
'until curl -sf http://localhost:8080/health; do sleep 2; done'
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: http://localhost:8080
fail_action: false
artifact_name: zap-report
rules_file_name: .zap/rules.tsv
- name: Tear down
if: always()
run: docker compose -f docker-compose.test.yml down -vDAST with Nuclei (dedicated staging)
nuclei:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: projectdiscovery/nuclei-action@main
with:
target: ${{ vars.STAGING_URL }}
flags: >-
-t exposures
-t misconfiguration
-t default-logins
-severity medium,high,critical
-rate-limit 50
-sarif-export nuclei.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: nuclei.sarifThreshold Management and False Positive Handling
SARIF unifies everything. Uploaded via codeql-action/upload-sarif, it appears in the GitHub Security tab — a unified dashboard. For CRA, this dashboard is also the audit trail of vulnerabilities detected and their remediation.
- Semgrep:
# nosemgrep: rule-idinline. Every suppression needs a written justification — unjustified suppressions are an audit risk under CRA. - Trivy:
.trivyignorewith CVE IDs and comments. - ZAP:
.zap/rules.tsvmapping alert IDs to IGNORE/WARN with notes. - Progressive ratchet: CRITICAL first, then HIGH, then MEDIUM. Never start with everything on day one against a legacy codebase.
Suppression rate is a health metric: more than 20-30% of findings suppressed rather than resolved signals a calibration or ownership problem. For CRA audits, a high unjustified suppression rate is a red flag.
Anti-Patterns That Kill Security Automation Programs
Zero tolerance from day one on a legacy codebase
Enabling all tools at maximum severity on a codebase with no prior security automation. 800 findings on day one, developers quietly adding --exit-code 0, tool disabled within two weeks. The progressive ratchet is not a compromise — it is the only strategy that works.
Every scanner on every commit
CodeQL, full DAST, complete OWASP Dependency-Check on every push including feature branches. Pipeline becomes slow, developers stop waiting for green, tools become decorative. Match scan scope to pipeline event.
DAST against production
Pointing a DAST scanner with active scan enabled at production. Self-DoS, test data in prod DB, rate limiting blocking real users. DAST belongs in isolated ephemeral environments with synthetic data — no exceptions.
SCA without a patching process
Finding CRITICAL CVEs without a remediation SLA. For CRA, knowing about an unfixed critical vulnerability without acting is a direct violation of Annex I, Part II, §1 vulnerability management obligations. "We didn't know" is no longer a valid defense once SCA is active.
Security automation without developer buy-in
Adding scanners without explaining what they find and how to fix it. Developers see "build failed: security scan" with no context, rebase to bypass it, or open a PR to remove the step. Security automation works when developers understand the value — not just see the red CI badge.
Measuring What Matters
Time from vulnerability introduction to detection. Goal: approach zero — caught in the PR, not in production.
With working SAST + SCA + DAST, expected baseline is over 85% caught before production.
High rate signals calibration or ownership problems. Target: less than 25% suppressed instead of fixed.
60% coverage leaves 40% of the surface unmonitored. For CRA-scoped products, coverage must be total.
SecDevOps and the Cyber Resilience Act
The CRA doesn't name "CI/CD security automation" directly, but its technical obligations are practically impossible to satisfy without it:
- Art. 13(2) — Security by design: a CI/CD pipeline with documented security gates and audit trail is the operational proof that security is integrated into the development process — not added post-hoc.
- Annex I, Part I, §2 — No known exploitable vulnerabilities: SAST + SCA in CI is the continuous mechanism for satisfying this requirement at every release, not just at initial certification.
- Art. 13(8) — SBOM: automated SBOM generation (Trivy/Syft) integrated into the build pipeline is the delivery mechanism. Every release should produce a signed CycloneDX SBOM — not a manually created document assembled before an audit.
- Art. 14 — Vulnerability notification (24h for actively exploited): continuous monitoring via Trivy with scheduled DB refreshes lets you detect new CVEs affecting production dependencies — a prerequisite for meeting ENISA notification timelines.
For Class II and Critical products, notified body assessment will almost certainly include verification of documented automated vulnerability testing processes. A CI/CD pipeline with configured security scans, execution logs, and remediation traceability is not just best practice — it's how you pass certification due diligence.
Building a Program That Survives
I've seen carefully built security automation programs decay within six months when the implementing team moved on. Sustainability isn't technology — it's ownership and culture.
Three things learned the hard way: first, every scanner needs a named owner responsible for calibration and triage — "the security team" is not an owner. Second, custom rules and suppression files must be in version control with a review process. Third, tool speed is an adoption requirement: if a developer waits 20 minutes for security feedback, they stop waiting. Slow tools get bypassed.
The CRA.tips team has designed and implemented security automation in CI/CD pipelines for organizations producing enterprise SaaS, embedded IoT products, and critical systems in CRA scope. If you want to build a real program — not a set of installed-and-ignored tools — start by assessing your compliance gap.