הפעלת 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 מורכבת מנוסחאות לוגיות. הוא משתמש בקישורים לוגיים נפוצים כגון and, orו- not, יחד עם כמתים כגון forall ו- exists. מאחר ש- QL יורש את החזרדיקאטים רקורסיביים, באפשרותך גם לכתוב שאילתות רקורסיביות מורכבות באמצעות תחביר QL בסיסי וצבירות כגון count, ו sum- average.
לקבלת מידע נוסף אודות שפת ה- QL, עיין בתיעוד CodeQL.
שאילתות נתיב
האופן שבו מידע זורם דרך תוכנית חשוב. נתונים שנראה מועילים יכולים לזרום בדרכים בלתי צפויות שמאפשרות שימוש זדוני בהם.
יצירת שאילתות נתיב יכולה לעזור לך להציג באופן חזותי את זרימת המידע באמצעות בסיס קוד. שאילתה יכולה לעקוב אחר הנתיב שהנתונים לוקחים מנקודות ההתחלה האפשריות שלה (source) לנקודות הקצה האפשריות שלה (sink). כדי ליצור מודל של נתיבים, השאילתה שלך חייבת לספק מידע על המקור, ה- 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הוא מודול המכיל את בפרדיקאטים המגדירים את אופן זרימות הנתונים ביןsourceל-sink. -
Flowהיא התוצאה של חישוב זרימת הנתונים בהתבסס עלMyConfiguration. -
Flow::Pathgraphהוא המודול המתוצא של גרף זרימת הנתונים שעליך לייבא כדי לכלול הסברי נתיב בשאילתה. -
sourceו-sinkהם צמתים בגרף כפי שהוגדר בתצורה,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-queries ופקודות codeql database interpret-results הצנרת.
לחלופין, באפשרותך להפעיל שאילתות שלא עומדות בדרישות של מפורשות כהתראות קוד מקור. לשם כך, השתמש ב- codeql-database run-queries או codeql query run. לאחר מכן codeql bqrs decode בהמרת התוצאות הגולמיות סמיון קריא.
באפשרותך לקבל רשימה מלאה של פקודות CLI זמינות של CodeQL במדריך CodeQL CLI.
שימוש בקובץ SARIF עם קטגוריות
CodeQL תומך ב- SARIF לשיתוף תוצאות ניתוח סטטי. SARIF נועד לייצג את הפלט של מגוון רחב של כלי ניתוח סטטיים.
עליך לציין קטגוריה בעת שימוש בפלט SARIF עבור ניתוח CodeQL. קטגוריות יכולות להבחין בין ניתוחים מרובים שבוצעו באותו מאגר Commit ובשפות שונות או בחלקים שונים של הקוד. עם זאת, קבצי 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 כתיבה.
ייתכן שתאפשר ל- CLI של CodeQL להשתמש באותו אסימון אם שרתי CI כבר משתמשים אסימון עם טווח זה כדי להוציא מאגרים מ- GitHub. אחרת, צור אסימון חדש עם הרשאת security_events והוסף אסימון זה לחנות הסודית של מערכת CI. שיטת עבודה מומלצת לאבטחה היא להגדיר --github-auth-stdin המקשים ולה להעביר את האסימון לפקודה באמצעות הקלט הרגיל.
העלאת תוצאות SARIF
כדי לסריקת קודים כדי להציג תוצאות מכלי ניתוח סטטי שאינו של Microsoft במאגר GitHub, התוצאות שלך חייבות להיות מאוחסנות בקובץ SARIF התומך בערכת משנה ספציפית של סכימת JSON SARIF 2.1.0. באפשרותך להעלות את התוצאות באמצעות ה- API לסריקת קוד או באמצעות ממשק ה- CLI של CodeQL.
בכל פעם שאתה מעלה את התוצאות של סריקת קוד חדשה, CodeQL מעבד את התוצאות ומוסיף התראות למאגר. כדי למנוע התראות כפולות עבור אותה בעיה, סריקת קוד משתמשת במאפיין SARIF partialFingerprints כדי להתאים לתוצאות במגוון הפעלות, כך שהן יופיעו פעם אחת בלבד בהפעלה העדכנית ביותר עבור הענף שנבחר. ביטול כפילויות מאפשר להתאים התראות לקוד הנכון בעת עריכה של קבצים.
מזהה הכלל עבור תוצאה חייב להיות זהה על-פני ניתוחים. נתוני טביעות אצבע כלולים באופן אוטומטי בקובצי SARIF שנוצרו באמצעות זרימת העבודה של ניתוח CodeQL או באמצעות הרץ CodeQL.
מפרטי SARIF משתמשים בשם המאפיין JSON partialFingerprints, מילון מסוגי טביעות אצבע בעלי שם עד טביעת האצבע. מאפיין זה מכיל, לכל הפחות, ערך עבור primaryLocationLineHash, המספק טביעת אצבע בהתבסס על הקשר המיקום הראשי.
GitHub מנסה לאכלס partialFingerprints המקור מקבצי המקור אם אתה מעלה קובץ SARIF באמצעות upload-sarif הפעולה ונתונים אלה חסרים. בנוסף, אם אתה מעלה קובץ SARIF ללא נתוני טביעת אצבע באמצעות נקודת הקצה של ה- API של /code-scanning/sarifs, המשתמשים עשויים לראות התראות כפולות בעת עיבוד והצגה של התראות באמצעות סריקת קוד.
כדי להימנע משימוש בהתראות כפולות בזמן שאתה עובד עם כלי ניתוח סטטיים, חשב נתוני טביעת אצבע ומלא את partialFingerprints הנתונים לפני העלאת קובץ SARIF. נקודת התחלה שימושית היא להשתמש באותו קובץ Script כמו upload-sarif הפעולה.