Torna al Blog Back to Blog

SecDevOps: Automatizzare la Sicurezza
nei Pipeline CI/CD

SecDevOps: Automating Security
Into Your CI/CD Pipeline

Dopo anni a costruire e riparare programmi di sicurezza in organizzazioni di ogni dimensione, posso dire con certezza: la maggior parte dello "shift left" che vedo in produzione è teatro della sicurezza. Strumenti installati, dashboard piene di rosso, zero mitigazioni reali. Questa guida è su come farlo nel modo giusto — con scanner open source, integrazione reale nei pipeline e una strategia che non fa odiare la sicurezza ai developer.

After years of building and fixing security programs across organizations of all sizes, I can say with confidence: most "shift left" I see in production is security theater. Tools installed, dashboards full of red, zero real mitigations. This guide is about doing it right — open source scanners, real pipeline integration, and a strategy that doesn't make developers hate security.

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. Questo è il valore reale — non la compliance checkbox.

"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.

Pre-commit
Gitleaks TruffleHog Semgrep (fast rules)
PR / MR Gate
Semgrep Trivy (SCA + IaC) CodeQL Hadolint Checkov
Build
Trivy (container image) Syft (SBOM) Grype
Deploy Staging
ZAP Baseline Nuclei Wapiti
Scheduled
ZAP Active Scan Trivy DB refresh OWASP Dep-Check Full Nuclei sweep

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 · CI

Il 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 (per le false positive da entropy-based detection). Output in JSON, SARIF, CSV. Si integra nativamente con GitHub Actions come action dedicata.

TruffleHog

Secrets · High signal · Verified detection

Si 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 del SAST era il rapporto segnale/rumore: troppi falsi positivi, developer che imparano a ignorare i report, strumento che viene disabilitato silenziosamente entro un mese dall'installazione.

Gli strumenti moderni hanno risolto parzialmente questo problema. 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 la copertura. Questo approccio a ratchet è l'unico che sopravvive all'impatto con una codebase reale.

Semgrep

Multi-language · Fast · Customizable

Il backbone del SAST moderno per la maggior parte dei team. 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 non sta nei community rules — sta nella capacità di scrivere custom rules: se hai avuto una vulnerability class una volta, scrivi una regola Semgrep e quella classe non entrerà mai più in produzione. Le regole sono YAML leggibile, non regex infernali. Il registry pubblico (semgrep.dev/r) offre migliaia di regole mantenute dalla community. Nota: --config=auto scarica le regole a runtime — in ambienti air-gapped o per build deterministici, usa un registry locale o versiona le regole nel repo.

CodeQL

Deep semantic analysis · GitHub-native · Java, C/C++, Python, JS

Analisi semantica profonda del flusso di dati: traccia come i dati si muovono dal punto di input (HTTP request, file, env var) fino al punto di sink (SQL query, shell exec, network write). Eccelle nel trovare vulnerability class complesse che Semgrep non vede — taint analysis, path-sensitive issues, inter-procedural bugs. Il prezzo: build time significativo (compila il codebase). 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 · Brakeman

Bandit per Python: analizza AST, trova hardcoded passwords, uso di eval(), MD5/SHA1, binding su 0.0.0.0, ecc. Veloce, configurabile con .bandit. Gosec per Go: G-rules specifiche per il runtime Go, file permission insicuri, SQL injection via fmt.Sprintf, TLS misconfiguration. SpotBugs + FindSecBugs plugin per Java/Kotlin: 80+ security bug patterns inclusi injection, XXE, insecure deserialization. Brakeman per Ruby on Rails: analisi statica con comprensione profonda del framework, trova mass assignment vulnerabilities, SQL injection, XSS nel contesto specifico di Rails.

DAST: Il Problema Difficile

Il DAST è la categoria che la maggior parte dei team implementa male — o non implementa affatto nel CI/CD — perché richiede un'applicazione in esecuzione. Non puoi fare DAST su codice sorgente. Hai bisogno di un endpoint HTTP raggiungibile, il che significa ambiente di deploy, il che significa Docker Compose o Kubernetes, il che significa pipeline più complessi.

Il pattern corretto per il DAST in CI: spin up dell'applicazione in Docker Compose (app + database + dipendenze), health check, scan DAST, tear down. L'intero ciclo dovrebbe stare in 5-10 minuti per essere sostenibile. Se ci vuole di più, il DAST appartiene a un job separato su schedule, non al PR gate.

