ShellCheck ile Statik Analiz ve Denetim
ShellCheck'i bir güvenlik kapısına entegre edin ve her betiği güçlendirmek için bulgularını yorumlayın.
ShellCheck ile Statik Analiz ve Denetim, CoddyKit'te ücretsiz bir DevOps Bootcamp dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, DevOps Bootcamp öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DevOps Bootcamp kursu toplamda 4 dersten oluşur.
ShellCheck Nedir ve Neden Önemlidir
ShellCheck, kabuk betikleri için açık kaynaklı bir statik analiz aracıdır. Bash (ve POSIX sh, dash, ksh) kaynak kodunuzu çalıştırmadan ayrıştırır; hataları, güvensiz yapıları, taşınabilirlik sorunlarını ve biçem problemlerini bildirir. Her bulgu SC2086 gibi benzersiz bir kural koduyla etiketlenir.
Güvenliği güçlendirilmiş bir işlem hattında ShellCheck, zorunlu bir geçit görevi görür: hiçbir betik denetimden geçmeden yayımlanmaz. Bunun önemli olmasının nedenleri şunlardır:
- Pek çok kabuk güvenlik açığı (kelime bölme, ekleme, tırnak içine alınmamış genişletme), normal akış testleri sırasında görünmez; ancak saldırganın denetimindeki girdilerle tetiklenir.
- ShellCheck bu hata sınıflarını çalışma zamanından önce, hiçbir ek maliyet olmadan yakalar.
- Her kalıbın neden tehlikeli olduğunu belgeleyerek ekibinizin zamanla daha bilinçli olmasını sağlar.
Herhangi bir sisteme yükleyin:
# 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 --versionShellCheck'i İlk Kez Çalıştırma
En basit çağrı shellcheck <script> biçimindedir. ShellCheck, kabuk lehçesini belirlemek için shebang satırını okur ve ardından bulguları stdout'a yazar.
Her bulgu şunları içerir:
- Dosya ve satır numarası — tam konum
- Önem derecesi —
error,warning,infoveyastyle - SC kodu — bakabileceğiniz veya bastırabileceğiniz kararlı kural tanımlayıcısı
- İnsanların anlayacağı açıklama — neyin yanlış olduğunu ve çoğu zaman nasıl düzeltileceğini söyler
Aşağıdaki betiği çalıştırın ve ShellCheck'in üreteceği çıktıyı inceleyin:
#!/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 -lShellCheck Çıktısını ve SC Kodlarını Yorumlama
Önceki sahnedeki betik için ShellCheck şu tür bulgular üretirdi:
SC2086(warning) — Glob genişletmesini ve kelime bölmeyi önlemek için çift tırnak kullanın —[ $FILE == '' ]içindeki$FILEvecat $FILEiçindeki$FILEiçin.SC2039/SC3010(info) — [ ] içinde == Bash'e özgüdür; POSIX için = kullanın.SC2002(style) — Gereksiz cat. cat file | cmd yerine cmd < file kullanmayı düşünün.
Her SC kodu, gerekçe ve düzeltilmiş bir örnek içeren https://www.shellcheck.net/wiki/SCxxxx adresindeki bir wiki sayfasına karşılık gelir.
Bu betiğin düzeltilmiş sürümü:
#!/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"Önem Derecesi Düzeyleri ve Yapılacaklar
ShellCheck her bulguyu önem derecesine göre sınıflandırır. Bir güvenlik geçidinde bunları şöyle ele almalısınız:
- error — Neredeyse kesinlikle bir hata veya güvenlik açığıdır. Derlemeyi engelleyin. Hemen düzeltin. Örnek: shebang eksikliği olan
SC2148, tırnak içine alınmamış$?içinSC2070. - warning — Sıklıkla istismar edilebilen yüksek riskli kalıptır. Derlemeyi engelleyin. Düzeltin veya bastırmayı açıkça gerekçelendirin. Örnek: tırnak içine alınmamış değişken için
SC2086. - info — Bugün büyük olasılıkla doğrudur; ancak kırılgan veya taşınabilir olmayabilir. Kapsam dışında bırakılmadıysa aynı PR içinde düzeltin.
- style — Görsel veya POSIX tercihiyle ilgilidir. Salt Bash kod tabanında önerilir, ancak isteğe bağlıdır.
Yalnızca warning ve üzeri bulgularda sıfırdan farklı çıkış kodu vermek için --severity=warning kullanın; bu, standart güvenlik geçidi eşiğidir:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"ShellCheck'i CI Güvenlik Geçidi Olarak Entegre Etme
Bir güvenlik geçidi ancak zorunlu ve otomatik olduğunda işe yarar. Aşağıdaki kalıp, ShellCheck'i şu işlemleri yapan bir CI adımına sarar:
- Depodaki tüm
.shdosyalarını bulur. - ShellCheck'i
--severity=warningve makinelerin okuyabileceği JSON çıktısıyla çalıştırır. - Herhangi bir bulgu varsa işlem hattını (
exit 1) başarısız kılar. - Mühendislerin CI günlüğünden ayrılmadan bulgular üzerinde işlem yapabilmesi için bir özet yazdırır.
Bu dosyayı deponuza ekleyin ve CI işlem hattınızdan (GitHub Actions, Jenkins, GitLab CI vb.) çağırın:
#!/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."SC2086 Ailesi: Tırnak İçine Alınmamış Değişken Genişletmeleri
SC2086, en yaygın ShellCheck bulgusu ve en çok istismar edilen kabuk güvenlik açıklarından biridir: tırnak içine alınmamış değişken genişletmeleri.
Bir değişken çift tırnak içine alınmadığında kabuk, değeri üzerinde kelime bölme işlemi (IFS üzerinden böler) ve glob genişletmesi gerçekleştirir. Değişkeni denetleyen bir saldırgan, ek bağımsız değişkenler ekleyebilir, dosya sistemi geçişini tetikleyebilir veya komutların beklenmeyen işlenenler almasına neden olabilir.
Klasik tehlikeli kalıp:
#!/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[@]}"SC2046 ve SC2035 ile Komut Ekleme Riskini Belirleme
Daha az bilinen ancak kritik iki kural, alt kabuk çıktısı üzerinden komut ekleme sorununu ele alır:
SC2046—$(…)içindeki kelime bölme ve glob genişlemesini önlemek için bunu tırnak içine alın. Alt kabuğun çıktısı tırnak içine alınmadan kullanılırsa çıktıda bulunan her boşluk veya glob karakteri bir kabuk belirtecine dönüşür.SC2035—-ile başlayan dosya adlarının seçenek olarak yorumlanmasını önlemek için*.shyerine./*.shkullanın; bu, klasik bir bağımsız değişken ekleme vektörüdür.
Somut istismar senaryosu ve düzeltmesi:
#!/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 -- ./*.shOtomasyon İçin JSON Çıktı Biçimini Kullanma
ShellCheck, --format aracılığıyla birden çok çıktı biçimini destekler:
tty(varsayılan) — insanların okuyabileceği terminal çıktısıjson— makinelerin okuyabileceği biçim; panolar, özel engelleyiciler veya SAST platformlarına yükleme için idealdirgcc— GCC hata biçimini ayrıştıran araçlarla uyumludur (IDE'ler, Vim/Emacs)checkstyle— Jenkins Checkstyle eklentisinin kullandığı XML biçimi
JSON biçimi, örneğin yalnızca belirli SC kodlarını engellemek veya büyük bir kod tabanındaki bulguları tek bir güvenlik raporunda toplamak gibi otomatik politikalar yazmanıza olanak tanır.
#!/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)'Yanlış Pozitifleri Doğru Biçimde Bastırma
ShellCheck'i topluca devre dışı bırakmak amacını boşa çıkarır. Doğru yaklaşım, yalnızca bulgunun gerçekten uygulanamadığı tam satırı veya bloğu etkileyen hedefli ve belgelenmiş bir bastırma kullanmaktır.
Üç bastırma mekanizması vardır:
- Satır içi devre dışı bırakma — sorunlu kodun üstündeki satıra
# shellcheck disable=SC2086yazın. Yalnızca o satırı etkiler. - Blok için devre dışı bırakma/etkinleştirme — bir bölümü
# shellcheck disable=…ve# shellcheck enable=…ile sarın. - Dosya düzeyinde yönerge —
# shellcheck disable=…ifadesini dosyanın en üstüne yerleştirin (nadiren gerekçelendirilebilir; nedenini belgeleyin).
Her bastırma, bulgunun neden yanlış pozitif olduğunu açıklayan bir yorum içermelidir:
#!/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.shellcheckrc Üzerinden ShellCheck Yapılandırma
Proje genelindeki ayarlar için ShellCheck, betiğin bulunduğu dizinden başlayarak / dizinine kadar yukarı doğru .shellcheckrc dosyasını okur. Böylece her çağrıda seçenekleri tekrarlamaktan kaçınabilir ve geçit betiklerini basit tutabilirsiniz.
.shellcheckrc içindeki kullanışlı yönergeler:
shell=bash— lehçe algılamasını geçersiz kılar (shebang içermeyen dosyalar için kullanışlıdır)enable=all— isteğe bağlı denetimleri etkinleştirir (ör.avoid-nullary-conditions,require-variable-braces)disable=SC2059— gerekçelendirilmiş bir istisna için proje genelinde bastırmaexternal-sources=true—source/.yönergelerini izler ve denetler
# .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=SC2312Uçtan Uca Güçlendirilmiş Betik: Öncesi ve Sonrası
ShellCheck bulgularını içselleştirmenin en etkili yolu, gerçekçi bir betiği başarısız bir durumdan temiz ve güçlendirilmiş bir duruma yeniden düzenlemektir. Aşağıdaki betik bir dizini yedekler ve güvenlik dikkate alınmadan yazılmıştır. En az beş farklı kural açısından ShellCheck denetiminden geçemez.
Her iki sürümü de inceleyin. Sonraki sürüm, bastırma yönergeleri olmadan shellcheck --severity=warning denetiminden geçer ve saldırganın denetimindeki girdiler karşısında önemli ölçüde daha güvenlidir:
#!/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' >&2Bilgi Yoklaması: Güvenlik Geçidinde ShellCheck
ShellCheck'in güvenlik geçidi olarak rolünü ne kadar anladığınızı sınayın.
Özet: Güvenlik Geçidi Olarak Statik Analiz
Bu derste ShellCheck'i Bash iş akışınızda zorunlu bir güvenlik geçidi hâline getirmeyi öğrendiniz:
- ShellCheck, betiklerinizi çalıştırmadan statik analiz gerçekleştirir; tırnak kullanımı hatalarını, ekleme risklerini ve güvensiz kalıpları çalışma zamanından önce yakalar.
- Her bulgu, ayrıntılı belgelere ve düzeltme yönergelerine bağlanan bir SC kodu (ör.
SC2086) içerir. - Önem derecesi basamakları —
error,warning,info,style— geçidi ayarlamanıza olanak tanır:--severity=warningönerilen güvenlik eşiğidir. - Raporlamayı, eğilim takibini ve SAST entegrasyonunu otomatikleştirmek için makinelerin okuyabileceği çıktıyı (
--format=json) kullanın. - Bastırmayı ölçülü kullanın: her zaman tek bir satırı hedefleyin, nedenini her zaman bir yorumda belgeleyin ve gerekçelendirilmedikçe
.shellcheckrcaracılığıyla bile olsa genel olarak bastırmayın. - Derinlemesine savunma için ShellCheck'i
set -euo pipefail, açık tırnak kullanımı,-- argumentsonlandırıcıları ve girdi doğrulamasıyla birlikte kullanın.
ShellCheck denetiminden geçen bir betik otomatik olarak güvenli değildir; ancak ShellCheck denetiminden geçemeyen bir betik asla üretim ortamına ulaşmamalıdır.
Sıkça Sorulan Sorular
“ShellCheck ile Statik Analiz ve Denetim” dersi ücretsiz mi?
Evet — “ShellCheck ile Statik Analiz ve Denetim” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve DevOps Bootcamp kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DevOps Bootcamp kursu toplamda 4 dersten oluşur.
“ShellCheck ile Statik Analiz ve Denetim” dersinde ne öğreneceğim?
ShellCheck'i bir güvenlik kapısına entegre edin ve her betiği güçlendirmek için bulgularını yorumlayın. DevOps Bootcamp ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
DevOps Bootcamp öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te DevOps Bootcamp, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.
“ShellCheck ile Statik Analiz ve Denetim” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu DevOps Bootcamp dersinde kod yazıp çalıştırabilir miyim?
Evet. Her DevOps Bootcamp dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Komut ve Bağımsız Değişken Enjeksiyonunu Önleme
- Güvenli Gizli Bilgi Yönetimi ve Ortam Hijyeni
- En Az Ayrıcalıklı Çalıştırma ve sudo Disiplini
- ShellCheck ile Statik Analiz ve Denetim