تشغيل CodeQL في قاعدة بيانات
باستخدام التعليمات البرمجية المستخرجة إلى قاعدة بيانات، يمكنك الآن تحليلها باستخدام استعلامات CodeQL. يكتب خبراء GitHub والباحثون الأمنيون والمساهمون في المجتمع استعلامات CodeQL الافتراضية ويحافظون عليها. يمكنك أيضا كتابة الاستعلامات الخاصة بك.
يمكنك استخدام استعلامات CodeQL في تحليل فحص التعليمات البرمجية للعثور على المشكلات في التعليمات البرمجية المصدر والعثور على الثغرات الأمنية المحتملة. يمكنك أيضا كتابة استعلامات مخصصة لتحديد المشاكل لكل لغة تستخدمها في التعليمات البرمجية المصدر.
هناك نوعان مهمان من الاستعلامات:
- استعلامات التنبيه تمييز المشاكل في مواقع معينة من التعليمات البرمجية الخاصة بك.
- استعلامات المسار تصف تدفق المعلومات بين المصدر والمتلقي في التعليمات البرمجية الخاصة بك.
استعلام CodeQL بسيط
تحتوي بنية استعلام CodeQL الأساسية على ملحق الملف .ql وتحتوي على عبارة select. فيما يلي مثال على بنية الاستعلام:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */
بيانات تعريف الاستعلام
يؤدي استخدام CodeQL مع فحص التعليمات البرمجية إلى تحويل النتائج بطريقة تسلط الضوء على المشاكل المحتملة التي تم تصميم الاستعلامات للعثور عليها. تحتوي الاستعلامات على خصائص بيانات التعريف التي تشير إلى كيفية تفسير النتائج. استخدم بيانات تعريف الاستعلام من أجل:
- حدد الاستعلامات المخصصة عند إضافتها إلى مستودع GitHub الخاص بك.
- توفير معلومات حول الغرض من الاستعلام.
يمكن أن تتضمن معلومات بيانات التعريف وصفا للاستعلام ومعرفا فريدا ونوع المشكلة (تنبيه أو مسار). تحدد بيانات التعريف أيضا كيفية تفسير نتائج الاستعلام وعرضها.
يحتوي GitHub على دليل نمط موصى به لبيانات تعريف الاستعلام. يمكنك العثور عليه في وثائق CodeQL.
يوضح هذا المثال بيانات التعريف لأحد استعلامات Java القياسية:
لقطة شاشة
لا يفسر CodeQL الاستعلامات التي لا تحتوي على بيانات تعريف. يعرض هذه النتائج كجدول ولا يعرضها في التعليمات البرمجية المصدر.
بناء جملة QL
QL هي لغة استعلام تعريفية موجهة للكائنات. تم تحسينه لتمكين التحليل الفعال لهياكل البيانات الهرمية، وعلى وجه الخصوص، قواعد البيانات التي تمثل البيانات الاصطناعية للبرامج.
بناء جملة QL مشابه ل SQL، ولكن دلالات QL تستند إلى Datalog. Datalog هي لغة برمجة منطقية تعريفية، والتي غالبا ما تستخدم كلغة استعلام. نظرا لأن QL هي في المقام الأول لغة منطقية، فإن جميع العمليات في QL هي عمليات منطقية. ترث QL أيضا المسندات التكرارية من Datalog. يضيف QL دعما للتجاميع لجعل الاستعلامات المعقدة موجزة وبسيطة.
تتكون لغة QL من صيغ منطقية. ويستخدم الموصلات المنطقية الشائعة مثل andornot، جنبا إلى جنب مع الكمية مثل forallexists. نظرا لأن QL يرث دالات التقييم التكرارية، يمكنك أيضا كتابة استعلامات متكررة معقدة باستخدام بناء جملة QL الأساسي والتجاميع مثل countو sumو.average
لمزيد من المعلومات حول لغة QL، راجع وثائق CodeQL.
استعلامات المسار
الطريقة التي تتدفق بها المعلومات عبر برنامج أمر مهم. يمكن أن تتدفق البيانات التي تبدو حميدة بطرق غير متوقعة تسمح باستخدامها بشكل ضار.
يمكن أن يساعدك إنشاء استعلامات المسار في تصور تدفق المعلومات من خلال قاعدة التعليمات البرمجية. يمكن للاستعلام تتبع المسار الذي تأخذه البيانات من نقاط البداية المحتملة (source) إلى نقاط النهاية المحتملة الخاصة به (sink). لنمذجة المسارات، يجب أن يوفر الاستعلام معلومات حول المصدر والمتلقي وخطوات تدفق البيانات التي تربطها.
أسهل طريقة لبدء كتابة استعلام المسار الخاص بك هي استخدام أحد الاستعلامات الموجودة كقالب. للحصول على هذه الاستعلامات للغات المدعومة، راجع وثائق CodeQL.
يتطلب استعلام المسار بيانات تعريف معينة ومسندات استعلام وبنى عبارة select. تتبع العديد من استعلامات المسار المضمنة في CodeQL بنية أساسية. تعتمد البنية على كيفية تصميم CodeQL للغة التي تقوم بتحليلها.
فيما يلي مثال على قالب لاستعلام مسار:
/**
* ...
* @kind path-problem
* ...
*/
import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...
module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"
في هذا القالب:
-
MyConfigurationهي وحدة تحتوي على دالات التقييم التي تحدد كيفية تدفق البيانات بينsourcesink. -
Flowهو نتيجة حساب تدفق البيانات استنادا إلىMyConfiguration. -
Flow::Pathgraphهي وحدة الرسم البياني لتدفق البيانات الناتجة التي تحتاج إلى استيرادها لتضمين تفسيرات المسار في الاستعلام. -
sourcesinkهي عقد في الرسم البياني كما هو محدد في التكوين،Flow::PathNodeهي نوعها. -
DataFlow::Global<..>هو استدعاء لتدفق البيانات. يمكنك استخدامTaintTracking::Global<..>بدلا من ذلك لتضمين مجموعة افتراضية من خطوات اللطخة.
كيفية كتابة استعلام مسار
يحتاج الاستعلام إلى حساب رسم بياني للمسار لإنشاء تفسيرات المسار. للقيام بذلك، حدد دالة تقييم استعلام تسمى edges. دالة تقييم الاستعلام هي دالة تقييم غير مرتبطة بتعليق توضيحي query. يرجع التعليق التوضيحي للاستعلام جميع المجموعات التي تقيمها دالة التقييم.
تحدد دالة التقييم edges علاقات الحافة للرسم البياني الذي تحوسبه. يتم استخدامه لحساب المسارات المتعلقة بكل نتيجة ينشئها الاستعلام الخاص بك. يمكنك أيضا استيراد دالة تقييم edges معرفة مسبقا من وحدة الرسم البياني للمسار في إحدى مكتبات تدفق البيانات القياسية.
تحتوي مكتبات تدفق البيانات على الفئات والمسندات والوحدات النمطية الأخرى المستخدمة بشكل شائع في تحليل تدفق البيانات، بالإضافة إلى وحدة الرسم البياني للمسار. تعمل مكتبات تدفق البيانات CodeQL عن طريق نمذجة الرسم البياني لتدفق البيانات أو تنفيذ تحليل تدفق البيانات. تستخدم مكتبات تدفق البيانات العادية لتحليل تدفق المعلومات الذي يتم فيه الاحتفاظ بقيم البيانات في كل خطوة.
فيما يلي مثال على عبارة تستورد الوحدة النمطية pathgraph من مكتبة تدفق البيانات (DataFlow.qll)، حيث يتم تعريف edges:
import DataFlow::PathGraph
يمكنك استيراد العديد من المكتبات الأخرى المضمنة مع CodeQL. يمكنك أيضا استيراد المكتبات المصممة خصيصا لتنفيذ تحليل تدفق البيانات في أطر عمل وبيئات مشتركة مختلفة.
تم تصميم الفئة PathNode لتنفيذ تحليل تدفق البيانات. تمت زيادته Node بسياق استدعاء (باستثناء المتلقيات) ومسار الوصول والتكوين. يتم إنشاء القيم PathNode التي يمكن الوصول إليها من مصدر فقط.
فيما يلي مثال على مسار الاستيراد:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
يمكنك تحديد دالة تقييم استعلام nodes اختياريا، والتي تحدد عقد الرسم البياني للمسار لجميع اللغات. عند تعريف nodes، تحدد العقد المحددة الحواف ذات نقاط النهاية فقط. عندما لا تحدد nodes، تحتاج إلى تحديد جميع نقاط النهاية المحتملة edges.
تحليل قاعدة البيانات
عند استخدام الاستعلامات لتحليل قاعدة بيانات CodeQL، تتلقى نتائج ذات معنى في سياق التعليمات البرمجية المصدر. يتم تصميم النتائج كتنبيهات أو مسارات بتنسيق SARIF أو تنسيق آخر مفسر.
فيما يلي مثال على أمر قاعدة بيانات CodeQL الذي يحلل قاعدة البيانات عن طريق تشغيل استعلامات محددة مقابلها وتفسير النتائج:
codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...
يجمع هذا الأمر بين تأثير أوامر codeql database run-queriescodeql database interpret-results السباكة.
بدلا من ذلك، يمكنك تشغيل الاستعلامات التي لا تفي بمتطلبات تفسيرها على أنها تنبيهات التعليمات البرمجية المصدر. للقيام بذلك، استخدم codeql-database run-queries أو codeql query run. ثم استخدم codeql bqrs decode لتحويل النتائج الأولية إلى رمز قابل للقراءة.
يمكنك الحصول على قائمة كاملة بأوامر CodeQL CLI المتوفرة في دليل CodeQL CLI.
استخدام ملف SARIF مع فئات
يدعم CodeQL SARIF لمشاركة نتائج التحليل الثابت. تم تصميم SARIF لتمثيل ناتج مجموعة واسعة من أدوات التحليل الثابت.
تحتاج إلى تحديد فئة عند استخدام إخراج SARIF لتحليل CodeQL. يمكن للفئات تمييز التحليلات المتعددة التي يتم إجراؤها على نفس مستودع التثبيت وعلى لغات مختلفة أو أجزاء مختلفة من التعليمات البرمجية. ومع ذلك، فإن ملفات SARIF التي لها نفس الفئة تستبدل بعضها البعض.
يمكنك فحص كل ملف إخراج SARIF باستخدام CodeQL لتحليل لغات مختلفة داخل نفس قاعدة التعليمات البرمجية عندما تكون قيمة الفئة متسقة بين تشغيل التحليل. نوصي باستخدام اللغة التي يتم مسحها ضوئيا كمعرف للفئة.
فيما يلي مثال واحد. تظهر قيمة الفئة (مع إلحاق شرطة مائلة لاحقة إذا لم تكن موجودة بالفعل) كخاصية <run>.automationId في SARIF v1، وخاصية <run>.automationLogicalId في SARIF v2، وخاصية <run>.automationDetails.id في SARIF v2.1.0.
نشر نتائج SARIF إلى GitHub
بعد أن تصبح قاعدة البيانات جاهزة، يمكنك الاستعلام عنها بشكل تفاعلي. أو يمكنك تشغيل مجموعة من الاستعلامات لإنشاء مجموعة من النتائج بتنسيق SARIF وتحميل النتائج إلى مستودع هدف على GitHub.com:
codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...
لتحميل النتائج إلى GitHub، تأكد من أن كل خادم تكامل مستمر (CI) يحتوي على تطبيق GitHub أو رمز وصول شخصي لاستخدام CodeQL CLI. يجب استخدام رمز مميز للوصول أو تطبيق GitHub مع إذن الكتابة security_events.
يمكنك السماح ل CodeQL CLI باستخدام نفس الرمز المميز إذا كانت خوادم CI تستخدم بالفعل رمزا مميزا مع هذا النطاق للتحقق من المستودعات من GitHub. وإلا، قم بإنشاء رمز مميز جديد بإذن الكتابة security_events وأضف هذا الرمز المميز إلى المخزن السري لنظام CI. أفضل ممارسة للأمان هي تعيين علامة --github-auth-stdin وتمرير الرمز المميز إلى الأمر من خلال الإدخال القياسي.
تحميل نتائج SARIF
لفحص التعليمات البرمجية لعرض النتائج من أداة تحليل ثابتة غير تابعة ل Microsoft في مستودع GitHub، يجب تخزين نتائجك في ملف SARIF يدعم مجموعة فرعية معينة من مخطط JSON SARIF 2.1.0. يمكنك تحميل النتائج باستخدام واجهة برمجة التطبيقات لفحص التعليمات البرمجية أو CodeQL CLI.
في كل مرة تقوم فيها بتحميل نتائج فحص التعليمات البرمجية الجديد، يعالج CodeQL النتائج ويضيف تنبيهات إلى المستودع. لمنع تكرار التنبيهات لنفس المشكلة، يستخدم مسح التعليمات البرمجية خاصية partialFingerprints SARIF لمطابقة النتائج عبر عمليات التشغيل المختلفة بحيث تظهر مرة واحدة فقط في آخر تشغيل للفرع المحدد. إن إزالة التكرارات يجعل من الممكن مطابقة التنبيهات مع السطر الصحيح من التعليمات البرمجية عند تحرير الملفات.
يجب أن يكون معرف القاعدة للنتيجة هو نفسه عبر التحليلات. يتم تضمين بيانات بصمة الإصبع تلقائيا في ملفات SARIF التي تم إنشاؤها من خلال سير عمل تحليل CodeQL أو مشغل CodeQL.
تستخدم مواصفات SARIF اسم خاصية JSON partialFingerprints، قاموس من أنواع بصمات الأصابع المسماة إلى بصمة الإصبع. تحتوي هذه الخاصية، كحد أدنى، على قيمة primaryLocationLineHash، والتي توفر بصمة استنادا إلى سياق الموقع الأساسي.
يحاول GitHub ملء حقل partialFingerprints من الملفات المصدر إذا قمت بتحميل ملف SARIF باستخدام الإجراء upload-sarif وكانت هذه البيانات مفقودة. بالإضافة إلى ذلك، إذا قمت بتحميل ملف SARIF بدون بيانات بصمة الإصبع باستخدام نقطة نهاية واجهة برمجة التطبيقات /code-scanning/sarifs، فقد يرى المستخدمون تنبيهات مكررة عند معالجة تنبيهات فحص التعليمات البرمجية وعرضها.
لتجنب رؤية تنبيهات مكررة أثناء العمل باستخدام أدوات تحليل ثابتة، احسب بيانات بصمة الإصبع واملأ خاصية partialFingerprints قبل تحميل ملف SARIF. نقطة البداية المفيدة هي استخدام نفس البرنامج النصي كإجراء upload-sarif.