التحليل الساكن والتدقيق باستخدام ShellCheck
أدمج ShellCheck في بوابة أمنية وفسّر نتائجه لتقوية كل سكربت
التحليل الساكن والتدقيق باستخدام ShellCheck درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ما هو ShellCheck ولماذا يهم
ShellCheck أداة مفتوحة المصدر للتحليل الساكن لسكربتات shell. تحلّل شيفرة Bash (وكذلك POSIX sh وdash وksh) دون تنفيذها، وتبلّغ عن الأخطاء والبنى غير الآمنة ومشكلات قابلية النقل ومشكلات الأسلوب — ويحمل كل منها رمز قاعدة فريدًا مثل SC2086.
في مسار عمل مُحصّن أمنيًا، يعمل ShellCheck كبوابة إلزامية: لا يُطلَق أي سكربت حتى ينجح في الفحص. وهذا مهم للأسباب التالية:
- تكون العديد من ثغرات shell (مثل تقسيم الكلمات والحقن والتوسعة غير المقتبسة) غير ظاهرة أثناء اختبار المسار السليم، لكنها تظهر عند التعامل مع مدخلات يتحكم فيها المهاجم.
- يكتشف ShellCheck هذه الفئات من الأخطاء قبل وقت التشغيل، دون تكلفة تُذكر.
- يوثّق سبب خطورة كل نمط، مما يزيد وعي فريقك بمرور الوقت.
ثبّته على أي نظام:
# 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تشغيل ShellCheck للمرة الأولى
أبسط طريقة للاستدعاء هي shellcheck <script>. يقرأ ShellCheck سطر shebang لتحديد لهجة shell، ثم يرسل النتائج إلى stdout.
تتضمن كل نتيجة ما يلي:
- رقم الملف والسطر — الموقع الدقيق
- الخطورة —
errorأوwarningأوinfoأوstyle - رمز SC — معرّف ثابت للقاعدة يمكنك البحث عنه أو تعطيله
- شرح بلغة بشرية — يوضح لك ما الخطأ، وغالبًا كيفية إصلاحه
شغّل السكربت أدناه ولاحظ المخرجات التي سينتجها 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 -lتفسير مخرجات ShellCheck ورموز SC
بالنسبة إلى السكربت الوارد في المشهد السابق، سيصدر ShellCheck نتائج مثل:
SC2086(تحذير) — ضع علامات اقتباس مزدوجة لمنع توسعة glob وتقسيم الكلمات — على$FILEفي[ $FILE == '' ]وفيcat $FILE.SC2039/SC3010(معلومات) — == في [ ] خاصية في bash؛ استخدم = مع POSIX.SC2002(أسلوب) — استخدام cat غير مفيد. ضع في الاعتبار استخدام cmd < file بدلًا من cat file | cmd.
يرتبط كل رمز SC بصفحة wiki على https://www.shellcheck.net/wiki/SCxxxx تتضمن الأساس المنطقي ومثالًا مصححًا.
الإصدار المصحح من ذلك السكربت:
#!/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"مستويات الخطورة وما يجب التعامل معه
يصنّف ShellCheck كل نتيجة وفقًا لدرجة خطورتها. وفي بوابة أمنية، ينبغي التعامل معها كما يلي:
- error — خطأ أو ثغرة أمنية شبه مؤكدة. أوقف عملية البناء وأصلح المشكلة فورًا. مثال:
SC2148لغياب shebang، وSC2070لوجود$?غير مقتبس. - warning — نمط عالي الخطورة يمكن استغلاله غالبًا. أوقف عملية البناء، وأصلح المشكلة أو برّر تعطيلها صراحةً. مثال:
SC2086لمتغير غير مقتبس. - info — صحيح على الأرجح اليوم، لكنه هش أو غير قابل للنقل. أصلحه في طلب الدمج نفسه ما لم يُستبعد صراحةً من النطاق.
- style — تفضيل تجميلي أو متعلق بـ POSIX. موصى به لكنه اختياري في قاعدة شيفرة Bash خالصة.
استخدم --severity=warning للخروج برمز غير صفري عند وجود تحذيرات أو نتائج أعلى منها فقط — وهو الحد القياسي للبوابة الأمنية:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"دمج ShellCheck بوصفه بوابة أمنية في CI
لا تكون البوابة الأمنية مفيدة إلا عندما تكون إلزامية ومؤتمتة. يغلّف النمط أدناه ShellCheck داخل خطوة CI تنفّذ ما يلي:
- العثور على كل ملف
.shفي المستودع. - تشغيل ShellCheck باستخدام
--severity=warningوإخراج JSON قابل للقراءة آليًا. - فشل المسار (
exit 1) عند وجود أي نتيجة. - طباعة ملخص حتى يتمكن المهندسون من التعامل مع النتائج دون مغادرة سجل CI.
ضع هذا الملف في مستودعك واستدعِه من مسار CI لديك (GitHub Actions أو Jenkins أو GitLab CI وغير ذلك):
#!/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: توسعات المتغيرات غير المقتبسة
يُعد SC2086 النتيجة الأكثر شيوعًا في ShellCheck، كما يُعد من أكثر ثغرات shell استغلالًا: توسعات المتغيرات غير المقتبسة.
عندما لا يوضع متغير بين علامتي اقتباس مزدوجتين، تنفّذ shell تقسيم الكلمات (التقسيم وفق IFS) وتوسعة glob على قيمته. ويمكن لمهاجم يتحكم في المتغير أن يحقن وسائط إضافية، أو يفعّل اجتياز نظام الملفات، أو يتسبب في تلقي الأوامر معاملات غير متوقعة.
النمط الخطير التقليدي:
#!/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 وSC2035
تتناول قاعدتان أقل شهرة لكنهما مهمتان حقن الأوامر عبر مخرجات subshell:
SC2046— ضع هذا بين علامتي اقتباس لمنع تقسيم الكلمات / glob داخل$(…). إذا استُخدمت مخرجات subshell دون اقتباس، فإن أي مسافة بيضاء أو محرف glob في المخرجات يتحول إلى رمز shell.SC2035— استخدم./*.shبدلًا من*.shلتجنب تفسير أسماء الملفات التي تبدأ بـ-على أنها خيارات (وهو مسار تقليدي لحقن الوسائط).
سيناريو استغلال عملي وإصلاحه:
#!/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استخدام تنسيق إخراج JSON للأتمتة
يدعم ShellCheck تنسيقات إخراج متعددة عبر --format:
tty(الافتراضي) — مخرجات طرفية سهلة القراءة للبشرjson— قابل للقراءة آليًا؛ مثالي للوحات المعلومات أو أدوات المنع المخصصة أو الرفع إلى منصات SASTgcc— متوافق مع الأدوات التي تحلّل تنسيق أخطاء GCC (مثل IDEs وVim/Emacs)checkstyle— تنسيق XML يستهلكه ملحق Checkstyle في Jenkins
يتيح لك تنسيق JSON كتابة سياسات مؤتمتة، مثل المنع استنادًا إلى رموز SC محددة فقط أو تجميع النتائج عبر قاعدة شيفرة كبيرة في تقرير أمني.
#!/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)'تعطيل النتائج الإيجابية الكاذبة بطريقة صحيحة
إن تعطيل ShellCheck بالكامل يقوّض الغرض منه. والنهج الصحيح هو تعطيل موجّه وموثّق يؤثر فقط في السطر أو الكتلة المحددة التي لا تنطبق عليها النتيجة فعلًا.
هناك ثلاث آليات لتعطيل النتائج:
- تعطيل مضمن — ضع
# shellcheck disable=SC2086في السطر السابق للشيفرة المخالفة. ويؤثر ذلك في هذا السطر فقط. - تعطيل/تمكين كتلة — أحط قسمًا بالتعليمتين
# shellcheck disable=…و# shellcheck enable=…. - توجيه على مستوى الملف — ضع
# shellcheck disable=…في أعلى الملف (ولا يُبرَّر ذلك إلا نادرًا؛ وثّق السبب).
يجب أن يتضمن كل تعطيل تعليقًا يشرح سبب كون النتيجة إيجابية كاذبة:
#!/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تهيئة ShellCheck عبر .shellcheckrc
بالنسبة إلى الإعدادات على مستوى المشروع، يقرأ ShellCheck الملف .shellcheckrc من مجلد السكربت صعودًا حتى /. ويتيح لك ذلك تجنب تكرار الخيارات عند كل استدعاء، كما يحافظ على بساطة سكربتات البوابة.
توجيهات مفيدة في .shellcheckrc:
shell=bash— تجاوز اكتشاف اللهجة (مفيد للملفات التي لا تحتوي على shebang)enable=all— تفعيل الفحوصات الاختيارية (مثلavoid-nullary-conditionsوrequire-variable-braces)disable=SC2059— تعطيل على مستوى المشروع لاستثناء مبررexternal-sources=true— تتبّع وفحص توجيهات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سكربت مُحصّن من البداية إلى النهاية: قبل وبعد
تتمثل الطريقة الأكثر فاعلية لاستيعاب نتائج ShellCheck في إعادة هيكلة سكربت واقعي من حالة فاشلة إلى حالة نظيفة ومُحصّنة. ينسخ السكربت أدناه نسخة احتياطية من مجلد، وقد كُتب دون مراعاة الأمان. ويفشل في ShellCheck وفق خمس قواعد مختلفة على الأقل.
ادرس الإصدارين. ينجح إصدار بعد في shellcheck --severity=warning دون توجيهات تعطيل، وهو أكثر أمانًا بدرجة ملحوظة عند التعامل مع مدخلات يتحكم فيها المهاجم:
#!/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اختبار المعرفة: ShellCheck في بوابة أمنية
اختبر مدى فهمك لدور ShellCheck بوصفه بوابة أمنية.
مراجعة: التحليل الساكن بوصفه بوابة أمنية
تعلّمت في هذا الدرس كيفية جعل ShellCheck بوابة أمنية إلزامية ضمن سير عمل Bash لديك:
- ينفّذ ShellCheck تحليلًا ساكنًا دون تشغيل السكربتات، فيكتشف أخطاء الاقتباس ومخاطر الحقن والأنماط غير الآمنة قبل وقت التشغيل.
- تحمل كل نتيجة رمز SC (مثل
SC2086) يرتبط بوثائق مفصلة وإرشادات للإصلاح. - يتيح تدرّج الخطورة —
errorوwarningوinfoوstyle— ضبط البوابة؛ ويُعد--severity=warningحد الأمان الموصى به. - استخدم مخرجات قابلة للقراءة آليًا (
--format=json) لأتمتة إعداد التقارير وتتبع الاتجاهات ودمج SAST. - عطّل النتائج باعتدال: استهدف سطرًا واحدًا دائمًا، ووثّق السبب في تعليق دائمًا، ولا تعطّل الفحوصات على مستوى العالم إلا إذا كان ذلك مبررًا عبر
.shellcheckrc. - اجمع ShellCheck مع
set -euo pipefailوالاقتباس الصريح وفواصل إنهاء الوسائط-- argumentوالتحقق من المدخلات لتحقيق دفاع متعدد الطبقات.
لا يكون السكربت الذي ينجح في ShellCheck آمنًا تلقائيًا — لكن يجب ألا يصل السكربت الذي يفشل في ShellCheck مطلقًا إلى بيئة الإنتاج.
الأسئلة الشائعة
هل درس «التحليل الساكن والتدقيق باستخدام ShellCheck» مجاني؟
نعم — نص درس «التحليل الساكن والتدقيق باستخدام ShellCheck» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ماذا ستتعلم في «التحليل الساكن والتدقيق باستخدام ShellCheck»؟
أدمج ShellCheck في بوابة أمنية وفسّر نتائجه لتقوية كل سكربت تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟
لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «التحليل الساكن والتدقيق باستخدام ShellCheck»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Linux Command Line & Bash Scripting Mastery هذا؟
نعم. كل درس في Linux Command Line & Bash Scripting Mastery يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- منع حقن الأوامر والوسائط
- التعامل الآمن مع الأسرار ونظافة البيئة
- التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo
- التحليل الساكن والتدقيق باستخدام ShellCheck