Analisi statica e audit con ShellCheck
Integra ShellCheck in un controllo di sicurezza e interpreti i risultati per rendere più robusto ogni script.
Analisi statica e audit con ShellCheck è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.
Che cos'è ShellCheck e perché è importante
ShellCheck è uno strumento open source di analisi statica per gli script shell. Analizza il codice sorgente Bash (oltre a POSIX sh, dash e ksh) senza eseguirlo e segnala bug, costrutti non sicuri, problemi di portabilità ed errori di stile; ogni segnalazione è contrassegnata da un codice di regola univoco, come SC2086.
In una pipeline con sicurezza rafforzata, ShellCheck funge da controllo obbligatorio: nessuno script viene rilasciato finché non lo supera. Questo è importante perché:
- molte vulnerabilità della shell (suddivisione delle parole, injection, espansioni senza virgolette) non sono visibili nei test dei casi normali, ma si attivano con input controllato da un aggressore;
- ShellCheck rileva queste classi di bug prima dell'esecuzione, senza costi;
- documenta perché ogni modello è pericoloso, aumentando nel tempo la consapevolezza del team.
Lo installi su qualsiasi sistema:
# Debian / Ubuntu
sudo apt-get install shellcheck
# macOS (Homebrew)
brew install shellcheck
# From source via Cabal (any platform)
cabal update && cabal install ShellCheck
# Verify
shellcheck --versionEseguire ShellCheck per la prima volta
L'invocazione più semplice è shellcheck <script>. ShellCheck legge la riga shebang per determinare il dialetto della shell, quindi emette i risultati su stdout.
Ogni risultato include:
- Numero del file e della riga — la posizione esatta
- Gravità —
error,warning,infoostyle - Codice SC — identificativo stabile della regola, che è possibile consultare o disattivare
- Spiegazione comprensibile — indica che cosa non va e spesso anche come correggerlo
Esegua lo script seguente e osservi l'output che ShellCheck produrrebbe:
#!/usr/bin/env bash
# demo_bad.sh — intentionally flawed for ShellCheck demonstration
FILE=$1
if [ $FILE == '' ]; then
echo "No file given"
fi
cat $FILE | grep 'error' | wc -lInterpretare l'output di ShellCheck e i codici SC
Per lo script della scena precedente, ShellCheck emetterebbe risultati come questi:
SC2086(warning) — Inserisca le virgolette doppie per impedire l'espansione dei caratteri jolly e la suddivisione delle parole — su$FILEin[ $FILE == '' ]e incat $FILE.SC2039/SC3010(info) — == in [ ] è un bashism; utilizzi = per POSIX.SC2002(style) — cat non necessario. Valuti cmd < file invece di cat file | cmd.
Ogni codice SC rimanda a una pagina wiki all'indirizzo https://www.shellcheck.net/wiki/SCxxxx, con la motivazione e un esempio corretto.
La versione corretta dello script:
#!/usr/bin/env bash
# demo_fixed.sh — ShellCheck-clean
FILE="$1"
if [ -z "$FILE" ]; then
echo "No file given" >&2
exit 1
fi
grep -c 'error' "$FILE"Livelli di gravità e interventi necessari
ShellCheck classifica ogni risultato in base alla gravità. In un controllo di sicurezza, dovrebbe gestirli come segue:
- error — quasi certamente un bug o una falla di sicurezza. Blocchi la build. Corregga immediatamente. Esempi:
SC2148, shebang mancante, eSC2070,$?senza virgolette. - warning — modello ad alto rischio, spesso sfruttabile. Blocchi la build. Corregga o motivi esplicitamente la disattivazione. Esempio:
SC2086, variabile senza virgolette. - info — probabilmente corretto oggi, ma fragile o non portabile. Lo corregga nella stessa PR, a meno che non sia stato escluso dall'ambito.
- style — preferenza estetica o POSIX. Raccomandato ma facoltativo in una codebase esclusivamente Bash.
Utilizzi --severity=warning per ottenere un codice di uscita diverso da zero solo in presenza di warning o risultati più gravi: è la soglia standard per il controllo di sicurezza:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"Integrare ShellCheck come controllo di sicurezza CI
Un controllo di sicurezza è utile solo quando è obbligatorio e automatizzato. Il modello seguente integra ShellCheck in un passaggio CI che:
- trova ogni file
.shnel repository; - esegue ShellCheck con
--severity=warninge output JSON leggibile dalle macchine; - interrompe la pipeline (
exit 1) se è presente anche solo un risultato; - stampa un riepilogo, così gli sviluppatori possono intervenire sui risultati senza uscire dal log CI.
Inserisca questo file nel repository e lo richiami dalla pipeline CI (GitHub Actions, Jenkins, GitLab CI e così via):
#!/usr/bin/env bash
# ci/shellcheck_gate.sh
set -euo pipefail
SCRIPTS=$(find . -name '*.sh' -not -path './.git/*')
FAILED=0
for script in $SCRIPTS; do
echo "==> Checking: $script"
if ! shellcheck --severity=warning --format=tty "$script"; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo "[GATE] ShellCheck found warnings or errors. Build blocked." >&2
exit 1
fi
echo "[GATE] All scripts passed ShellCheck."La famiglia SC2086: espansioni di variabili senza virgolette
SC2086 è il risultato più comune di ShellCheck e una delle vulnerabilità della shell più sfruttate: le espansioni di variabili senza virgolette.
Quando una variabile non è racchiusa tra virgolette doppie, la shell esegue la suddivisione delle parole (in base a IFS) e l'espansione dei caratteri jolly sul suo valore. Un aggressore che controlla la variabile può iniettare argomenti aggiuntivi, attivare l'attraversamento del filesystem o fare in modo che i comandi ricevano operandi imprevisti.
Modello classico pericoloso:
#!/usr/bin/env bash
# Attacker sets: FILENAME="important.txt /etc/passwd"
FILENAME="$1"
# UNSAFE — word splitting turns this into two args
rm $FILENAME
# SAFE — double quotes prevent splitting
rm "$FILENAME"
# Arrays are the right tool for lists
FILES=("$@")
rm -- "${FILES[@]}"Rilevare il rischio di command injection con SC2046 e SC2035
Due regole meno note ma fondamentali riguardano la command injection tramite l'output delle subshell:
SC2046— Inserisca le virgolette per impedire la suddivisione delle parole e l'espansione dei caratteri jolly all'interno di$(…). Se l'output di una subshell viene utilizzato senza virgolette, qualsiasi spazio o carattere jolly presente nell'output diventa un token della shell.SC2035— Utilizzi./*.shinvece di*.shper evitare che i nomi di file che iniziano con-vengano interpretati come opzioni (un classico vettore di injection degli argomenti).
Scenario concreto di sfruttamento e correzione:
#!/usr/bin/env bash
# SC2046 example — output of find fed unquoted to chmod
# If a filename contains spaces, extra arguments appear
# UNSAFE
chmod 600 $(find /secrets -name '*.key')
# SAFE — use a while-read loop or xargs with -0
find /secrets -name '*.key' -print0 \
| xargs -0 chmod 600
# SC2035 example
# UNSAFE — a file named '-rf' would be passed as an option
rm *.sh
# SAFE
rm -- ./*.shUtilizzare il formato di output JSON per l'automazione
ShellCheck supporta diversi formati di output tramite --format:
tty(predefinito) — output del terminale leggibile dalle personejson— leggibile dalle macchine; ideale per dashboard, controlli personalizzati o caricamento su piattaforme SASTgcc— compatibile con gli strumenti che analizzano il formato degli errori GCC (IDE, Vim/Emacs)checkstyle— formato XML utilizzato dal plugin Checkstyle di Jenkins
Il formato JSON consente di scrivere policy automatizzate, ad esempio bloccando solo codici SC specifici o aggregando i risultati di una codebase estesa in un report di sicurezza.
#!/usr/bin/env bash
# Emit JSON and filter for only error-severity findings using jq
shellcheck --format=json scripts/deploy.sh \
| jq '[.[] | select(.level == "error")]'
# Count distinct SC codes across all scripts
find . -name '*.sh' -print0 \
| xargs -0 shellcheck --format=json 2>/dev/null \
| jq '[.[] | .code] | group_by(.) | map({code: .[0], count: length}) | sort_by(-.count)'Disattivare correttamente i falsi positivi
Disabilitare ShellCheck indiscriminatamente ne vanifica lo scopo. L'approccio corretto consiste in una disattivazione mirata e documentata, che interessi solo la riga o il blocco esatto in cui il risultato non è realmente applicabile.
Tre meccanismi di disattivazione:
- Disattivazione inline —
# shellcheck disable=SC2086sulla riga precedente al codice problematico. Influisce solo su quella riga. - Disattivazione/riattivazione di un blocco — racchiuda una sezione tra
# shellcheck disable=…e# shellcheck enable=…. - Direttiva a livello di file — inserisca
# shellcheck disable=…all'inizio del file (raramente giustificata; documenti il motivo).
Ogni disattivazione deve includere un commento che spieghi perché il risultato è un falso positivo:
#!/usr/bin/env bash
# deploy.sh
# Legitimate suppression: $DEPLOY_ARGS is intentionally word-split
# because it is a pre-validated list of flags from a trusted config file.
# shellcheck disable=SC2086
exec deploy-tool $DEPLOY_ARGS
# Block suppression for a section that generates dynamic code
# shellcheck disable=SC2016
VARS='$HOME $PATH $USER'
echo "Unexpanded vars: $VARS"
# shellcheck enable=SC2016Configurare ShellCheck tramite .shellcheckrc
Per le impostazioni a livello di progetto, ShellCheck legge .shellcheckrc dalla directory dello script risalendo fino a /. Ciò consente di evitare la ripetizione dei flag a ogni invocazione e mantiene semplici gli script del controllo.
Direttive utili in .shellcheckrc:
shell=bash— sovrascrive il rilevamento del dialetto (utile per i file privi di shebang)enable=all— attiva i controlli opzionali (ad esempioavoid-nullary-conditions,require-variable-braces)disable=SC2059— disattivazione a livello di progetto per un'eccezione giustificataexternal-sources=true— segue e controlla le direttivesource/.
# .shellcheckrc — project root
shell=bash
enable=all
external-sources=true
# SC2312: consider invoking this command separately to avoid masking its
# return value — suppressed project-wide because we use set -e.
# Rationale: errexit already aborts on failure; masking risk is mitigated.
disable=SC2312Script consolidato end-to-end: prima e dopo
Il modo più efficace per interiorizzare i risultati di ShellCheck consiste nel rifattorizzare uno script realistico, portandolo da uno stato che fallisce i controlli a uno stato corretto e consolidato. Lo script seguente esegue il backup di una directory ed è stato scritto senza considerare la sicurezza. Fallisce ShellCheck su almeno cinque regole distinte.
Studi entrambe le versioni. La versione dopo supera shellcheck --severity=warning senza direttive di disattivazione ed è significativamente più sicura in presenza di input controllato da un aggressore:
#!/usr/bin/env bash
# BEFORE — multiple ShellCheck violations
DEST=$1
SRC=$2
DATE=`date +%Y%m%d`
if [ ! -d $DEST ]; then
mkdir $DEST
fi
cp -r $SRC $DEST/$DATE
echo Done
#!/usr/bin/env bash
# AFTER — ShellCheck-clean and hardened
set -euo pipefail
DEST="${1:?Usage: backup.sh <dest> <src>}"
SRC="${2:?Usage: backup.sh <dest> <src>}"
DATE=$(date +%Y%m%d)
if [ ! -d "$DEST" ]; then
mkdir -p -- "$DEST"
fi
cp -r -- "$SRC" "$DEST/$DATE"
echo 'Done' >&2Verifica delle conoscenze: ShellCheck in un controllo di sicurezza
Verifichi la propria comprensione del ruolo di ShellCheck come controllo di sicurezza.
Riepilogo: l'analisi statica come controllo di sicurezza
In questa lezione ha imparato a rendere ShellCheck un controllo di sicurezza obbligatorio nel flusso di lavoro Bash:
- ShellCheck esegue l'analisi statica senza eseguire gli script, rilevando prima del runtime errori nelle virgolette, rischi di injection e modelli non sicuri.
- Ogni risultato include un codice SC (ad esempio
SC2086) collegato a documentazione dettagliata e indicazioni per la correzione. - La scala di gravità —
error,warning,info,style— consente di calibrare il controllo:--severity=warningè la soglia di sicurezza raccomandata. - Utilizzi un output leggibile dalle macchine (
--format=json) per automatizzare la reportistica, monitorare le tendenze e integrare SAST. - Disattivi con parsimonia: punti sempre a una singola riga, documenti sempre il motivo in un commento e non disattivi mai globalmente, salvo giustificazione in
.shellcheckrc. - Affianchi ShellCheck a
set -euo pipefail, virgolette esplicite, terminatori-- argumente convalida degli input per una difesa a più livelli.
Uno script che supera ShellCheck non è automaticamente sicuro, ma uno script che non supera ShellCheck non dovrebbe mai arrivare in produzione.
Impara DevOps Bootcamp con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 142
- Lezioni
- 568
Domande Frequenti
La lezione «Analisi statica e audit con ShellCheck» è gratuita?
Sì — il testo completo di «Analisi statica e audit con ShellCheck» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.
Cosa imparerò in «Analisi statica e audit con ShellCheck»?
Integra ShellCheck in un controllo di sicurezza e interpreti i risultati per rendere più robusto ogni script. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare DevOps Bootcamp?
Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Analisi statica e audit con ShellCheck»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?
Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Prevenire l'iniezione di comandi e argomenti
- Gestire i segreti in sicurezza e mantenere pulito l'ambiente
- Esecuzione con privilegi minimi e disciplina nell'uso di sudo
- Analisi statica e audit con ShellCheck