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 Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Linux Command Line & Bash Scripting Mastery 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.
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 Linux Command Line & Bash Scripting Mastery, passa a CoddyKit PRO. Il corso Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery?
Non è richiesta alcuna esperienza precedente. Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery?
Sì. Ogni lezione Linux Command Line & Bash Scripting Mastery 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