08 - DevSecOps per Developer: SAST, DAST e Sicurezza nella CI/CD Pipeline
Nel febbraio 2024, un ricercatore di sicurezza ha dimostrato come il compromesso di tj-actions/changed-files, una GitHub Action usata da oltre 23.000 repository, avesse permesso l'esfiltrazione di secrets da pipeline CI/CD di migliaia di organizzazioni. Il problema non era nel codice dell'applicazione, non era nelle dipendenze, era nella pipeline stessa: lo strumento che avrebbe dovuto proteggere il software era diventato il vettore di attacco.
Questo scenario riflette una verita scomoda: la maggior parte dei team investe nella sicurezza del codice applicativo ma trascura la sicurezza dell'infrastruttura CI/CD che lo costruisce, testa e distribuisce. DevSecOps non e solo integrare qualche tool di scansione nella pipeline, ma un cambio di approccio fondamentale: la sicurezza deve essere parte integrante di ogni fase del ciclo di sviluppo, non un controllo finale prima del rilascio.
Secondo il rapporto DORA 2025, i team che adottano pratiche DevSecOps complete riportano un Mean Time to Remediate (MTTR) per vulnerabilità critiche di 4 volte inferiore rispetto ai team che eseguono security test solo in fase di staging o produzione. Il costo di correzione di un bug di sicurezza in produzione e mediamente 100 volte superiore rispetto a correggerlo in fase di sviluppo: il principio "shift-left" ha una giustificazione economica solida, oltre che tecnica.
Cosa Imparerai
- Shift-left security: integrare la sicurezza dall'inizio del ciclo di sviluppo
- SAST con Semgrep e CodeQL: analisi statica del codice sorgente
- DAST con OWASP ZAP: test dinamici su applicazioni in esecuzione
- SCA con Snyk e Trivy: Software Composition Analysis per dipendenze
- Secrets scanning con Gitleaks e TruffleHog: prevenire la fuga di credenziali
- IaC security con Checkov: scansione di Terraform e Kubernetes manifests
- Pipeline GitHub Actions completa con security gates
- Metriche e KPI per misurare la maturita DevSecOps
Shift-Left Security: il Principio Fondamentale
Il termine "shift-left" deriva dalla rappresentazione visiva del ciclo di sviluppo come linea temporale da sinistra (pianificazione, sviluppo) a destra (test, produzione). Spostare la sicurezza a sinistra significa portare i controlli di sicurezza il più vicino possibile al momento in cui il codice viene scritto: nell'IDE del developer, nel pre-commit hook, nella pull request, non solo nell'ambiente di staging.
I livelli di shift-left security, dalla più reattiva alla più proattiva, sono:
- Livello 0 - Produzione: vulnerability scanning solo in prod. Tardivo, costoso.
- Livello 1 - Staging/Pre-prod: DAST in ambiente di test. Meglio, ma ancora lento.
- Livello 2 - CI/CD Pipeline: SAST, SCA, secrets scanning su ogni push. Standard DevSecOps.
- Livello 3 - Pull Request: security review automatica su ogni PR. Veloce e contestuale.
- Livello 4 - Pre-commit: controlli locali prima del commit. Feedback immediato.
- Livello 5 - IDE: plugin di sicurezza nell'editor (Snyk IDE, CodeQL extension). Real-time.
Il Paradosso del 45%
Ricerche di Veracode e GitLab mostrano che il 45% del codice generato da strumenti AI (GitHub Copilot, Cursor, Claude Code) presenta vulnerabilità di sicurezza che fallirebbero i security test di base. Non perchè l'AI sia intrinsecamente pericolosa, ma perchè replica pattern di codice insicuro presenti nei dataset di training. Questo rende i controlli automatizzati nel CI/CD ancora più critici nell'era del vibe coding e dell'AI-assisted development. Vedi la serie Vibe Coding per approfondire questo tema.
SAST: Static Application Security Testing
SAST analizza il codice sorgente, bytecode o binario senza eseguire l'applicazione, cercando pattern di codice vulnerabile, misconfigurazioni e anti-pattern di sicurezza. E il controllo più veloce (eseguibile in secondi o minuti) e può essere integrato direttamente nell'IDE, nel pre-commit hook e nella CI/CD pipeline.
I principali vantaggi di SAST rispetto ad altri approcci sono: esegue sul codice sorgente (non richiede un'istanza dell'applicazione in esecuzione), identifica vulnerabilità prima del deploy, e può analizzare il 100% del codice, incluse le code path raramente eseguite. Lo svantaggio principale e il tasso di falsi positivi, che richiede un processo di triage e tuning delle regole.
Semgrep: SAST Open Source e Veloce
Semgrep e probabilmente lo strumento SAST più usato nell'ecosistema open source nel 2025.
La sua forza sta nella sintassi semplice delle regole (struttura quasi identica al codice
che analizza), nella velocità di esecuzione e nel supporto nativo per TypeScript, JavaScript,
Python, Go, Java e molti altri linguaggi. Mantiene migliaia di regole di sicurezza nel
registry ufficiale semgrep.dev.
# Installazione Semgrep
pip install semgrep
# oppure con Homebrew
brew install semgrep
# Scansione base con ruleset OWASP
semgrep --config p/owasp-top-ten .
# Scansione con regole specifiche per JavaScript/TypeScript
semgrep --config p/javascript .
semgrep --config p/typescript .
# Regole per Angular/Node.js
semgrep --config p/nodejs-express-security .
semgrep --config p/jwt .
# Output in formato SARIF per GitHub Security tab
semgrep --config p/owasp-top-ten --sarif --output semgrep.sarif .
# Output JSON per processing automatico
semgrep --config p/owasp-top-ten --json --output semgrep.json .
# Escludi cartelle non rilevanti
semgrep --config p/owasp-top-ten \
--exclude="node_modules,dist,coverage,*.min.js" .
# Regola custom Semgrep per trovare JWT senza verifica della firma
# .semgrep/jwt-insecure.yaml
# rules:
# - id: jwt-decode-without-verify
# patterns:
# - pattern: jwt.decode($TOKEN, ...)
# - pattern-not: jwt.verify($TOKEN, ...)
# message: "Uso di jwt.decode senza verifica: usa jwt.verify"
# severity: ERROR
# languages: [javascript, typescript]
CodeQL: Analisi Semantica Profonda
CodeQL, sviluppato da GitHub, esegue un'analisi più profonda rispetto a Semgrep: costruisce un database del codice e permette di scrivere query in linguaggio SQL-like per trovare vulnerabilità anche attraverso molteplici livelli di chiamate di funzione (taint analysis). LinkedIn ha annunciato nel febbraio 2026 di usare sia CodeQL che Semgrep in modo complementare per la copertura ottimale del codebase. CodeQL e integrato nativamente in GitHub Advanced Security ed e disponibile gratuitamente per repository pubblici.
# .github/workflows/codeql.yml
name: CodeQL Security Analysis
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
# Analisi settimanale (domenica alle 2:00 UTC)
- cron: '0 2 * * 0'
permissions:
contents: read
security-events: write
actions: read
jobs:
codeql-analysis:
name: CodeQL Analysis
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['javascript-typescript']
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Initialize CodeQL
uses: github/codeql-action/init@dd746615b1a4b1e1c5d3b87432fe040f4c04082 # v3.28.0
with:
languages: {{ matrix.language }}
# Query suite: default, extended, security-extended
queries: security-extended
config: |
paths-ignore:
- node_modules
- dist
- '**/*.test.ts'
- '**/*.spec.ts'
- name: Autobuild
uses: github/codeql-action/autobuild@dd746615b1a4b1e1c5d3b87432fe040f4c04082 # v3.28.0
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@dd746615b1a4b1e1c5d3b87432fe040f4c04082 # v3.28.0
with:
category: "/language:{{ matrix.language }}"
upload: true
SAST: Falsi Positivi e Triage
Il problema principale del SAST e il numero di falsi positivi: un ruleset aggressivo può generare centinaia di segnalazioni per ogni commit, rendendo il processo insostenibile. La strategia raccomandata e iniziare con un profilo di severita alta (solo CRITICAL/HIGH), aggiustare le regole per il tuo codebase specifico, e aumentare progressivamente la copertura man mano che il team si familiarizza con lo strumento. Non bloccare mai la build per LOW/MEDIUM senza un processo di triage consolidato.
DAST: Dynamic Application Security Testing
Mentre SAST analizza il codice senza eseguirlo, DAST testa l'applicazione mentre e in esecuzione, simulando attacchi reali dall'esterno come farebbe un attaccante. DAST trova vulnerabilità che SAST non può vedere: problemi di configurazione del server, comportamenti runtime, vulnerabilità nelle API REST, e problemi di autenticazione che emergono solo con richieste HTTP reali.
DAST e complementare a SAST, non sostitutivo: SAST trova il 70-80% delle vulnerabilità nel codice sorgente, DAST trova il 20-30% restante che emerge solo a runtime. Per una copertura completa, entrambi sono necessari.
OWASP ZAP: DAST Standard nell'Ecosistema Open Source
OWASP ZAP (Zed Attack Proxy) e il tool DAST più usato nel mondo open source, con integrazione nativa in GitHub Actions. Offre tre modalità principali per CI/CD: baseline scan (passivo, non invasivo, ideale per ogni PR), full scan (attivo, usa payload di attacco reali, solo in ambienti isolati) e API scan (specializzato per OpenAPI/Swagger, ideale per microservizi).
# .github/workflows/dast-zap.yml
name: DAST Security Scan (OWASP ZAP)
on:
push:
branches: [main]
workflow_dispatch:
inputs:
target_url:
description: 'URL target per la scansione DAST'
required: true
default: 'https://staging.myapp.com'
permissions:
contents: read
issues: write # ZAP crea issues GitHub per le vulnerabilità trovate
jobs:
zap-baseline-scan:
name: OWASP ZAP Baseline Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Baseline scan: scansione PASSIVA (non invia payload di attacco)
# Ideale per ogni PR/push - veloce e non invasivo
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.14.0
with:
target: 'https://staging.myapp.com'
rules_file_name: '.zap/rules.tsv'
fail_action: true
cmd_options: '-a -j -l WARN'
# Full scan: scansione ATTIVA (usa payload di attacco reali)
# Solo in ambienti di test isolati, mai in produzione!
# - name: ZAP Full Scan (staging only)
# uses: zaproxy/action-full-scan@v0.11.0
# with:
# target: 'https://staging-isolated.myapp.com'
# API Scan: specializzato per REST API con OpenAPI spec
# - name: ZAP API Scan
# uses: zaproxy/action-api-scan@v0.9.0
# with:
# target: 'https://api.staging.myapp.com/openapi.json'
- name: Upload ZAP Report
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.0
if: always()
with:
name: zap-report
path: report_html.html
# .zap/rules.tsv - personalizzazione regole ZAP
# ID ACTION PARAM
# 10015 WARN # Incomplete or No Cache-control Header Set
# 10038 IGNORE # CSP Header Not Set (gestito separatamente dal server)
# 10096 WARN # Timestamp Disclosure
# 40014 IGNORE # CSRF (se il token CSRF e implementato lato app)
Per progetti Angular con SSR, il DAST deve essere eseguito su un'istanza raggiungibile dalla pipeline. Una strategia efficace e il deploy su uno staging environment effimero (ad esempio su Firebase Hosting Preview Channel) come parte della pipeline, eseguire il DAST, e procedere con il deploy in produzione solo se il DAST passa.
# Esempio: Deploy staging effimero + DAST in pipeline Angular
# .github/workflows/angular-devsecops.yml (estratto)
jobs:
deploy-staging:
runs-on: ubuntu-latest
outputs:
staging_url: {{ steps.deploy.outputs.details_url }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: npm ci --ignore-scripts
- run: npm run build --configuration=staging
- name: Deploy to Firebase Hosting preview channel
id: deploy
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: {{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: {{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: staging-{{ github.run_id }}
expires: 1d
dast-scan:
needs: deploy-staging
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: ZAP Baseline Scan on ephemeral staging
uses: zaproxy/action-baseline@v0.14.0
with:
target: {{ needs.deploy-staging.outputs.staging_url }}
fail_action: true
rules_file_name: '.zap/rules.tsv'
SCA: Software Composition Analysis
SCA (Software Composition Analysis) analizza le dipendenze open source dell'applicazione per identificare vulnerabilità note (CVE), problemi di licenza e pacchetti obsoleti. L'articolo precedente della serie ( Supply Chain Security: npm audit e SBOM) copre in dettaglio npm audit, Snyk, Dependabot e la generazione di SBOM. In questo contesto ci concentriamo su Trivy, che offre sia SCA che IaC scanning in un unico strumento, ideale per pipeline DevSecOps unificate.
# Trivy: SCA + container + IaC in un unico tool
# Installazione
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
| sh -s -- -b /usr/local/bin
# Scansione dipendenze npm (SCA)
trivy fs --scanners vuln --pkg-types library .
# Solo vulnerabilità HIGH e CRITICAL (per security gate)
trivy fs --scanners vuln --severity HIGH,CRITICAL \
--pkg-types library --exit-code 1 .
# Output SARIF per GitHub Security tab
trivy fs --scanners vuln --format sarif \
--output trivy-results.sarif .
# Scansione con SBOM output (CycloneDX)
trivy fs --format cyclonedx --output sbom.json .
# File .trivyignore per CVE accettati (con motivazione obbligatoria!)
# .trivyignore
# CVE-2023-44270 # postcss: impatto solo in CLI, non nel build output - review 2026-06-01
# Scansione completa: vuln + segreti + misconfigurazioni
trivy fs --scanners vuln,secret,misconfig .
# GitHub Actions con Trivy Action ufficiale
# - name: Run Trivy vulnerability scanner
# uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca
# with:
# scan-type: 'fs'
# scan-ref: '.'
# scanners: 'vuln,secret'
# severity: 'HIGH,CRITICAL'
# exit-code: '1'
# format: 'sarif'
# output: 'trivy-results.sarif'
Secrets Scanning: Prevenire la Fuga di Credenziali
Ogni settimana vengono esposti accidentalmente su GitHub pubblico migliaia di secrets: API keys, token OAuth, password di database, certificati privati. GitGuardian, che monitora i repository pubblici, riporta che nel 2024 sono stati rilevati oltre 12 milioni di secrets esposti su repository pubblici, con un aumento del 28% rispetto all'anno precedente. Una volta che un secret e in un repository pubblico (anche per pochi minuti prima della rimozione), deve essere considerato compromesso e ruotato immediatamente.
Gitleaks e TruffleHog sono i due tool più usati per la secrets detection, con approcci complementari: Gitleaks e veloce e ottimo per CI/CD, TruffleHog verifica attivamente se i secrets trovati sono ancora validi (riduce significativamente i falsi positivi).
Gitleaks: Configurazione e Integrazione
# Installazione Gitleaks
brew install gitleaks
# oppure scarica il binario da GitHub Releases
# https://github.com/gitleaks/gitleaks/releases
# Scansione del repository corrente (tutti i commit)
gitleaks detect --source . --verbose
# Scansione solo dei file staged (per pre-commit hook)
gitleaks protect --staged --verbose
# Scansione di un range di commit specifico
gitleaks detect --source . --log-opts HEAD~1..HEAD
# Output in formato JSON
gitleaks detect --source . --report-format json \
--report-path gitleaks-report.json
# Configurazione pre-commit con pre-commit framework
# .pre-commit-config.yaml
# repos:
# - repo: https://github.com/gitleaks/gitleaks
# rev: v8.24.2
# hooks:
# - id: gitleaks
# Configurazione custom .gitleaks.toml
# [extend]
# useDefault = true
#
# [[rules]]
# id = "internal-api-token"
# description = "Token API interno MyCompany"
# regex = '''mycompany_token_[0-9a-zA-Z]{32}'''
# tags = ["token", "internal"]
#
# [allowlist]
# description = "Test fixtures e placeholder sicuri"
# regexTarget = "line"
# regexes = [
# '''EXAMPLE_TOKEN_PLACEHOLDER''',
# '''test_secret_[a-z0-9]{8}'''
# ]
# paths = [
# '''.*_test\.ts''',
# '''.*\.spec\.ts''',
# '''.*\.fixture\.ts'''
# ]
TruffleHog: Verifica Attiva dei Secrets
# Installazione TruffleHog
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \
| sh -s -- -b /usr/local/bin
# Scansione con verifica attiva (riduce falsi positivi)
trufflehog git file://. --only-verified
# Scansione di un repository remoto
trufflehog github --repo https://github.com/myorg/myrepo --only-verified
# Output JSON per processing
trufflehog git file://. --json --no-verification
# GitHub Actions: secrets scanning su ogni push/PR
# .github/workflows/secrets-scan.yml
# - name: TruffleHog secrets scan
# uses: trufflesecurity/trufflehog@main
# with:
# path: ./
# base: $GITHUB_BASE_REF
# head: HEAD
# extra_args: --only-verified
# Differenza chiave Gitleaks vs TruffleHog:
# - Gitleaks: veloce, pattern matching, ottimo per pre-commit e CI
# - TruffleHog: verifica attiva (testa il secret contro l'API reale)
# Risultato: meno falsi positivi, ma più lento e invasivo
# Raccomandazione: entrambi in pipeline (Gitleaks fast, TruffleHog verified)
I Secrets non si Rimuovono con git rm
Se un secret viene committato per errore, rimuovere il file con git rm o
aggiungere il file a .gitignore non e sufficiente: il secret e ancora
visibile nella storia di git. Per rimuoverlo davvero usa git filter-repo
(strumento ufficialmente raccomandato da GitHub), poi force-push su tutti i branch,
e considera il secret compromesso e ruotalo immediatamente. GitHub offre anche il
tool nativo "Secret scanning" nelle impostazioni del repository che monitora
continuamente la storia dei commit per 200+ tipi di token noti.
IaC Security: Checkov per Terraform e Kubernetes
L'Infrastructure as Code (IaC) ha democratizzato la gestione dell'infrastruttura, ma ha anche introdotto un nuovo vettore di rischio: misconfigurazioni nei file Terraform, Kubernetes manifests, Docker Compose e AWS CloudFormation che possono esporre l'intera infrastruttura. Checkov (Bridgecrew/Palo Alto Networks) scansiona staticamente questi file con oltre 1000 policy di sicurezza integrate, mappate su CIS Benchmarks, NIST e altri standard.
Nota: tfsec, lo strumento originale di Aqua Security per IaC scanning, e stato integrato in Trivy. Per nuovi progetti, si raccomanda Trivy per IaC o Checkov per copertura più ampia.
# Installazione Checkov
pip install checkov
# oppure con Homebrew
brew install checkov
# Scansione directory Terraform
checkov -d ./terraform
# Scansione Kubernetes manifests
checkov -d ./k8s --framework kubernetes
# Scansione Dockerfile
checkov -f Dockerfile --framework dockerfile
# Output in formato SARIF per GitHub Security
checkov -d ./terraform --output sarif --output-file checkov.sarif
# Skippa check specifici (con commento di motivazione!)
checkov -d ./terraform --skip-check CKV_AWS_18,CKV_AWS_21
# Violazioni Terraform AWS comuni che Checkov rileva:
# CKV_AWS_18: S3 bucket - access logging disabilitato
# CKV_AWS_57: S3 bucket - ACL pubblica (mai in produzione!)
# CKV_AWS_87: Lambda - non in VPC
# CKV_AWS_135: EC2 - IMDSv1 abilitato (usa IMDSv2)
# Esempio Kubernetes manifest INSICURO (violazioni Checkov):
# apiVersion: v1
# kind: Pod
# spec:
# containers:
# - name: app
# image: myapp:latest # CKV_K8S_14: usa tag specifico, non :latest
# securityContext:
# runAsRoot: true # CKV_K8S_6: non eseguire come root
# privileged: true # CKV_K8S_16: non usare privileged mode
# resources: {} # CKV_K8S_11: definisci resource limits
# Esempio Kubernetes manifest SICURO (supera Checkov):
# spec:
# containers:
# - name: app
# image: myapp:1.2.3
# securityContext:
# runAsNonRoot: true
# runAsUser: 1000
# allowPrivilegeEscalation: false
# readOnlyRootFilesystem: true
# resources:
# limits:
# memory: "256Mi"
# cpu: "500m"
# requests:
# memory: "128Mi"
# cpu: "250m"
Pipeline GitHub Actions Completa: il Security Gate Definitivo
Integrare tutti questi strumenti in un'unica pipeline coerente richiede una pianificazione attenta: non tutti i job devono bloccare la build, alcune scansioni sono più appropriate per PR mentre altre per push su main, e la parallelizzazione e fondamentale per non rallentare eccessivamente il flusso di sviluppo. La pipeline seguente illustra una configurazione completa con 6 job paralleli e in sequenza.
# .github/workflows/devsecops-complete.yml
name: DevSecOps Security Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 3 * * 1' # Ogni lunedi alle 3:00 UTC (scan completo)
# Permissions minime a livello workflow (principio del minimo privilegio)
permissions:
contents: read
jobs:
# ===== JOB 1: SECRETS SCANNING (primo - fail fast) =====
secrets-scan:
name: Secrets Scanning
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0 # Storia completa per Gitleaks
- name: Gitleaks secrets detection
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: {{ secrets.GITHUB_TOKEN }}
# ===== JOB 2: SCA - Dependency Vulnerabilities =====
sca-scan:
name: SCA Dependency Audit
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: '22'
cache: 'npm'
- run: npm ci --ignore-scripts
- name: npm audit (solo production deps)
run: npm audit --audit-level=high --omit=dev
- name: Trivy SCA scan
uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln'
severity: 'HIGH,CRITICAL'
exit-code: '1'
format: 'sarif'
output: 'trivy-vuln.sarif'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@dd746615b1a4b1e1c5d3b87432fe040f4c04082
if: always()
with:
sarif_file: 'trivy-vuln.sarif'
# ===== JOB 3: SAST - Static Code Analysis =====
sast-scan:
name: SAST Static Analysis
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Initialize CodeQL
uses: github/codeql-action/init@dd746615b1a4b1e1c5d3b87432fe040f4c04082
with:
languages: 'javascript-typescript'
queries: security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@dd746615b1a4b1e1c5d3b87432fe040f4c04082
- name: CodeQL Analysis
uses: github/codeql-action/analyze@dd746615b1a4b1e1c5d3b87432fe040f4c04082
with:
category: "/language:javascript-typescript"
- name: Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/owasp-top-ten
p/javascript
p/typescript
p/jwt
env:
SEMGREP_APP_TOKEN: {{ secrets.SEMGREP_APP_TOKEN }}
# ===== JOB 4: IaC Security =====
iac-scan:
name: IaC Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Trivy config scan
uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca
with:
scan-type: 'config'
scan-ref: '.'
severity: 'HIGH,CRITICAL'
exit-code: '1'
- name: Checkov IaC scan
uses: bridgecrewio/checkov-action@v12
with:
directory: .
framework: all
soft_fail_on: MEDIUM
hard_fail_on: HIGH,CRITICAL
# ===== JOB 5: DAST (solo su push a main) =====
dast-scan:
name: DAST ZAP Baseline
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [secrets-scan, sca-scan, sast-scan]
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.14.0
with:
target: {{ secrets.STAGING_URL }}
fail_action: true
- name: Upload ZAP Report
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
if: always()
with:
name: zap-report-{{ github.sha }}
path: report_html.html
retention-days: 30
# ===== JOB 6: SBOM (su push a main dopo tutti i check) =====
sbom-generate:
name: Generate SBOM
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
needs: [sca-scan, sast-scan, iac-scan]
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: '22'
- run: npm ci --ignore-scripts
- name: Generate SBOM (CycloneDX)
run: |
npm install -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --omit dev \
--output-format json \
--output-file sbom-{{ github.sha }}.json
- name: Attest SBOM
uses: actions/attest-build-provenance@v1
with:
subject-path: sbom-{{ github.sha }}.json
- name: Upload SBOM
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
with:
name: sbom-{{ github.sha }}
path: sbom-{{ github.sha }}.json
retention-days: 365
Security Gates: Cosa Blocca la Build e Cosa no
Un security gate efficace non blocca ogni singolo warning ma distingue tra problemi che richiedono attenzione immediata e problemi che possono essere gestiti nel tempo. La regola base e: blocca solo ciò che e chiaramente pericoloso e actionable. Bloccare la build per troppi falsi positivi o vulnerabilità LOW porta il team a disabilitare i controlli completamente, vanificando l'intero investimento.
Matrice dei Security Gates Raccomandata
- BLOCCA sempre: Secrets nel codice, vulnerabilità CRITICAL con fix disponibile, SAST findings di tipo injection/RCE, IaC misconfigurazioni CRITICAL (S3 bucket pubblico, security group 0.0.0.0/0 in ingress SSH)
- BLOCCA su main (non su PR): Vulnerabilità HIGH con fix disponibile, DAST findings impostati su FAIL, dipendenze con licenze incompatibili (GPL in prodotto commerciale)
- WARNING (notifica ma non blocca): Vulnerabilità HIGH senza fix disponibile, MEDIUM in SAST, IaC HIGH con workaround documentato nel file di eccezioni, DAST findings impostati su WARN
- IGNORA (con documentazione): Vulnerabilità LOW/INFO, falsi positivi documentati nel file di eccezioni (.trivyignore, .gitleaks.toml allowlist), CVE senza impatto reale sul contesto applicativo specifico
# Gestione delle eccezioni documentate nei file di configurazione
# .trivyignore - CVE accettati con motivazione obbligatoria
# Formato: CVE-ID # [data] Motivazione e piano di revisione
CVE-2023-44270 # [2026-02-01] postcss: impatto solo in CLI usage, non nel build output. Review 2026-06
# .gitleaks.toml - allowlist per pattern non pericolosi
# [allowlist]
# description = "Test fixtures, placeholder e valori di esempio"
# regexes = [
# '''EXAMPLE_API_KEY_[A-Z0-9]{16}''',
# '''test_secret_placeholder''',
# '''YOUR_TOKEN_HERE'''
# ]
# paths = [
# '''.*\.spec\.ts''',
# '''.*\.fixture\.ts''',
# '''README\.md'''
# ]
# Semgrep - eccezione inline nel codice sorgente
# Aggiungi commento sulla stessa riga per sopprimere un finding
# const regex = new RegExp(userPattern); // nosemgrep: javascript.lang.security.audit.unsafe-regex
# Checkov - soppressione inline nel file Terraform
# resource "aws_s3_bucket" "cdn_assets" {
# bucket = "my-public-cdn-assets"
# #checkov:skip=CKV_AWS_57: Bucket CDN per assets statici pubblici - progettato per essere pubblico
# }
Checklist Angular: DevSecOps Pipeline
I progetti Angular hanno considerazioni specifiche per il DevSecOps, legate alla toolchain (Angular CLI, esbuild, ng-packagr), all'uso di template HTML con binding dinamico, e al SSR con Node.js server Express.
# Angular DevSecOps Checklist
# 1. SAST: regole Semgrep specifiche per Angular
# Patterns pericolosi da rilevare:
# - DomSanitizer.bypassSecurityTrustHtml(userInput) senza sanitizzazione
# - [innerHTML]="untrustedData" (binding HTML non sicuro)
# - eval() o new Function() in servizi Angular
# - HttpClient senza interceptor di autenticazione
# 2. SECRETS: environment files NON devono contenere valori reali
# SBAGLIATO - environment.ts committato con secret:
# export const environment = {
# apiKey: 'sk-prod-real-key-12345', # Bloccato da Gitleaks!
# googleMapsKey: 'AIza...' # Bloccato da Gitleaks!
# };
# CORRETTO - placeholder nei file committati, valori iniettati in CI:
# export const environment = {
# apiKey: '', // iniettato a build time dalla pipeline CI/CD
# };
# In pipeline: sed -i "s/apiKey: ''/apiKey: '{{ secrets.API_KEY }}'/" src/environments/environment.prod.ts
# 3. CSP HEADER nel server SSR Angular (Express)
# server.ts
# import { randomBytes } from 'crypto';
# const generateNonce = () => randomBytes(16).toString('base64');
#
# app.use((req, res, next) => {
# const nonce = generateNonce();
# res.locals['nonce'] = nonce;
# res.setHeader('Content-Security-Policy', [
# "default-src 'self'",
# `script-src 'self' 'nonce-






