Ogni riga di codice che arriva in produzione è stata scritta da qualcuno che credeva fosse corretta. L'analisi statica del codice è il sistema automatizzato che verifica se tale convinzione fosse fondata, non eseguendo il codice, ma leggendone la struttura, tracciandone i flussi di dati e confrontandolo con modelli noti di vulnerabilità, bug e violazioni della qualità. La questione non è se eseguirla, ma in quale punto della pipeline eseguirla, quale strumento utilizzare, quali soglie imporre e come garantire un feedback sufficientemente rapido da indurre gli sviluppatori ad agire di conseguenza, anziché ignorarlo.
La differenza tra l'analisi statica intesa come semplice attività di verifica e l'analisi statica come vero e proprio controllo di qualità sta nella configurazione. Uno scanner che si limita a eseguire un'esecuzione e a produrre un report che nessuno legge è pura finzione. Un controllo di qualità che blocca una build quando vengono introdotte nuove vulnerabilità critiche, che impedisce un merge quando la copertura del codice scende al di sotto di una determinata soglia e che fornisce un feedback preciso su file e righe nell'interfaccia di revisione delle pull request, è un sistema che modifica il proprio comportamento nel momento stesso in cui vengono prese le decisioni.
In quale punto della pipeline eseguire l'analisi statica
L'analisi statica dovrebbe essere eseguita in più punti, ognuno con uno scopo diverso. Eseguirla in un solo punto crea punti ciechi; eseguirla ovunque in egual misura crea flussi di lavoro lenti che gli sviluppatori sono costretti a bypassare.
| Fase di pipeline | Cosa correre | Budget di latenza | Missione |
|---|---|---|---|
| Pre-conferma (locale) | Solo linter veloci (ESLint, Clippy, rugginefmt) | Sotto 5 secondi | Elimina i problemi evidenti prima che vengano segnalati. |
| Richiesta di pull/richiesta di merge | Analisi completa del linting + SAST + scansione incrementale SonarQube | Meno di 3 minuti | Blocchi si fondono su nuove questioni critiche |
| Compilazione del ramo principale | Analisi completa, inclusi copertura, duplicazione e debito tecnico. | Meno di 10 minuti | Monitora le tendenze qualitative e aggiorna le dashboard. |
| Programmato ogni notte | Scansioni approfondite, audit delle dipendenze, controlli di conformità | Nessun budget | Individuare i problemi di sviluppo lento e i rischi della catena di approvvigionamento |
La fase più importante è la pull request . L'analisi che viene eseguita al momento del push sul ramo principale arriva dopo che è stata presa la decisione di unire le modifiche. L'analisi che viene eseguita su una pull request e che include commenti in linea con un contesto preciso di file e riga arriva quando lo sviluppatore sta ancora lavorando sul codice e il costo della correzione è minimo.
Eseguire analisi approfondite prima di ogni salvataggio compromette l'esperienza di sviluppo. I linter veloci che si completano in meno di 5 secondi sono più adatti a questo scopo. Tutto il resto appartiene alla CI (Continuous Integration).
Azioni GitHub: Completamento della configurazione funzionante
GitHub Actions è la piattaforma CI più utilizzata. Il flusso di lavoro seguente esegue una pipeline di qualità a più livelli: controllo della formattazione, analisi statica del codice (linting), compilazione, scansione di sicurezza SAST e controllo di qualità SonarCloud.
YAML
# .github/workflows/quality.yml
name: Code Quality
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
# ── Layer 1: Fast checks (under 60 seconds) ─────────────────────────────
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- name: Lint (fail on warnings)
run: npx eslint src/ --max-warnings 0
- name: Format check
run: npx prettier --check src/
- name: TypeScript type check
run: npx tsc --noEmit
# ── Layer 2: Security SAST (Semgrep) ────────────────────────────────────
sast:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST scan
uses: semgrep/semgrep-action@v1
with:
config: p/javascript p/nodejs p/owasp-top-ten
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# ── Layer 3: SonarCloud quality gate ────────────────────────────────────
sonar:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history required for blame data
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage --coverageReporters=lcov
- name: SonarCloud scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
proprietà
# sonar-project.properties
sonar.projectKey=my-org_my-project
sonar.organization=my-org
sonar.sources=src
sonar.tests=tests
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=**/*.test.js,**/*.spec.js
Decisioni di configurazione chiave in questo flusso di lavoro:
--max-warnings 0Su ESLint: qualsiasi avviso indica un errore di compilazione. I team che consentono la visualizzazione degli avvisi li accumulano finché nessuno li legge più.fetch-depth: 0Al momento del checkout: SonarCloud necessita della cronologia completa di Git per attribuire la responsabilità dei problemi introdotti e calcolare correttamente le metriche del nuovo codice.- Semgrep viene eseguito dopo lint in parallelo con Sonar: la sicurezza e la scansione di qualità sono aspetti indipendenti; eseguirle in parallelo riduce il tempo totale della pipeline.
- Test eseguiti con
lcovOutput di copertura: SonarCloud legge questo output per mostrare la copertura per file e applicare le soglie di copertura nel quality gate.
GitLab CI/CD: Pipeline di qualità con controlli di qualità
YAML
# .gitlab-ci.yml
stages:
- lint
- test
- analyze
- security
variables:
SONAR_HOST_URL: "https://sonarcloud.io"
lint:
stage: lint
image: node:20
cache:
paths: [node_modules/]
script:
- npm ci
- npx eslint src/ --max-warnings 0
- npx prettier --check src/
- npx tsc --noEmit
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == "main"
test:
stage: test
image: node:20
script:
- npm ci
- npm test -- --coverage --coverageReporters=lcov cobertura
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
sonarcloud:
stage: analyze
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
cache:
key: "${CI_JOB_NAME}"
paths: [.sonar/cache]
script:
- sonar-scanner
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == "main"
semgrep:
stage: security
image: returntocorp/semgrep:latest
script:
- semgrep scan --config=p/owasp-top-ten --config=p/javascript
--sarif --output=semgrep.sarif src/
artifacts:
reports:
sast: semgrep.sarif
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == "main"
Migliori artifacts: reports: sast: Il blocco integra l'output SARIF di Semgrep direttamente nella dashboard di sicurezza di GitLab, dove i risultati vengono visualizzati nel report di sicurezza della richiesta di merge anziché come output grezzo del log CI.
Jenkins: Pipeline dichiarativa con SonarQube
Groovy
// Jenkinsfile
pipeline {
agent any
tools {
nodejs "NodeJS-20"
}
environment {
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Lint') {
steps {
sh 'npm ci'
sh 'npx eslint src/ --max-warnings 0'
sh 'npx tsc --noEmit'
}
}
stage('Test') {
steps {
sh 'npm test -- --coverage --coverageReporters=lcov'
}
post {
always {
publishHTML([
allowMissing: false,
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh '''
npx sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.sources=src \
-Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
'''
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}
post {
failure {
emailext(
subject: "Quality Gate FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Build failed quality gate. Review: ${env.BUILD_URL}",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
waitForQualityGate abortPipeline: true Questa è la riga critica. Interroga SonarQube fino al completamento dell'analisi e interrompe la build se il quality gate non viene soddisfatto. Senza di essa, la pipeline si completa mentre l'analisi è ancora in corso e il risultato del quality gate non viene mai applicato.
Configurare i controlli di qualità che i team rispettano davvero
Un sistema di controllo qualità che fallisce a ogni commit perché le soglie sono impostate in modo troppo restrittivo viene disabilitato. Un sistema che non fallisce mai perché le soglie sono troppo permissive non offre alcun valore. L'obiettivo è un sistema di controllo calibrato sul profilo di rischio effettivo del progetto.
Configurazione iniziale consigliata per un nuovo quality gate di SonarQube:
| Metrico | Condizioni dell'oggetto | Soglia |
|---|---|---|
| Nuovi bug | Maggiore di | 0 |
| Nuove vulnerabilità | Maggiore di | 0 |
| Analisi dei nuovi punti critici per la sicurezza. | Meno di | 100% |
| Nuova copertura del codice | Meno di | 80% |
| Nuove linee duplicate | Maggiore di | 3% |
| Il nuovo codice puzza | Maggiore di | 10 |
Applica queste soglie solo al nuovo codice (il periodo "nuovo codice" in SonarQube). Non applicarle all'intero codice sorgente di un progetto legacy, poiché il fallimento di ogni build a causa del debito tecnico accumulato in cinque anni porta i team a disabilitare completamente il gate. L'approccio basato sul nuovo codice consente di arrestare l'emorragia mentre il debito esistente viene affrontato separatamente.
Regolate gradualmente le soglie nel tempo. Iniziate con soglie conservative, poi inaspritele ogni trimestre man mano che il team smaltisce l'arretrato. Un obiettivo che oggi supera il controllo con una copertura del 75% può essere aggiornato all'80% una volta che la linea di base è nettamente superiore a tale valore. Un inasprimento graduale è più sostenibile rispetto al tentativo di raggiungere la perfezione in modo repentino.
Gestione delle prestazioni dell'analisi statica in codebase di grandi dimensioni
Il motivo più comune per cui i team disabilitano o aggirano l'analisi statica nella CI è che rallenta eccessivamente le pipeline. Una scansione di qualità di 15 minuti a ogni push su un ramo di funzionalità interrompe il flusso di lavoro degli sviluppatori. Diverse strategie consentono di mantenere l'analisi veloce:
Analisi incrementale (solo per il nuovo codice). SonarQube e la maggior parte degli strumenti di analisi statica aziendali supportano l'analisi dei soli file modificati in una pull request anziché dell'intero codice sorgente. Configura sonar.pullrequest.base, sonar.pullrequest.branche sonar.pullrequest.key Parametri per abilitare la scansione incrementale specifica per PR. L'analisi completa viene comunque eseguita al momento dell'unione al ramo principale; la scansione delle PR viene eseguita in meno di 2 minuti sulla maggior parte delle codebase.
Memorizzazione nella cache. La parte più onerosa di molte analisi statiche è il download dello strumento e delle relative definizioni delle regole. Memorizza nella cache il file binario dello scanner, il database delle regole e tutte le dipendenze risolte tra un'esecuzione e l'altra:
YAML
# GitHub Actions: cache SonarCloud scanner
- name: Cache SonarCloud packages
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
Parallelismo. L'analisi del codice sorgente, la scansione di sicurezza e l'analisi del controllo qualità sono attività indipendenti. Eseguitele in parallelo anziché in sequenza. Il tempo totale della pipeline è determinato dal processo più lento, non dalla somma di tutti i processi.
Attivazione selettiva degli strumenti. Non tutti gli strumenti devono essere eseguiti su ogni ramo. È possibile eseguire scansioni di sicurezza complete e controlli di conformità sulle pull request e sul ramo principale, mentre sui rami pre-merge vengono eseguiti solo i linter veloci. Utilizzare i filtri di ramo nella configurazione CI per applicare profili di analisi differenti.
Analisi statica nelle pipeline orientate alla sicurezza: SAST e analisi delle dipendenze
Le pipeline incentrate sulla sicurezza aggiungono due livelli alla normale analisi della qualità: SAST (Static Application Security Testing) per le vulnerabilità del codice dell'applicazione e la scansione delle dipendenze per individuare CVE note nelle librerie di terze parti.
Gli strumenti SAST (Semgrep, CodeQL, Snyk Code, Checkmarx) analizzano il flusso di dati non attendibili attraverso il codice dell'applicazione per raggiungere operazioni sensibili. Rilevano SQL injection, XSS, command injection, deserializzazione non sicura e credenziali hardcoded che i linter di qualità standard non sono in grado di individuare.
YAML
# GitHub Actions: CodeQL analysis for deep vulnerability detection
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:javascript-typescript"
Scansione delle dipendenze controlli package.json, pom.xml, requirements.txte file manifest equivalenti rispetto ai database delle vulnerabilità:
YAML
# Dependency audit in the pipeline
- name: Dependency audit
run: |
npm audit --audit-level=high
# Fail if high or critical vulnerabilities are found
Il modello di sicurezza a livelli esegue un rapido SAST basato su pattern (Semgrep) su ogni pull request per un feedback immediato da parte degli sviluppatori e un SAST semantico approfondito (CodeQL) a intervalli programmati per una copertura completa. La scansione delle dipendenze viene eseguita su ogni build poiché vengono pubblicate quotidianamente nuove vulnerabilità CVE.
Errori comuni nell'integrazione e come evitarli
Eseguire l'analisi solo sul ramo principale. Il valore dell'analisi statica sta nell'individuare i problemi prima che vengano uniti, non dopo. Un'analisi eseguita solo dopo l'unione serve come report, non come controllo.
Impostare i controlli di qualità in modo che emettano un avviso anziché generare un errore. Un avviso che non blocca la pipeline è una notifica che gli sviluppatori imparano a ignorare. I controlli che non generano errori non contribuiscono a costruire una cultura della qualità.
Non escludere i file di test dalle soglie di copertura. Testare il codice che testa altro codice di test produce numeri di copertura gonfiati. Escludere le directory di test dai calcoli di copertura per ottenere misurazioni accurate della copertura del codice di produzione.
Ignorare l'arretrato di analisi sui codebase legacy. Avviare SonarQube per la prima volta su un codebase legacy di un milione di righe e trovarsi di fronte a 40,000 problemi è demotivante e controproducente. Utilizzare l'approccio della baseline del nuovo codice: definire una data di inizio per il "nuovo codice", applicare i gate solo al codice scritto dopo tale data e trattare l'arretrato di problemi esistente come un programma separato di riduzione del debito tecnico.
Non collegare i risultati nei commenti della pull request. I risultati dell'analisi pubblicati su una dashboard separata che gli sviluppatori devono visitare vengono ignorati. I risultati dell'analisi pubblicati come commenti in linea sulle righe specifiche che hanno introdotto problemi, all'interno dell'interfaccia di revisione della pull request, vengono presi in considerazione. Configura sonar.pullrequest.* parametri per l'integrazione con GitHub, GitLab o Bitbucket in modo che i risultati vengano visualizzati dove avviene la revisione del codice.
Come SMART TS XL Si integra con le pipeline CI/CD
La maggior parte degli strumenti di analisi statica nelle pipeline CI/CD considera un solo linguaggio alla volta. ESLint considera JavaScript. SonarQube considera Java, Python, JavaScript e C#. Nessuno di questi considera COBOL, JCL, RPG o le dipendenze tra linguaggi diversi che collegano un programma batch su mainframe al microservizio Java che ne elabora l'output, il quale a sua volta si connette al frontend JavaScript che lo visualizza.
SMART TS XL si integra nelle pipeline CI/CD come livello di analisi statica cross-language che copre simultaneamente ogni linguaggio nell'ambiente aziendale. Quando un servizio Java viene modificato, SMART TS XL'S analisi d'impatto Traccia il grafo delle dipendenze per identificare quali programmi COBOL, schemi di database e servizi a valle sono interessati da tale modifica, prima che questa venga implementata. Quando un copybook COBOL viene modificato, identifica ogni programma nell'intero portfolio applicativo che include tale copybook e che necessita di convalida.
Questa consapevolezza della dipendenza interlinguistica è la capacità che rende SMART TS XLL'integrazione CI/CD di SonarQube è diversa dall'aggiunta di un altro linguaggio di programmazione. Non si tratta solo di analizzare il codice del file modificato, ma di comprendere quali altri elementi del sistema saranno influenzati da tale modifica. Questa è l'analisi architetturale che determina la reale portata di una modifica prima che venga implementata.
Per i team di sviluppo aziendale che operano su stack legacy e moderni, SMART TS XL'S Integrazione DevOps Questa funzionalità integra l'analisi strutturale nel flusso di lavoro di revisione della pipeline, fornendo la visibilità architetturale che gli strumenti specifici per un singolo linguaggio non possono offrire. Il risultato sono dei controlli di qualità che impongono standard non solo all'interno di un linguaggio specifico, ma in tutto il sistema, garantendo che una modifica a qualsiasi componente non introduca guasti imprevisti nei componenti che dipendono da esso.
L'analisi statica come cittadino permanente della pipeline
I team che implementano correttamente l'analisi statica la trattano allo stesso modo dei test: non come una fase da superare, ma come una pratica continua integrata in ogni commit. L'analisi viene eseguita su ogni pull request. I risultati del quality gate vengono visualizzati in linea con la revisione del codice. Le soglie si inaspriscono man mano che la codebase migliora. Nuove categorie di analisi vengono aggiunte con la crescita della maturità del team in termini di sicurezza e qualità.
I modelli di configurazione della pipeline presentati in questo articolo sono punti di partenza. Gli strumenti specifici, le soglie e le condizioni di gate devono essere calibrati in base allo stack linguistico del progetto, al profilo di rischio e alla maturità del team. Ciò che non deve variare è il principio: un'analisi che non prevede un gate per le fusioni non modifica il comportamento, e il comportamento è proprio ciò che i programmi di qualità del codice cercano di modificare.


