0Pricing
Linux Command Line & Bash Scripting Mastery · Lezione

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 --version

Eseguire 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, info o style
  • 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 -l

Interpretare 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 $FILE in [ $FILE == '' ] e in cat $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, e SC2070, $? 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 .sh nel repository;
  • esegue ShellCheck con --severity=warning e 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 ./*.sh invece di *.sh per 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 -- ./*.sh

Utilizzare il formato di output JSON per l'automazione

ShellCheck supporta diversi formati di output tramite --format:

  • tty (predefinito) — output del terminale leggibile dalle persone
  • json — leggibile dalle macchine; ideale per dashboard, controlli personalizzati o caricamento su piattaforme SAST
  • gcc — 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=SC2086 sulla 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=SC2016

Configurare 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 esempio avoid-nullary-conditions, require-variable-braces)
  • disable=SC2059 — disattivazione a livello di progetto per un'eccezione giustificata
  • external-sources=true — segue e controlla le direttive source / .
# .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=SC2312

Script 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' >&2

Verifica 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 -- argument e 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

  1. Prevenire l'iniezione di comandi e argomenti
  2. Gestire i segreti in sicurezza e mantenere pulito l'ambiente
  3. Esecuzione con privilegi minimi e disciplina nell'uso di sudo
  4. Analisi statica e audit con ShellCheck
← Torna a Linux Command Line & Bash Scripting Mastery