Analiza statyczna i audyt za pomocą ShellCheck
Włącz ShellCheck do bramki bezpieczeństwa i interpretuj jego ustalenia, aby wzmacniać każdy skrypt.
Analiza statyczna i audyt za pomocą ShellCheck to bezpłatna lekcja DevOps Bootcamp na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej DevOps Bootcamp, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Czym jest ShellCheck i dlaczego ma znaczenie
ShellCheck to narzędzie open source do analizy statycznej skryptów powłoki. Analizuje kod źródłowy Bash (a także POSIX sh, dash i ksh) bez jego wykonywania i zgłasza błędy, niebezpieczne konstrukcje, problemy z przenośnością oraz kwestie stylu — każde zgłoszenie ma unikatowy kod reguły, taki jak SC2086.
W potoku wzmocnionym pod kątem bezpieczeństwa ShellCheck pełni funkcję obowiązkowej bramki: żaden skrypt nie trafia do wdrożenia, dopóki nie przejdzie kontroli. Ma to znaczenie, ponieważ:
- Wiele luk w skryptach powłoki (dzielenie na słowa, wstrzykiwanie kodu, niecytowane rozwinięcia) pozostaje niewidocznych podczas testów poprawnych scenariuszy, ale ujawnia się przy danych kontrolowanych przez atakującego.
- ShellCheck wykrywa te klasy błędów przed wykonaniem, bez dodatkowego kosztu.
- Dokumentuje, dlaczego dany wzorzec jest niebezpieczny, dzięki czemu z czasem zwiększa świadomość zespołu.
Zainstaluj go w dowolnym systemie:
# 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 --versionPierwsze uruchomienie ShellCheck
Najprostsze wywołanie to shellcheck <script>. ShellCheck odczytuje wiersz shebang, aby określić dialekt powłoki, a następnie wypisuje wyniki na stdout.
Każde zgłoszenie zawiera:
- Numer pliku i wiersza — dokładną lokalizację
- Poziom ważności —
error,warning,infolubstyle - Kod SC — stały identyfikator reguły, który można sprawdzić lub wyłączyć
- Wyjaśnienie zrozumiałe dla człowieka — informuje, co jest nieprawidłowe i często także, jak to naprawić
Uruchom poniższy skrypt i zobacz dane wyjściowe, które wygenerowałby ShellCheck:
#!/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 -lInterpretowanie danych wyjściowych ShellCheck i kodów SC
Dla skryptu z poprzedniej sceny ShellCheck zgłosiłby między innymi takie wyniki:
SC2086(warning) — Double quote to prevent globbing and word splitting — dla$FILEw[ $FILE == '' ]oraz wcat $FILE.SC2039/SC3010(info) — == in [ ] is a bashism; use = for POSIX.SC2002(style) — Useless cat. Consider cmd < file instead of cat file | cmd.
Każdy kod SC odpowiada stronie wiki pod adresem https://www.shellcheck.net/wiki/SCxxxx, zawierającej uzasadnienie i poprawiony przykład.
Poprawiona wersja tego skryptu:
#!/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"Poziomy ważności i działania, które należy podjąć
ShellCheck klasyfikuje każde zgłoszenie według poziomu ważności. W bramce bezpieczeństwa należy postępować z nimi w następujący sposób:
- error — niemal na pewno błąd lub luka w zabezpieczeniach. Zablokuj kompilację. Napraw natychmiast. Przykład:
SC2148— brak shebangu,SC2070— niecytowane$?. - warning — wzorzec wysokiego ryzyka, który często można wykorzystać. Zablokuj kompilację. Napraw problem lub wyraźnie uzasadnij wyłączenie reguły. Przykład:
SC2086— niecytowana zmienna. - info — rozwiązanie prawdopodobnie działa poprawnie obecnie, ale jest podatne na problemy lub nieprzenośne. Napraw w ramach tego samego PR-a, chyba że uznano to za wykraczające poza zakres.
- style — kwestia kosmetyczna lub preferencja POSIX. Zalecane, ale opcjonalne w bazie kodu zawierającej wyłącznie Bash.
Użyj --severity=warning, aby zwracać kod różny od zera tylko w przypadku ostrzeżeń i poważniejszych problemów — jest to standardowy próg bramki bezpieczeństwa:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"Integracja ShellCheck jako bramki bezpieczeństwa CI
Bramka bezpieczeństwa jest użyteczna tylko wtedy, gdy jest obowiązkowa i zautomatyzowana. Poniższy schemat opakowuje ShellCheck w krok CI, który:
- Znajduje każdy plik
.shw repozytorium. - Uruchamia ShellCheck z opcją
--severity=warningi generuje dane wyjściowe JSON czytelne maszynowo. - Kończy potok niepowodzeniem (
exit 1), jeśli istnieje jakiekolwiek zgłoszenie. - Wyświetla podsumowanie, aby inżynierowie mogli reagować na zgłoszenia bez opuszczania dziennika CI.
Umieść ten plik w repozytorium i wywołuj go z potoku CI (GitHub Actions, Jenkins, GitLab CI itd.):
#!/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."Rodzina SC2086: niecytowane rozwinięcia zmiennych
SC2086 to najczęstsze zgłoszenie ShellCheck i jedna z najczęściej wykorzystywanych luk w zabezpieczeniach powłoki: niecytowane rozwinięcia zmiennych.
Gdy zmienna nie jest ujęta w podwójny cudzysłów, powłoka wykonuje na jej wartości dzielenie na słowa (według IFS) oraz rozwinięcie symboli wieloznacznych. Atakujący, który kontroluje zmienną, może wstrzyknąć dodatkowe argumenty, wywołać przechodzenie po systemie plików lub sprawić, że polecenia otrzymają nieoczekiwane operandy.
Klasyczny niebezpieczny wzorzec:
#!/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[@]}"Wykrywanie ryzyka wstrzyknięcia polecenia za pomocą SC2046 i SC2035
Dwie mniej znane, ale krytyczne reguły dotyczą wstrzykiwania poleceń przez dane wyjściowe podpowłoki:
SC2046— Quote this to prevent word splitting / glob wewnątrz$(…). Jeśli dane wyjściowe podpowłoki są używane bez cudzysłowów, każdy znak odstępu lub symbol wieloznaczny w tych danych staje się tokenem powłoki.SC2035— Use./*.shinstead of*.sh, aby nazwy plików zaczynające się od-nie były interpretowane jako opcje (to klasyczny wektor wstrzyknięcia argumentów).
Konkretny scenariusz wykorzystania luki i poprawka:
#!/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 -- ./*.shUżywanie formatu JSON danych wyjściowych do automatyzacji
ShellCheck obsługuje wiele formatów danych wyjściowych za pomocą opcji --format:
tty(domyślny) — dane wyjściowe terminala czytelne dla człowiekajson— format czytelny maszynowo; idealny do pulpitów nawigacyjnych, niestandardowych blokad lub przesyłania do platform SASTgcc— zgodny z narzędziami analizującymi format błędów GCC (IDE, Vim/Emacs)checkstyle— format XML używany przez wtyczkę Jenkins Checkstyle
Format JSON pozwala tworzyć zautomatyzowane zasady, na przykład blokować tylko określone kody SC lub agregować zgłoszenia z dużej bazy kodu w raporcie bezpieczeństwa.
#!/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)'Prawidłowe wyłączanie fałszywie dodatnich zgłoszeń
Masowe wyłączanie ShellCheck niweczy jego cel. Właściwym podejściem jest ukierunkowane, udokumentowane wyłączenie reguły, które obejmuje wyłącznie konkretny wiersz lub blok, w którym zgłoszenie rzeczywiście nie ma zastosowania.
Trzy mechanizmy wyłączania reguł:
- Wyłączenie w wierszu —
# shellcheck disable=SC2086w wierszu powyżej problematycznego kodu. Dotyczy tylko tego wiersza. - Wyłączenie/włączenie bloku — obejmij sekcję dyrektywami
# shellcheck disable=…i# shellcheck enable=…. - Dyrektywa na poziomie pliku — umieść
# shellcheck disable=…na początku pliku (rzadko uzasadnione; należy wyjaśnić przyczynę).
Każde wyłączenie reguły musi zawierać komentarz wyjaśniający, dlaczego zgłoszenie jest fałszywie dodatnie:
#!/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=SC2016Konfigurowanie ShellCheck za pomocą .shellcheckrc
W przypadku ustawień obowiązujących w całym projekcie ShellCheck odczytuje plik .shellcheckrc z katalogu skryptu, a następnie z kolejnych katalogów nadrzędnych aż do /. Dzięki temu nie trzeba powtarzać opcji przy każdym wywołaniu, a skrypty bramki pozostają proste.
Przydatne dyrektywy w pliku .shellcheckrc:
shell=bash— wymusza wykryty dialekt (przydatne w plikach bez shebangu)enable=all— włącza opcjonalne kontrole (np.avoid-nullary-conditions,require-variable-braces)disable=SC2059— wyłączenie reguły w całym projekcie dla uzasadnionego wyjątkuexternal-sources=true— podąża za dyrektywamisource/.i sprawdza wskazywane pliki
# .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=SC2312Wzmocniony skrypt od początku do końca: przed i po zmianach
Najskuteczniejszym sposobem przyswojenia wyników ShellCheck jest przekształcenie realistycznego skryptu ze stanu niespełniającego wymagań do czystej, wzmocnionej wersji. Poniższy skrypt tworzy kopię zapasową katalogu i został napisany bez uwzględnienia bezpieczeństwa. Nie przechodzi kontroli ShellCheck z powodu co najmniej pięciu różnych reguł.
Proszę przeanalizować obie wersje. Wersja po przechodzi kontrolę shellcheck --severity=warning bez dyrektyw wyłączających reguły i jest znacznie bezpieczniejsza w przypadku danych kontrolowanych przez atakującego:
#!/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' >&2Sprawdzenie wiedzy: ShellCheck jako bramka bezpieczeństwa
Proszę sprawdzić swoją wiedzę na temat roli ShellCheck jako bramki bezpieczeństwa.
Podsumowanie: analiza statyczna jako bramka bezpieczeństwa
W tej lekcji nauczyli się Państwo, jak uczynić narzędzie ShellCheck obowiązkową bramką bezpieczeństwa w procesie pracy z Bash:
- ShellCheck wykonuje analizę statyczną bez uruchamiania skryptów, wykrywając przed wykonaniem błędy cytowania, ryzyko wstrzyknięć i niebezpieczne wzorce.
- Każde zgłoszenie zawiera kod SC (np.
SC2086) powiązany ze szczegółową dokumentacją i instrukcjami naprawy. - Skala poziomów ważności —
error,warning,info,style— pozwala dostosować bramkę:--severity=warningto zalecany próg bezpieczeństwa. - Używaj danych wyjściowych czytelnych maszynowo (
--format=json), aby automatyzować raportowanie, śledzenie trendów i integrację z SAST. - Wyłączaj reguły oszczędnie: zawsze ograniczaj wyłączenie do jednego wiersza, zawsze dokumentuj przyczynę w komentarzu i nigdy nie wyłączaj reguł globalnie, chyba że jest to uzasadnione w pliku
.shellcheckrc. - Połącz ShellCheck z
set -euo pipefail, jawnym cytowaniem, terminatorami-- argumentoraz walidacją danych wejściowych, aby uzyskać wielowarstwową ochronę.
Skrypt, który przechodzi kontrolę ShellCheck, nie jest automatycznie bezpieczny — jednak skrypt, który jej nie przechodzi, nigdy nie powinien trafić na produkcję.
Często zadawane pytania
Czy lekcja „Analiza statyczna i audyt za pomocą ShellCheck” jest bezpłatna?
Tak — pełny tekst „Analiza statyczna i audyt za pomocą ShellCheck” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu DevOps Bootcamp, przejdź na CoddyKit PRO. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Co nauczysz się w „Analiza statyczna i audyt za pomocą ShellCheck”?
Włącz ShellCheck do bramki bezpieczeństwa i interpretuj jego ustalenia, aby wzmacniać każdy skrypt. Ćwiczysz DevOps Bootcamp z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć DevOps Bootcamp?
Nie wymagamy żadnego doświadczenia. DevOps Bootcamp w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Analiza statyczna i audyt za pomocą ShellCheck”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji DevOps Bootcamp?
Tak. Każda lekcja DevOps Bootcamp zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Zapobieganie wstrzykiwaniu poleceń i argumentów
- Bezpieczne obchodzenie się z sekretami i higiena środowiska
- Wykonywanie z minimalnymi uprawnieniami i dyscyplina sudo
- Analiza statyczna i audyt za pomocą ShellCheck