Menjalankan CodeQL dalam database
Dengan kode yang diekstrak ke database, Anda sekarang dapat menganalisisnya dengan menggunakan kueri CodeQL. Pakar GitHub, peneliti keamanan, dan kontributor komunitas menulis dan memelihara kueri CodeQL default. Anda juga dapat menulis kueri Anda sendiri.
Anda dapat menggunakan kueri CodeQL dalam analisis pemindaian kode untuk menemukan masalah dalam kode sumber Anda dan mengidentifikasi potensi kerentanan keamanan. Anda juga dapat menulis kueri kustom untuk mengidentifikasi masalah untuk setiap bahasa yang Anda gunakan dalam kode sumber Anda.
Ada dua jenis kueri penting:
- Kueri pemberitahuan menyoroti masalah di lokasi kode tertentu.
- Kueri jalur menjelaskan alur informasi antara sumber dan sink dalam kode Anda.
Kueri CodeQL sederhana
Struktur kueri CodeQL dasar memiliki ekstensi file .ql dan berisi klausa select. Berikut adalah contoh struktur kueri:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... */
where /* ... logical formula ... */
select /* ... expressions ... */
Kustomisasi kueri
Analisis CodeQL bergantung pada kueri. Meskipun Anda dapat menggunakan kueri standar yang disediakan oleh GitHub, Anda juga dapat menyesuaikan analisis dengan menulis kueri Anda sendiri dan mengaturnya ke dalam paket kueri.
Kueri biasanya dikelompokkan ke dalam paket kueri, yang merupakan direktori yang berisi kueri, pustaka bersama, dan file konfigurasi. Paket kueri memungkinkan Anda menentukan sekumpulan aturan analisis yang dapat digunakan kembali untuk proyek Anda. Dalam paket, Anda mungkin menyertakan file individual .ql , pustaka pembantu yang menentukan logika yang dapat digunakan kembali, dan suite kueri yang mengelompokkan beberapa kueri bersama-sama.
Rangkaian kueri (.qlsfile) digunakan untuk mengontrol kueri mana yang dijalankan selama analisis. Alih-alih menjalankan kueri satu per satu, Anda menentukan rangkaian yang mencantumkan semua kueri yang ingin Anda jalankan. Contohnya:
- description: Custom security queries
- queries:
- ./queries/hardcoded-credentials.ql
- ./queries/insecure-config.ql
Rangkaian ini mengelompokkan beberapa kueri sehingga dapat berjalan bersama sebagai bagian dari satu analisis.
Anda dapat membuat kueri Anda sendiri dengan menulis .ql file. Kueri menjelaskan pola dalam kode yang ingin Anda deteksi. Ini biasanya mengimpor pustaka bahasa, mendefinisikan kondisi, dan mengembalikan hasil menggunakan pernyataan select.
Misalnya, kueri berikut mencari literal string yang mungkin berisi kredensial yang dikodekan secara permanen:
/**
* @name Hardcoded credential detection
* @description Finds string literals that may contain passwords
* @kind problem
* @id example/hardcoded-credentials
* @severity warning
*/
import javascript
from Literal l
where l.getValue().toString().matches("%password%")
select l, "Possible hardcoded credential"
Dalam kueri ini:
- Pernyataan
importmemuat model bahasa untuk JavaScript. - Klausa
frommenentukan data yang sedang dianalisis. - Klausa
wherememfilter untuk pola yang cocok. - Pernyataan
selectmenentukan hasil apa yang dikembalikan.
Anda dapat membuat kueri kustom dengan memulai dari kueri standar dan memodifikasi kondisi atau outputnya.
Untuk menggunakan kueri dengan pemindaian kode GitHub, Anda harus menyertakan metadata kueri. Metadata didefinisikan dalam blok komentar di bagian atas file dan mengontrol bagaimana hasil ditafsirkan dan ditampilkan.
Minimal, metadata harus mencakup:
- Pengidentifikasi unik (
@id) - Nama (
@name) - Deskripsi (
@description) - Jenis hasil (
@kind, sepertiproblemataupath-problem)
Properti tambahan seperti @severity dan @precision membantu menentukan bagaimana pemberitahuan muncul di GitHub.
Metadata diperlukan untuk integrasi dengan pemindaian kode. Saat metadata ada, hasil ditampilkan sebagai pemberitahuan di repositori. Jika metadata hilang, CodeQL masih menjalankan kueri, tetapi hasilnya hanya ditampilkan sebagai output mentah dan tidak muncul sebagai pemberitahuan pemindaian kode.
Setelah menentukan kueri atau rangkaian kueri, Anda dapat menyertakannya dalam konfigurasi analisis Anda. Di GitHub Actions, Anda menentukan kueri selama langkah inisialisasi:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
queries: ./path/to/query-suite.qls
Selama alur kerja:
- CodeQL membuat database.
- Menjalankan kueri terpilih.
- Menghasilkan hasil dalam format SARIF.
- Mengunggah hasilnya ke GitHub.
Hasil kueri kustom muncul bersama hasil CodeQL standar di tab Keamanan. Ini memungkinkan Anda untuk memperluas analisis default dengan pemeriksaan yang khusus untuk basis kode Anda sambil tetap mendapat manfaat dari kumpulan kueri GitHub yang dipertahankan.
Metadata kueri
Di bagian sebelumnya, Anda menambahkan metadata ke kueri sehingga dapat digunakan dalam pemindaian kode. Bagian ini menjelaskan bagaimana metadata digunakan dan pengaruhnya terhadap hasil kueri.
Metadata kueri ditentukan dalam blok komentar di bagian .ql atas file. Ini menyediakan informasi tentang kueri dan kontrol bagaimana hasil ditafsirkan dan ditampilkan.
Metadata digunakan oleh CodeQL dan pemindaian kode GitHub untuk:
- Identifikasi kueri dan tujuannya.
- Tentukan bagaimana hasil diklasifikasikan (misalnya,
problemataupath). - Tetapkan tingkat keparahan dan presisi.
- Memformat hasil untuk ditampilkan di repositori.
Misalnya, kueri mungkin menyertakan metadata seperti ini:
/**
* @name Hardcoded credential detection
* @description Finds string literals that may contain passwords
* @kind problem
* @id example/hardcoded-credentials
* @severity warning
*/
Ketika metadata ini ada:
- Hasil dikonversi menjadi format SARIF.
- Pemberitahuan ditampilkan dalam pemindaian kode GitHub.
- Temuan mencakup konteks seperti tingkat keparahan dan deskripsi.
Ketika metadata hilang:
- Kueri masih berjalan.
- Hasil tidak ditampilkan sebagai pemberitahuan.
- Output hanya ditampilkan sebagai tabel mentah.
Metadata juga menentukan bagaimana hasil dikelompokkan dan dilacak di seluruh pemindaian. Misalnya, kueri @id digunakan untuk mencocokkan pemberitahuan di antara eksekusi.
GitHub memiliki panduan gaya yang direkomendasikan untuk metadata kueri. Anda dapat menemukannya dalam dokumentasi CodeQL.
Contoh ini menunjukkan metadata untuk salah satu kueri Java standar:
CodeQL tidak menginterpretasikan kueri yang tidak memiliki metadata. Ini menunjukkan hasil tersebut sebagai tabel dan tidak menampilkannya dalam kode sumber.
Menulis, menguji, dan menjalankan kueri
Setelah Anda membuat kueri kustom, langkah selanjutnya adalah mengujinya, menjalankannya di alur kerja Anda, dan mempertahankannya dari waktu ke waktu.
Saat Anda menulis kueri, Anda menentukan pola yang harus dideteksi CodeQL di basis kode Anda. Cara paling efektif untuk mengembangkan kueri adalah dengan melakukan iterasi secara lokal sebelum menambahkannya ke repositori Anda.
Menguji kueri secara lokal
Anda dapat menguji kueri menggunakan CodeQL CLI atau ekstensi Visual Studio Code.
Dengan CodeQL CLI, Anda menjalankan kueri terhadap database yang telah Anda buat:
codeql database analyze <database> <query.ql>
Perintah ini menjalankan kueri dan menghasilkan hasil yang dapat Anda tinjau dalam SARIF atau format output lainnya.
Anda juga dapat menjalankan:
codeql query run <query.ql> --database=<database>
Pengujian secara lokal memungkinkan Anda untuk:
- Verifikasi bahwa kueri mengembalikan hasil yang diharapkan.
- Menyempurnakan logika kueri.
- Identifikasi positif palsu atau kasus yang terlewat.
Ekstensi Visual Studio Code memberikan pengalaman yang lebih interaktif. Kamu bisa:
- Buka basis data.
- Jalankan kueri langsung dari editor.
- Lihat hasil bersama kode sumber.
Hal ini memudahkan Anda memahami cara kerja kueri Anda dan menyesuaikannya dengan cepat.
Menjalankan kueri dalam pemindaian kode GitHub
Setelah kueri Anda menghasilkan hasil yang diharapkan, Anda dapat menyertakannya dalam alur kerja pemindaian kode Anda.
Dalam GitHub Actions, kueri dikonfigurasi dalam langkah inisialisasi:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
queries: ./path/to/query-suite.qls
Saat alur kerja berjalan:
- CodeQL membuat database untuk repositori.
- Menjalankan kueri terpilih.
- Mengonversi hasil menjadi SARIF.
- Mengunggah hasil ke GitHub.
Hasilnya muncul sebagai pemberitahuan di tab Keamanan , bersama temuan CodeQL standar.
Menjalankan kueri dalam alur kerja memastikan bahwa:
- Analisis dijalankan secara otomatis pada pull request dan cabang.
- Masalah baru terdeteksi saat kode berubah.
- Hasilnya terlihat oleh tim.
Memelihara dan memperbarui kueri
Setelah Anda menambahkan kueri kustom ke alur kerja, Anda mungkin melihat bahwa hasilnya tidak selalu seperti yang Anda harapkan.
Contohnya:
- Kueri mungkin mengembalikan terlalu banyak hasil (positif palsu).
- Ini mungkin melewatkan kasus yang Anda harapkan untuk dideteksi.
- Pola kode baru di repositori Anda mungkin tidak tercakup.
Dalam kasus ini, Anda memperbarui kueri untuk meningkatkan akurasinya.
Mulailah dengan menjalankan kueri secara lokal dan meninjau hasilnya. Lihat lokasi kode yang ditandai dan putuskan apakah lokasi tersebut mewakili masalah nyata. Jika tidak, persempit kondisi dalam where klausul untuk mempersempit hasilnya.
Misalnya, Anda dapat:
- Tambahkan kondisi tambahan untuk mengecualikan pola yang aman.
- Menyesuaikan pencocokan string atau logika aliran data.
- Gunakan kembali predikat dari pustaka yang ada untuk meningkatkan akurasi.
Setelah memperbarui kueri, jalankan lagi terhadap database Anda untuk mengonfirmasi bahwa hasilnya telah ditingkatkan.
Saat Anda menerapkan kueri yang diperbarui, kueri tersebut berjalan secara otomatis dalam alur kerja pemindaian kode Anda. Ini berarti:
- Pemberitahuan yang ada mungkin diperbarui atau dihapus.
- Pemberitahuan baru mungkin muncul berdasarkan logika yang diperbarui.
Seiring waktu, Anda mengulangi proses ini saat basis kode Anda berkembang. Mempertahankan kueri adalah tugas berkelanjutan yang membantu memastikan analisis Anda tetap akurat dan relevan.
Sintaks QL
QL adalah bahasa kueri deklaratif berorientasi objek. Ini dioptimalkan untuk memungkinkan analisis struktur data hierarkis yang efisien, dan khususnya, database yang mewakili artefak perangkat lunak.
Sintaks QL mirip dengan SQL, tetapi semantik QL didasarkan pada Datalog. Datalog adalah bahasa pemrograman logika deklaratif, yang sering digunakan sebagai bahasa kueri. Karena QL terutama merupakan bahasa logika, semua operasi dalam QL adalah operasi logis. QL juga mewarisi predikat rekursif dari Datalog. QL menambahkan dukungan untuk agregat untuk membuat kueri yang kompleks menjadi ringkas dan sederhana.
Bahasa QL terdiri dari rumus logis. Ini menggunakan konektif logis umum seperti and, or, dan not, bersama dengan kuantifer seperti forall dan exists. Karena QL mewarisi predikat rekursif, Anda juga dapat menulis kueri rekursif yang kompleks dengan menggunakan sintaks dan agregat QL dasar seperti count, , sumdan average.
Untuk informasi selengkapnya tentang bahasa QL, lihat dokumentasi CodeQL.
Kueri jalur
Cara informasi mengalir melalui program penting. Data yang tampaknya jinak dapat mengalir dengan cara yang tidak terduga yang memungkinkannya digunakan dengan berbahaya.
Membuat kueri jalur dapat membantu Anda memvisualisasikan alur informasi melalui basis kode. Kueri dapat melacak jalur yang diambil data dari kemungkinan titik awal (sumber) ke titik akhir (sink) yang mungkin. Untuk memodelkan jalur, kueri Anda harus memberikan informasi tentang sumber, sink, dan langkah-langkah aliran data yang menautkannya.
Cara yang paling mudah untuk mulai menulis kueri jalur Anda sendiri adalah dengan menggunakan salah satu kueri yang sudah ada sebagai contoh. Untuk mendapatkan kueri ini untuk bahasa yang didukung, lihat dokumentasi CodeQL.
Kueri jalur Anda memerlukan metadata, predikat kueri, dan struktur pernyataan select tertentu. Banyak kueri jalur bawaan di CodeQL mengikuti struktur dasar. Struktur tergantung pada bagaimana CodeQL memodelkan bahasa yang Anda analisis.
Berikut adalah contoh templat untuk kueri jalur:
/**
* ...
* @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
// 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>"
Dalam templat tersebut:
-
MyConfigurationadalah modul yang berisi predikat yang menentukan bagaimana data mengalir antara sumber dan sink. -
Flowadalah hasil komputasi aliran data berdasarkanMyConfiguration. -
Flow::PathGraphadalah modul grafik aliran data yang dihasilkan yang perlu Anda impor untuk menyertakan penjelasan jalur dalam kueri. -
sourcedansinkadalah simpul dalam grafik seperti yang didefinisikan dalam konfigurasi, danFlow::PathNodeadalah jenisnya. -
DataFlow::Global<..>adalah pemanggilan aliran data. Anda bisa, sebagai gantinya, menggunakanTaintTracking::Global<..>untuk menyertakan langkah-langkah taint bawaan.
Cara menulis kueri jalur
Kueri Anda perlu menghitung grafik jalur untuk menghasilkan penjelasan jalur. Untuk melakukannya, tentukan predikat kueri yang disebut edges. Predikat kueri adalah predikat nonmember dengan anotasi kueri. Anotasi kueri mengembalikan semua pasangan data yang dievaluasi oleh predikat.
Predikat edges menentukan hubungan tepi grafik yang Anda komputasi. Ini digunakan untuk menghitung jalur yang terkait dengan setiap hasil yang dihasilkan kueri Anda. Anda juga dapat mengimpor predikat edges yang telah ditentukan sebelumnya dari modul grafik jalur di salah satu pustaka aliran data standar.
Pustaka aliran data berisi kelas, predikat, dan modul lain yang umumnya digunakan dalam analisis aliran data, selain modul grafik jalur. Pustaka aliran data CodeQL berfungsi dengan memodelkan grafik aliran data atau menerapkan analisis aliran data. Pustaka aliran data normal digunakan untuk menganalisis aliran informasi di mana nilai data dipertahankan di setiap langkah.
Berikut adalah contoh pernyataan yang mengimpor modul PathGraph dari pustaka aliran data (DataFlow.qll), di mana edges ditentukan:
import DataFlow::PathGraph
Anda dapat mengimpor banyak pustaka lain yang disertakan dengan CodeQL. Anda juga dapat mengimpor pustaka yang dirancang khusus untuk menerapkan analisis aliran data di berbagai kerangka kerja dan lingkungan umum.
PathNode Kelas ini dirancang untuk menerapkan analisis aliran data. Ini adalah Node yang dilengkapi dengan konteks panggilan (kecuali untuk sink), jalur akses, dan konfigurasi. Hanya nilai PathNode yang terjangkau dari sumber yang dihasilkan.
Berikut adalah contoh jalur impor:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
Anda dapat secara opsional menentukan predikat kueri nodes, yang menentukan simpul grafik jalur untuk semua bahasa. Saat Anda menentukan nodes, simpul yang dipilih hanya menentukan tepi dengan titik akhir. Saat Anda tidak menentukan nodes, Anda perlu memilih semua titik akhir tepi yang mungkin.
Analisis database
Saat Anda menggunakan kueri untuk menganalisis database CodeQL, Anda menerima hasil yang bermakna dalam konteks kode sumber. Hasilnya ditata sebagai pemberitahuan atau lintasan dalam SARIF atau format terinterpretasi lainnya.
Berikut adalah contoh perintah database CodeQL yang menganalisis database dengan menjalankan kueri yang dipilih terhadapnya dan menginterpretasikan hasilnya:
codeql database analyze \
--format=<format> \
--output=<output> \
[--threads=<num>] \
[--ram=<MB>] \
<options>... \
-- <database> <query|dir|suite>...
Perintah ini menggabungkan efek perintah pipa codeql database run-queries dan codeql database interpret-results.
Atau, Anda dapat menjalankan kueri yang tidak memenuhi persyaratan untuk ditafsirkan sebagai pemberitahuan kode sumber. Untuk melakukannya, gunakan:
codeql database run-queriescodeql query run
Kemudian gunakan:
codeql bqrs decode
untuk mengonversi hasil mentah menjadi notasi yang dapat dibaca.
Anda bisa mendapatkan daftar lengkap perintah CodeQL CLI yang tersedia di manual CodeQL CLI.
Menggunakan file SARIF dengan kategori
CodeQL mendukung SARIF untuk berbagi hasil analisis statis. SARIF dirancang untuk mewakili output dari berbagai alat analisis statis.
Anda perlu menentukan kategori saat menggunakan output SARIF untuk analisis CodeQL. Kategori dapat membedakan beberapa analisis yang dilakukan pada repositori commit yang sama dan pada bahasa yang berbeda atau bagian kode yang berbeda. Namun, file SARIF dengan kategori yang sama saling menimpa.
Anda dapat memindai setiap file output SARIF dengan menggunakan CodeQL untuk menganalisis bahasa yang berbeda dalam basis kode yang sama ketika nilai kategori konsisten antara analisis yang dijalankan. Kami menyarankan agar Anda menggunakan bahasa yang dipindai sebagai pengidentifikasi untuk kategori.
Misalnya, nilai kategori ditampilkan (dengan garis miring di akhir ditambahkan jika belum ada) sebagai:
-
<run>.automationIddi SARIF v1 -
<run>.automationLogicalIddi SARIF v2 -
<run>.automationDetails.iddi SARIF v2.1.0
Memposting hasil SARIF ke GitHub
Setelah database siap, Anda bisa mengkuerinya secara interaktif. Atau Anda dapat menjalankan serangkaian kueri untuk menghasilkan serangkaian hasil dalam format SARIF dan mengunggah hasilnya ke repositori target di 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>...
Untuk mengunggah hasil ke GitHub, pastikan bahwa setiap server integrasi berkelanjutan (CI) memiliki Aplikasi GitHub atau token akses pribadi untuk digunakan codeQL CLI. Anda harus menggunakan token akses atau Aplikasi GitHub dengan izin tulis security_events.
Anda berpotensi mengizinkan CodeQL CLI menggunakan token yang sama jika server CI sudah menggunakan token dengan cakupan ini untuk memeriksa repositori dari GitHub. Jika tidak, buat token baru dengan izin tulis security_events dan tambahkan token ini ke penyimpanan rahasia sistem CI.
Sebagai praktik terbaik keamanan, gunakan flag --github-auth-stdin dan teruskan token tersebut ke perintah melalui masukan standar.
Unggah hasil SARIF
Agar pemindaian kode menampilkan hasil dari alat analisis statis non-Microsoft di repositori GitHub Anda, hasil Anda harus disimpan dalam file SARIF yang mendukung subset tertentu dari skema SARIF 2.1.0 JSON. Anda dapat mengunggah hasilnya dengan menggunakan API pemindaian kode atau CodeQL CLI.
Setiap kali Anda mengunggah hasil pemindaian kode baru, CodeQL memproses hasilnya dan menambahkan pemberitahuan ke repositori. Untuk mencegah pemberitahuan duplikat untuk masalah yang sama, pemindaian kode menggunakan properti SARIF partialFingerprints untuk mencocokkan hasil di seluruh eksekusi sehingga hanya muncul sekali dalam eksekusi terbaru untuk cabang yang dipilih.
Menghilangkan duplikat memungkinkan untuk mencocokkan pemberitahuan dengan baris kode yang benar saat file diedit.
ID aturan untuk sebuah hasil harus tetap sama di berbagai analisis. Data sidik jari secara otomatis disertakan dalam file SARIF yang dibuat melalui alur kerja analisis CodeQL atau runner CodeQL.
Spesifikasi SARIF menggunakan nama properti JSON partialFingerprints, yang merupakan sebuah kamus dari tipe sidik jari bernama ke nilai sidik jari. Properti ini berisi setidaknya nilai untuk primaryLocationLineHash, yang menyediakan sidik jari berdasarkan konteks dari lokasi utama.
GitHub mencoba mengisi bidang partialFingerprints dari file sumber jika Anda mengunggah file SARIF dengan menggunakan tindakan upload-sarif dan data ini hilang.
Selain itu, jika Anda mengunggah file SARIF tanpa data sidik jari dengan menggunakan /code-scanning/sarifs titik akhir API, pengguna mungkin melihat pemberitahuan duplikat saat pemberitahuan pemindaian kode diproses dan ditampilkan.
Untuk menghindari pemberitahuan duplikat saat bekerja dengan alat analisis statis, hitung data sidik jari dan isi partialFingerprints properti sebelum mengunggah file SARIF. Titik awal yang berguna adalah menggunakan skrip yang sama dengan tindakan upload-sarif.