Baseline vs. Active Scan: OWASP ZAP ha due modalità principali. La baseline scan esegue solo check passivi — analizza le risposte HTTP senza inviare payload di attacco. È CI-safe, completamente sicura su ambienti di staging condivisi, e prende 2-5 minuti. L'active scan genera traffico di attacco reale (fuzzing, brute force, injection payloads) — NON deve essere eseguita su database reali o ambienti condivisi. Usare active scan in production è auto-DoS. Riserva l'active scan a ambienti effimeri e isolati.

OWASP ZAP

DAST · Web Application · Passive + Active · GitHub Action

Lo standard de facto per il DAST open source in CI/CD. La GitHub Action zaproxy/action-baseline esegue la baseline scan (passiva) in modo completamente hands-off: spin-up headless, scan, report HTML/JSON/SARIF, tear-down. Configurabile con file di regole YAML per escludere path noti (health check endpoints, static assets) che altrimenti genererebbero rumore. Per applicazioni con autenticazione, supporta script di login in JavaScript/Python/ZAP scripting. Il report HTML è leggibile per i developer — non solo JSON per il SIEM.

Nuclei

DAST · Template-based · High signal · CI-friendly

Template-based scanner con un repository di oltre 9.000 template community-maintained. Si distingue per la qualità del segnale: i template Nuclei sono specifici per CVE, misconfiguration, exposed credentials, e default logins — raramente producono falsi positivi. Ideale per CI perché puoi selezionare esattamente quali categorie di template girare: -t exposures/ -t misconfiguration/ -t default-logins/ per un run veloce e a basso rumore. Sempre impostare -rate-limit 50 per evitare self-DoS. Output in JSON, SARIF, Markdown. ProjectDiscovery mantiene anche una versione cloud (PDCP) con vulnerability management integrato.

Wapiti

DAST · Web · Python · Lightweight

Alternativa più leggera a ZAP per applicazioni web 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. Non raggiunge la profondità di ZAP ma è più rapido su applicazioni semplici.

SCA: Dove Vivono le Tue CVE Reali

Se dovessi indicare la singola categoria di scan con il più alto ROI per la maggior parte delle organizzazioni, sarebbe la Software Composition Analysis. Più dell'80% delle CVE che trovo durante security review provengono da dipendenze di terze parti, non dal codice custom. Log4Shell, Spring4Shell, les innumerevoli CVE critical di componenti npm: queste vulnerability existevano nella codebase da mesi o anni senza che nessuno lo sapesse. L'SCA risolve questo problema in modo strutturale.

Trivy

Multi-purpose · OS + App + Container + IaC + SBOM

Il 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 in IaC (Terraform, Kubernetes, CloudFormation), segreti, e generazione SBOM in formato CycloneDX o SPDX-JSON. Il flag --ignore-unfixed è critico: non fallire il build su CVE per cui non esiste ancora una fix disponibile — ti addestreresti ad ignorare i report. Il flag --severity CRITICAL,HIGH per il PR gate, con MEDIUM in warning mode fino a quando hai risolto il backlog. Aggiorna il DB vulnerabilità a ogni run; in ambienti air-gapped usa --skip-db-update con un DB in cache.

Grype + Syft

SCA + SBOM · Composable · Anchore

L'alternativa composable di Anchore. Syft genera SBOM da filesystem, immagini container, o directory — output in SPDX-JSON, CycloneDX, o formato Syft nativo. Grype prende l'SBOM generato da Syft (o da qualsiasi altra fonte) e lo confronta con Grype DB per trovare CVE. Il vantaggio di questa separazione: puoi generare SBOM una volta e scansionarlo più volte con DB aggiornati, o condividerlo con clienti e partner per la loro propria vulnerability assessment. Ottima detection su ecosistemi Python, Ruby, e Java dove Trivy a volte manca coverage.

OWASP Dependency-Check

SCA · Java/Maven · Deep analysis · NVD

Il veterano del SCA, particolarmente forte su ecosistemi Java e .NET. Confronta le dipendenze contro il NVD (National Vulnerability Database) e il database OSS Index. Più lento di Trivy (scarica l'intero NVD al primo run — 10-15 minuti), ma produce report HTML molto dettagliati e ha coverage eccellente per i componenti enterprise Java. Appropriato per scans schedulati settimanali piuttosto che per il PR gate. Supporta Maven, Gradle, MSBuild, npm, Python, Ruby.

Container e IaC Security

Due categorie spesso dimenticate che meritano integrazione nel pipeline:

Container image scanning con Trivy (trivy image <name>) va eseguito sul layer finale dell'immagine built nel pipeline CI — non sulla base image in isolamento. Le vulnerability possono essere introdotte nei layer superiori (pacchetti installati nel Dockerfile, dipendenze copiate dentro). Il pattern corretto: docker buildtrivy image → push al registry solo se clean.

Hadolint per i Dockerfile è il linter che ogni team dovrebbe usare. Verifica best practice: no apt-get update senza apt-get install nella stessa RUN, no latest tags per le immagini base, no ADD quando COPY è sufficiente, no processi running come root. Integrazione in 3 righe di GitHub Actions.

Checkov o trivy config . per l'IaC (Terraform, Kubernetes manifests, Helm charts, CloudFormation): security groups too permissive, public S3 buckets, unencrypted EBS volumes, Kubernetes pods senza security context. Questi findings sono spesso i più critici — una misconfiguration IaC può esporre tutta l'infrastruttura, non solo un singolo endpoint.

Integrare Tutto: Pattern GitHub Actions

Ecco pattern pratici e production-ready per le categorie principali. Adattali al tuo workflow — l'importante è che ogni job produca output SARIF caricato nel GitHub Security tab, che diventa il tuo unified security dashboard.

Secrets Detection con Gitleaks

GitHub Actions — .github/workflows/security.yml
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

GitHub Actions
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            # exit 1 on findings
    - uses: github/codeql-action/upload-sarif@v3
      if: always()
      with:
        sarif_file: semgrep.sarif

SCA + Container + SBOM con Trivy

GitHub Actions
trivy:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    # SCA on source code
    - name: Trivy — filesystem scan
      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

    # Container image scan + SBOM
    - 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

    - 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
        path: sbom.cdx.json

DAST con OWASP ZAP (su ambiente effimero)

GitHub Actions
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       # warn, don't block — adjust after baseline
        artifact_name: zap-report
        rules_file_name: .zap/rules.tsv   # suppress known-acceptable alerts

    - name: Tear down
      if: always()
      run: docker compose -f docker-compose.test.yml down -v

DAST con Nuclei (su staging dedicato)

GitHub Actions
nuclei:
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'    # only on main branch
  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.sarif

Gestione delle Soglie e dei Falsi Positivi

Il formato SARIF (Static Analysis Results Interchange Format) è lo standard che unifica tutto: Semgrep, Trivy, ZAP, Nuclei, CodeQL producono tutti SARIF. Caricato in GitHub tramite codeql-action/upload-sarif, appare nel tab "Security → Code scanning alerts" del repository — un dashboard unificato senza bisogno di strumenti aggiuntivi.

Per gestire i falsi positivi senza degradare la qualità del programma:

  • Semgrep: # nosemgrep: rule-id inline nel codice. Ogni soppressione deve avere un commento su una riga separata che spiega il perché. Se non riesci a giustificare la soppressione per iscritto, probabilmente non dovresti sopprimerla.
  • Trivy: file .trivyignore nella root del repository, con CVE ID e commento. Esempio: CVE-2023-XXXXX # no fix available, not exploitable in our deployment model.
  • ZAP: file .zap/rules.tsv per la baseline scan — mappa alert ID a IGNORE/WARN con una nota.
  • Ratchet progressivo: inizia con solo CRITICAL, risolvi tutto, poi aggiungi HIGH, poi MEDIUM. Non provare a partire da zero con tutto abilitato su una codebase legacy — è un percorso sicuro verso la disabilitazione dello strumento.

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. Traccia il suppression rate nel tempo — un trend crescente è un segnale di allarme.

Anti-Pattern che Uccidono i Programmi di Security Automation

1

Zero tolerance dal primo giorno su una codebase legacy

Abilitare tutti i tool al massimo severity level su una codebase che non ha mai visto security automation. Risultato garantito: 800 finding nel dashboard il primo giorno, developer che aggiungono --exit-code 0 al comando in silenziosa disperazione, strumento disabilitato entro due settimane. Il ratchet progressivo non è un compromesso sulla sicurezza — è l'unica strategia che funziona in produzione.

2

Tutti gli scanner su ogni commit

CodeQL, full DAST, OWASP Dependency-Check completo, Nuclei full sweep — su ogni push, incluse le feature branch con 3 commit di "fix typo". Il pipeline diventa lento (15-20 minuti), i developer smettono di aspettare il verde e fanno merge comunque, i tool diventano decorativi. Abbina lo scope dello scan all'evento: fast scans su ogni commit, full scans su PR verso main, heavy scans su schedule notturni.

3

DAST contro produzione

Puntare uno scanner DAST — specialmente con active scan abilitato — contro l'ambiente di produzione. Ho visto questo causare self-DoS, dati di test scritti nel database prod, e rate limiting che ha bloccato utenti reali. Il DAST appartiene a ambienti effimeri isolati con dati sintetici. Se non hai un ambiente di staging, costruiscilo nel Docker Compose del CI prima di installare qualsiasi scanner DAST.

4

SCA senza un processo di patching

Trovare CVE CRITICAL nelle dipendenze e non avere un SLA di remediation. Sapere di una vulnerabilità critica non fixata è, in molte giurisdizioni e framework regolatori, peggio di non saperlo — sei in "knew or should have known" territory. Se installi l'SCA, devi anche avere: SLA di patching per severità (CRITICAL entro 7 giorni, HIGH entro 30), un owner per ogni componente, e un processo di eccezione documentato per i casi dove l'upgrade non è immediato.

5

Security automation senza developer buy-in

Aggiungere scanner al pipeline senza spiegare ai developer cosa trovano, perché contano, e come fixarli. Risultato: feedback loop muto — 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. Investi in documentazione interna, workshop, e soprattutto: fai in modo che il feedback dei tool sia chiaro, contestuale, e accompagnato da link a remediation guidance.

Misurare Quello che Conta

La metrica sbagliata: "numero di scan eseguiti". Non misura nulla di utile. Ecco le metriche che indicano se il tuo programma sta funzionando:

MTTD Pre-Prod
Mean Time to Detect

Quanto tempo passa tra l'introduzione di una vulnerability e la sua rilevazione? L'obiettivo è che questo numero tenda a zero — il tool dovrebbe rilevare nel PR, non in produzione.

Pre-prod Capture Rate
% Vuln catturate prima del deploy

Delle vulnerability trovate in un quarter, quante sono state rilevate prima di raggiungere produzione? Il baseline atteso con SAST + SCA + DAST funzionanti è oltre l'85%.

Suppression Rate
% Findings soppressi vs. fixati

Un suppression rate alto segnala problemi di calibrazione degli strumenti o mancanza di ownership. Target: meno del 25% dei finding soppressi (non fixati).

Scanner Coverage
% Repository con security scans

Quanti dei tuoi repository attivi hanno almeno SAST + SCA nel CI? Una security automation che copre il 60% dei repository ha il 40% di superficie non monitorata.

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: dimostrare che la sicurezza è integrata nel processo di sviluppo, non aggiunta post-hoc. Un pipeline CI/CD con security gates documentati e audit trail è la prova operativa di questo requisito.
  • 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.
  • Art. 13(8) — SBOM: la generazione automatica di SBOM (Trivy/Syft) integrata nel build pipeline è la delivery mechanism. Ogni release dovrebbe produrre un SBOM CycloneDX firmato come artefatto.
  • Art. 14 — Notifica delle vulnerabilità (24h per actively exploited): il monitoraggio continuo tramite Trivy con DB refresh schedulate è ciò che ti permette di rilevare nuove CVE che impattano le dipendenze già in produzione — un prerequisito per rispettare le timeline di notifica.

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. Avere 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 e budget decadere nel giro di sei mesi quando il team che li ha implementati si è spostato ad altro. La sostenibilità non è una questione di tecnologia — è una questione di ownership e cultura.

Tre cose che ho imparato nel modo difficile: primo, ogni scanner deve avere un owner nominato responsabile della calibrazione e della triage — "il team di sicurezza" non è un owner. Secondo, le regole custom Semgrep e i file .trivyignore devono essere in version control con review process, non gestiti da una singola persona. Terzo, il feedback loop deve essere abbastanza veloce da sembrare utile: se un developer deve aspettare 20 minuti per sapere se la sua PR ha un security finding, smette di aspettare. La velocità degli strumenti non è un'opzione — è un requisito di adozione.

Il team di ProductSecurity.it ha progettato e implementato security automation nei pipeline CI/CD per organizzazioni che producono SaaS enterprise, prodotti IoT embedded e sistemi critici. Se vuoi costruire un programma reale — non un set di tool installati e ignorati, ma un meccanismo che effettivamente riduce la superficie di rischio — inizia valutando la tua postura attuale.

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. Every PR can receive contextual security feedback before touching main. Vulnerability classes fixed once can be prevented forever with a custom rule. That is the real value — not the compliance checkbox.

"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, you need to answer one question: where in the pipeline does this analysis produce the most useful feedback with the least overhead? Each scan type has different requirements in terms of environment, execution time, and finding type.

Pre-commit
Gitleaks TruffleHog Semgrep (fast rules)
PR / MR Gate
Semgrep Trivy (SCA + IaC) CodeQL Hadolint Checkov
Build
Trivy (container image) Syft (SBOM) Grype
Deploy Staging
ZAP Baseline Nuclei Wapiti
Scheduled
ZAP Active Scan Trivy DB refresh OWASP Dep-Check Full Nuclei sweep

The general rule: fast scans (secret detection, SAST fast rules, linting) belong to pre-commit and the PR gate. Scans that require a running application (DAST) belong to the staging environment. Heavy scans (CodeQL, full dependency audit) can run on a nightly schedule without blocking every PR.

Secret Detection: The One True Zero-Tolerance Case

Secrets in git repositories are the only category where zero tolerance is justified. Unlike a vulnerability in code that requires exploitation, a committed API key is already an incident. The reason is trivial: git doesn't forget. You can remove the file in the next commit, but the key is in the history — and if the repository was ever cloned, forked, or crawled by GitHub, that key is out. Forever.

Gitleaks

Secrets · Git history · Pre-commit · CI

Best 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 (for false positives from entropy-based detection). Output in JSON, SARIF, CSV. Integrates natively with GitHub Actions as a dedicated action.

TruffleHog

Secrets · High signal · Verified detection

Distinguishes itself with verified detection: for certain secret categories (AWS, GitHub tokens, Stripe, etc.) it actively verifies whether the secret is still valid before reporting it. Dramatically reduces noise. Useful in combination with Gitleaks: Gitleaks for speed at pre-commit, TruffleHog for verification in CI pipelines. Supports scanning of git repositories, filesystems, S3, Docker images, and GitHub organization-wide scans.

The git history problem: If a secret was committed and later removed, the secret is still in history. The only resolution is git filter-repo to rewrite history (destructive, requires coordination with all contributors) 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

SAST analyzes source code without executing it, searching for known vulnerability patterns. The historical problem with SAST was signal-to-noise: too many false positives, developers who learn to ignore reports, tool silently disabled within a month of installation.

Modern tools have partially solved this problem. The key is progressive calibration: don't start with zero tolerance on all severity levels on day one. Start with only CRITICAL/HIGH, establish a baseline, resolve what's there, then gradually expand coverage. This ratchet approach is the only one that survives contact with a real codebase.

Semgrep

Multi-language · Fast · Customizable

The backbone of modern SAST for most teams. Runs in under 60 seconds on most codebases, supports 30+ languages, and SARIF output loads directly into the GitHub Security tab. The real value isn't in the community rules — it's in the ability to write custom rules: if you've had a vulnerability class once, write a Semgrep rule and that class will never enter production again. Rules are readable YAML, not hellish regexes. The public registry (semgrep.dev/r) offers thousands of community-maintained rules. Note: --config=auto downloads rules at runtime — in air-gapped environments or for deterministic builds, use a local registry or version the rules in your repo.

CodeQL

Deep semantic analysis · GitHub-native · Java, C/C++, Python, JS

Deep data-flow semantic analysis: traces how data moves from an input point (HTTP request, file, env var) to a sink (SQL query, shell exec, network write). Excels at finding complex vulnerability classes that Semgrep misses — taint analysis, path-sensitive issues, inter-procedural bugs. The price: significant build time (it compiles the codebase). 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 · Brakeman

Bandit for Python: analyzes AST, finds hardcoded passwords, use of eval(), MD5/SHA1, binding to 0.0.0.0, etc. Fast, configurable with .bandit. Gosec for Go: G-rules specific to the Go runtime, insecure file permissions, SQL injection via fmt.Sprintf, TLS misconfiguration. SpotBugs + FindSecBugs plugin for Java/Kotlin: 80+ security bug patterns including injection, XXE, insecure deserialization. Brakeman for Ruby on Rails: static analysis with deep framework understanding, finds mass assignment vulnerabilities, SQL injection, XSS in the specific Rails context.

DAST: The Hard One

DAST is the category most teams implement poorly — or don't implement at all in CI/CD — because it requires a running application. You can't DAST source code. You need a reachable HTTP endpoint, which means a deployed environment, which means Docker Compose or Kubernetes, which means more complex pipelines.

The correct pattern for DAST in CI: spin up the application in Docker Compose (app + database + dependencies), health check, DAST scan, tear down. The entire cycle should fit in 5-10 minutes to be sustainable. If it takes longer, DAST belongs on a separate scheduled job, not the PR gate.

Baseline vs. Active Scan: OWASP ZAP has two primary modes. The baseline scan runs only passive checks — it analyzes HTTP responses without sending attack payloads. It's CI-safe, completely safe against shared staging environments, and takes 2-5 minutes. The active scan generates real attack traffic (fuzzing, brute force, injection payloads) — it must NOT be run against real databases or shared environments. Running an active scan against production is self-DoS. Reserve active scans for isolated ephemeral environments.

OWASP ZAP

DAST · Web Application · Passive + Active · GitHub Action

The de facto standard for open source DAST in CI/CD. The zaproxy/action-baseline GitHub Action runs the baseline scan (passive) in a completely hands-off way: headless spin-up, scan, HTML/JSON/SARIF report, tear-down. Configurable with YAML rules files to exclude known paths (health check endpoints, static assets) that would otherwise generate noise. For authenticated applications, supports login scripts in JavaScript/Python/ZAP scripting. The HTML report is developer-readable — not just JSON for the SIEM.

Nuclei

DAST · Template-based · High signal · CI-friendly

Template-based scanner with a repository of over 9,000 community-maintained templates. Stands out for signal quality: Nuclei templates are specific to CVEs, misconfigurations, exposed credentials, and default logins — they rarely produce false positives. Ideal for CI because you can select exactly which template categories to run: -t exposures/ -t misconfiguration/ -t default-logins/ for a fast, low-noise run. Always set -rate-limit 50 to avoid self-DoS. Output in JSON, SARIF, Markdown. ProjectDiscovery also maintains a cloud version (PDCP) with integrated vulnerability management.

Wapiti

DAST · Web · Python · Lightweight

A lighter alternative to ZAP for web applications with a reduced surface. Written in Python, easy to extend with custom modules. Tests the main OWASP Top 10 categories: SQL injection, XSS, File inclusion, XXE, SSRF, Open redirect, CSRF. Useful when ZAP is oversized or when programmatic integration is needed in Python-based pipelines. Doesn't match ZAP's depth but is faster on simpler applications.

SCA: Where Your Real CVEs Live

If I had to identify the single scan category with the highest ROI for most organizations, it would be Software Composition Analysis. More than 80% of the CVEs I find during security reviews come from third-party dependencies, not custom code. Log4Shell, Spring4Shell, the countless critical CVEs in npm components — these vulnerabilities existed in codebases for months or years without anyone knowing. SCA solves this problem structurally.

Trivy

Multi-purpose · OS + App + Container + IaC + SBOM

The Swiss Army knife of security automation. A single tool that covers: application dependencies (npm, pip, Maven, Gradle, Go modules, Cargo, Composer, NuGet), OS packages in the container image, IaC misconfigurations (Terraform, Kubernetes, CloudFormation), secrets, and SBOM generation in CycloneDX or SPDX-JSON format. The --ignore-unfixed flag is critical: don't fail the build on CVEs for which no fix is yet available — you'd be training yourself to ignore reports. Use --severity CRITICAL,HIGH for the PR gate, with MEDIUM in warning mode until you've resolved the backlog. Updates the vulnerability DB on each run; in air-gapped environments use --skip-db-update with a cached DB.

Grype + Syft

SCA + SBOM · Composable · Anchore

The composable alternative from Anchore. Syft generates SBOMs from filesystems, container images, or directories — output in SPDX-JSON, CycloneDX, or native Syft format. Grype takes the SBOM generated by Syft (or any other source) and matches it against Grype DB to find CVEs. The advantage of this separation: you can generate the SBOM once and scan it multiple times with updated DBs, or share it with customers and partners for their own vulnerability assessment. Excellent detection on Python, Ruby, and Java ecosystems where Trivy sometimes has coverage gaps.

OWASP Dependency-Check

SCA · Java/Maven · Deep analysis · NVD

The veteran of SCA, particularly strong on Java and .NET ecosystems. Matches dependencies against the NVD (National Vulnerability Database) and OSS Index. Slower than Trivy (downloads the entire NVD on first run — 10-15 minutes), but produces very detailed HTML reports and has excellent coverage for enterprise Java components. Appropriate for scheduled weekly scans rather than the PR gate. Supports Maven, Gradle, MSBuild, npm, Python, Ruby.

Container and IaC Security

Two often-overlooked categories that deserve pipeline integration:

Container image scanning with Trivy (trivy image <name>) should run on the final built image in the CI pipeline — not the base image in isolation. Vulnerabilities can be introduced in upper layers (packages installed in the Dockerfile, dependencies copied in). The correct pattern: docker buildtrivy image → push to registry only if clean.

Hadolint for Dockerfiles is the linter every team should use. Verifies best practices: no apt-get update without apt-get install in the same RUN, no latest tags for base images, no ADD when COPY suffices, no processes running as root. Integration in 3 lines of GitHub Actions.

Checkov or trivy config . for IaC (Terraform, Kubernetes manifests, Helm charts, CloudFormation): overly permissive security groups, public S3 buckets, unencrypted EBS volumes, Kubernetes pods without a security context. These findings are often the most critical — an IaC misconfiguration can expose the entire infrastructure, not just a single endpoint.

Integrating Everything: GitHub Actions Patterns

Here are practical, production-ready patterns for the main categories. Adapt them to your workflow — the important thing is that every job produces SARIF output uploaded to the GitHub Security tab, which becomes your unified security dashboard.

Secrets Detection with Gitleaks

GitHub Actions — .github/workflows/security.yml
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 with Semgrep

GitHub Actions
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            # exit 1 on findings
    - uses: github/codeql-action/upload-sarif@v3
      if: always()
      with:
        sarif_file: semgrep.sarif

SCA + Container + SBOM with Trivy

GitHub Actions
trivy:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    # SCA on source code
    - name: Trivy — filesystem scan
      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

    # Container image scan + SBOM
    - 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

    - 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
        path: sbom.cdx.json

DAST with OWASP ZAP (ephemeral environment)

GitHub Actions
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       # warn, don't block — adjust after baseline
        artifact_name: zap-report
        rules_file_name: .zap/rules.tsv   # suppress known-acceptable alerts

    - name: Tear down
      if: always()
      run: docker compose -f docker-compose.test.yml down -v

DAST with Nuclei (dedicated staging)

GitHub Actions
nuclei:
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'    # only on main branch
  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.sarif

Threshold Management and False Positive Handling

SARIF (Static Analysis Results Interchange Format) is the standard that unifies everything: Semgrep, Trivy, ZAP, Nuclei, and CodeQL all produce SARIF. Uploaded to GitHub via codeql-action/upload-sarif, it appears in the repository's "Security → Code scanning alerts" tab — a unified dashboard without additional tooling.

To manage false positives without degrading program quality:

  • Semgrep: # nosemgrep: rule-id inline in code. Every suppression must have a comment on a separate line explaining why. If you can't justify the suppression in writing, you probably shouldn't suppress it.
  • Trivy: .trivyignore file in the repository root, with CVE IDs and comments. Example: CVE-2023-XXXXX # no fix available, not exploitable in our deployment model.
  • ZAP: .zap/rules.tsv file for the baseline scan — maps alert IDs to IGNORE/WARN with a note.
  • Progressive ratchet: start with CRITICAL only, resolve everything, then add HIGH, then MEDIUM. Don't try to start from zero with everything enabled on a legacy codebase — it's a guaranteed path to tool disablement.

Suppression rate is a health metric: if more than 20-30% of findings are suppressed rather than resolved, your tool has a calibration problem or your team has an ownership problem. Track the suppression rate over time — a growing trend is a warning signal.

Anti-Patterns That Kill Security Automation Programs

1

Zero tolerance from day one on a legacy codebase

Enabling all tools at maximum severity level on a codebase that has never seen security automation. Guaranteed result: 800 findings in the dashboard on day one, developers quietly adding --exit-code 0 to the command in silent desperation, tool disabled within two weeks. The progressive ratchet is not a security compromise — it is the only strategy that works in production.

2

Every scanner on every commit

CodeQL, full DAST, complete OWASP Dependency-Check, full Nuclei sweep — on every push, including feature branches with 3 "fix typo" commits. The pipeline becomes slow (15-20 minutes), developers stop waiting for green and merge anyway, tools become decorative. Match scan scope to event: fast scans on every commit, full scans on PRs toward main, heavy scans on nightly schedules.

3

DAST against production

Pointing a DAST scanner — especially with active scan enabled — at the production environment. I've seen this cause self-DoS, test data written to the prod database, and rate limiting that blocked real users. DAST belongs in isolated ephemeral environments with synthetic data. If you don't have a staging environment, build it in the CI's Docker Compose before installing any DAST scanner.

4

SCA without a patching process

Finding CRITICAL CVEs in dependencies with no remediation SLA. Knowing about an unfixed critical vulnerability is, in many jurisdictions and regulatory frameworks, worse than not knowing — you're in "knew or should have known" territory. If you install SCA, you must also have: patching SLAs per severity (CRITICAL within 7 days, HIGH within 30), an owner for each component, and a documented exception process for cases where upgrade isn't immediate.

5

Security automation without developer buy-in

Adding scanners to the pipeline without explaining to developers what they find, why it matters, and how to fix it. Result: a muted feedback loop — the developer sees "build failed: security scan" with no context, rebases to bypass it, or opens a PR to remove the step. Security automation works when developers understand the value. Invest in internal documentation, workshops, and above all: make tool feedback clear, contextual, and accompanied by remediation guidance links.

Measuring What Matters

The wrong metric: "number of scans run." It measures nothing useful. Here are the metrics that indicate whether your program is working:

MTTD Pre-Prod
Mean Time to Detect

How long passes between a vulnerability being introduced and its detection? The goal is for this number to approach zero — the tool should catch it in the PR, not in production.

Pre-prod Capture Rate
% Vulns caught before deploy

Of vulnerabilities found in a quarter, how many were detected before reaching production? The expected baseline with working SAST + SCA + DAST is over 85%.

Suppression Rate
% Findings suppressed vs. fixed

A high suppression rate signals tool calibration problems or lack of ownership. Target: less than 25% of findings suppressed (rather than fixed).

Scanner Coverage
% Repos with security scans

How many of your active repositories have at least SAST + SCA in CI? Security automation covering 60% of repositories leaves 40% of the surface unmonitored.

SecDevOps and the Cyber Resilience Act

The CRA doesn't name "CI/CD security automation" in its articles, but its technical obligations are practically impossible to satisfy without it:

  • Art. 13(2) — Security by design: demonstrating that security is integrated into the development process, not added post-hoc. A CI/CD pipeline with documented security gates and an audit trail is the operational proof of this requirement.
  • Annex I, Part I, §2 — No known exploitable vulnerabilities: SAST + SCA in CI is the operational mechanism for satisfying this requirement continuously, not just at initial release.
  • 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 as an artifact.
  • Art. 14 — Vulnerability notification (24h for actively exploited): continuous monitoring via Trivy with scheduled DB refreshes is what lets you detect new CVEs affecting dependencies already in production — a prerequisite for meeting notification timelines.

For Class II and Critical products, assessment by a notified body will almost certainly include verification of documented automated vulnerability testing processes. Having 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 with real budget decay within six months when the implementing team moved on. Sustainability isn't a technology question — it's an ownership and culture question.

Three things I've learned the hard way: first, every scanner must have a named owner responsible for calibration and triage — "the security team" is not an owner. Second, custom Semgrep rules and .trivyignore files must be in version control with a review process, not managed by a single person. Third, the feedback loop must be fast enough to feel useful: if a developer has to wait 20 minutes to know whether their PR has a security finding, they stop waiting. Tool speed isn't an option — it's an adoption requirement.

The ProductSecurity.it team has designed and implemented security automation in CI/CD pipelines for organizations producing enterprise SaaS, embedded IoT products, and critical systems. If you want to build a real program — not a set of installed-and-ignored tools, but a mechanism that actually reduces your risk surface — start by assessing your current posture.

Valuta la Tua Postura di Product Security

Assess Your Product Security Posture

La security automation è solo uno dei pilastri. Scopri come si posiziona la tua organizzazione sull'intera postura di Product Security.

Security automation is just one pillar. Discover how your organization ranks across the full Product Security posture.

Passo 1 di 2 — Verifica dell'Ambito

Verifichiamo prima se la tua organizzazione è nel perimetro di applicazione della Product Security e del CRA.