Spuštění CodeQL v databázi
S extrahovaným kódem do databáze ho teď můžete analyzovat pomocí dotazů CodeQL. Odborníci na GitHub, bezpečnostní pracovníci a přispěvatelé komunity zapisují a udržují výchozí dotazy CodeQL. Můžete také napsat vlastní dotazy.
Pomocí dotazů CodeQL v analýze vyhledávání kódu můžete najít problémy ve zdrojovém kódu a identifikovat potenciální ohrožení zabezpečení. Můžete také napsat vlastní dotazy, které identifikují problémy pro každý jazyk, který používáte ve zdrojovém kódu.
Existují dva důležité typy dotazů:
- Dotazy na výstrahy zvýrazňují problémy v konkrétních umístěních kódu.
- Dotazy na cestu popisují tok informací mezi zdrojem a jímkou v kódu.
Jednoduchý dotaz CodeQL
Základní struktura dotazu CodeQL má příponu souboru .ql a obsahuje klauzuli select. Tady je příklad struktury dotazu:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... */
where /* ... logical formula ... */
select /* ... expressions ... */
Přizpůsobení dotazů
Analýza CodeQL se řídí dotazy. I když můžete použít standardní dotazy poskytované GitHub, můžete také přizpůsobit analýzu tak, že napíšete vlastní dotazy a uspořádáte je do balíčků dotazů.
Dotazy se obvykle seskupují do balíčků dotazů, což jsou adresáře obsahující dotazy, sdílené knihovny a konfigurační soubory. Sada dotazů umožňuje definovat opakovaně použitelnou sadu pravidel analýzy pro vaše projekty. V rámci balíčku můžete zahrnout jednotlivé .ql soubory, pomocné knihovny, které definují opakovaně použitelnou logiku, a sady dotazů, které seskupují více dotazů.
Sada dotazů (.qlssoubor) slouží k řízení, které dotazy se spouštějí během analýzy. Místo spouštění dotazů po jednom definujete sadu, která obsahuje seznam všech dotazů, které chcete spustit. Příklady:
- description: Custom security queries
- queries:
- ./queries/hardcoded-credentials.ql
- ./queries/insecure-config.ql
Tato sada seskupí několik dotazů, aby se mohly spouštět společně jako součást jedné analýzy.
Vlastní dotazy můžete vytvářet zápisem .ql souborů. Dotaz popisuje v kódu vzor, který chcete zjistit. Obvykle importuje jazykovou knihovnu, definuje podmínky a vrací výsledky pomocí select příkazu.
Například následující dotaz vyhledá řetězcové literály, které můžou obsahovat pevně zakódované přihlašovací údaje:
/**
* @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"
V tomto dotazu:
- Příkaz
importnačte jazykový model pro JavaScript. - Klauzule
fromdefinuje analyzovaná data. - Klauzule
wherefiltruje odpovídající vzory. - Příkaz
selectdefinuje, jaké výsledky se vrátí.
Vlastní dotazy můžete vytvářet tak, že začnete od standardních dotazů a upravíte jejich podmínky nebo výstup.
Pokud chcete použít dotaz s kontrolou kódu GitHub, musíte zahrnout metadata dotazu. Metadata se definují v bloku komentáře v horní části souboru a řídí, jak se interpretují a zobrazují výsledky.
Metadata by měla obsahovat minimálně:
- Jedinečný identifikátor (
@id) - Název (
@name) - Popis (
@description) - Typ výsledku (
@kindnapříkladproblemnebopath-problem)
Další vlastnosti, například @severity a @precision pomáhají určit, jak se výstrahy zobrazují v GitHub.
Metadata se vyžadují pro integraci se skenováním kódu. Když jsou metadata přítomna, výsledky se v úložišti zobrazí jako výstrahy. Pokud chybí metadata, CodeQL dotaz pořád spouští, ale výsledky se zobrazují jenom jako nezpracovaný výstup a nezobrazují se jako výstrahy kontroly kódu.
Jakmile definujete dotazy nebo sadu dotazů, můžete je zahrnout do konfigurace analýzy. V GitHub Actions zadáte dotazy během inicializačního kroku:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
queries: ./path/to/query-suite.qls
Během pracovního postupu:
- CodeQL vytvoří databázi.
- Spustí vybrané dotazy.
- Generuje výsledky ve formátu SARIF.
- Nahraje výsledky do GitHub.
Výsledky vlastních dotazů se zobrazují vedle standardních výsledků CodeQL na kartě Zabezpečení. To vám umožní rozšířit výchozí analýzu pomocí kontrol, které jsou specifické pro váš základ kódu, a zároveň využívat výhod spravovaných sad dotazů GitHub.
Metadata dotazů
V předchozí části jste do dotazu přidali metadata, aby se dala použít při kontrole kódu. Tato část vysvětluje, jak se používají metadata a jak ovlivňují výsledky dotazů.
Metadata dotazu se definují v bloku komentáře v horní části .ql souboru. Poskytuje informace o dotazu a ovládacích prvcích, jak se interpretují a zobrazují výsledky.
Metadata používají CodeQL a GitHub Code Scanning k tomu, aby:
- Identifikujte dotaz a jeho účel.
- Určete, jak jsou výsledky klasifikovány (například
problemnebopath). - Přiřaďte úrovně závažnosti a přesnosti.
- Výsledky formátování pro zobrazení v úložišti
Dotaz může například obsahovat metadata podobná tomuto:
/**
* @name Hardcoded credential detection
* @description Finds string literals that may contain passwords
* @kind problem
* @id example/hardcoded-credentials
* @severity warning
*/
Pokud jsou tato metadata k dispozici:
- Výsledky se převedou do formátu SARIF.
- Výstrahy se zobrazují při kontrole kódu GitHub.
- Závěry zahrnují kontext, jako je závažnost a popis.
Pokud chybí metadata:
- Dotaz stále běží.
- Výsledky se nezobrazují jako výstrahy.
- Výstup se zobrazí jenom jako nezpracované tabulky.
Metadata také určují, jak jsou výsledky seskupovány a sledovány napříč skeny. Například dotaz @id se používá k párování výstrah mezi jednotlivými spuštěními.
GitHub obsahuje doporučeného průvodce stylem pro metadata dotazů. Najdete ho v dokumentaci CodeQL.
Tento příklad ukazuje metadata pro jeden ze standardních dotazů v Javě:
CodeQL neinterpretuje dotazy, které nemají metadata. Zobrazí tyto výsledky jako tabulku a nezobrazí je ve zdrojovém kódu.
Psaní, testování a spouštění dotazů
Po vytvoření vlastních dotazů je dalším krokem jejich otestování, spuštění v pracovních postupech a jejich údržba v průběhu času.
Při psaní dotazu definujete vzor, který by CodeQL měl rozpoznat v základu kódu. Nejúčinnější způsob, jak vyvíjet dotazy, je iterovat místně před jejich přidáním do úložiště.
Místní testování dotazů
Dotazy můžete testovat pomocí rozhraní příkazového řádku CodeQL nebo rozšíření Visual Studio Code.
Pomocí rozhraní příkazového řádku CodeQL spustíte dotazy na databázi, kterou jste už vytvořili:
codeql database analyze <database> <query.ql>
Tento příkaz spustí dotaz a vytvoří výsledky, které můžete zkontrolovat v SARIF nebo jiném výstupním formátu.
Můžete také spustit:
codeql query run <query.ql> --database=<database>
Místní testování umožňuje:
- Ověřte, že dotaz vrací očekávané výsledky.
- Upřesněte logiku dotazu.
- Identifikujte falešně pozitivní nebo chybějící případy.
Rozšíření Visual Studio Code poskytuje interaktivnější prostředí. Máte tyto možnosti:
- Otevřete databázi.
- Spouštět dotazy přímo z editoru
- Zobrazte výsledky společně se zdrojovým kódem.
To usnadňuje pochopení chování dotazu a jeho rychlé úpravy.
Spouštění dotazů při kontrole kódu GitHub
Jakmile dotaz vytvoří očekávané výsledky, můžete ho zahrnout do pracovního postupu kontroly kódu.
V GitHub Actions se dotazy konfigurují v kroku inicializace:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
queries: ./path/to/query-suite.qls
Při spuštění pracovního postupu:
- CodeQL vytvoří databázi pro úložiště.
- Spustí vybrané dotazy.
- Převede výsledky na SARIF.
- Nahraje výsledky do GitHub.
Výsledky se zobrazí jako výstrahy na kartě Zabezpečení spolu se standardními zjištěními CodeQL.
Spouštění dotazů v pracovních postupech zajišťuje, že:
- Analýza se spouští automaticky na pull requestech a větvích.
- Nové problémy se detekují jako změny kódu.
- Výsledky jsou viditelné pro tým.
Údržba a aktualizace dotazů
Po přidání vlastního dotazu do pracovního postupu si můžete všimnout, že výsledky nejsou vždy očekávané.
Příklady:
- Dotaz může vrátit příliš mnoho výsledků (falešně pozitivní výsledky).
- Může přehlédnout případy, u kterých jste očekávali, že je odhalí.
- Nové vzory kódu v úložišti nemusí být pokryté.
V těchto případech aktualizujete dotaz tak, aby se zlepšila jeho přesnost.
Začněte tím, že dotaz spustíte místně a zkontrolujete výsledky. Podívejte se na místa v kódu, která byla označena, a rozhodněte se, zda představují skutečné problémy. Pokud ne, upřesněte podmínky v where klauzuli, aby se výsledky zúžily.
Můžete například:
- Přidejte další podmínky pro vyloučení bezpečných vzorů.
- Upravte porovnávání řetězců nebo logiku toku dat.
- Znovu použijte predikáty z existujících knihoven, aby se zlepšila přesnost.
Po aktualizaci dotazu spusťte dotaz znovu v databázi a ověřte, že se výsledky zlepšily.
Když potvrdíte aktualizovaný dotaz, spustí se automaticky v pracovním postupu kontroly kódu. To znamená:
- Existující výstrahy mohou být aktualizovány nebo odebrány.
- Na základě aktualizované logiky se můžou objevit nové výstrahy.
V průběhu času tento proces opakujete při vývoji základu kódu. Udržování dotazů je průběžný úkol, který pomáhá zajistit, aby vaše analýza zůstala přesná a relevantní.
Syntaxe QL
QL je deklarativní dotazovací jazyk orientovaný na objekty. Je optimalizovaná tak, aby umožňovala efektivní analýzu hierarchických datových struktur a zejména databází, které představují softwarové artefakty.
Syntaxe jazyka QL je podobná jazyku SQL, ale sémantika QL je založená na datovém protokolu. Datalog je deklarativní programovací jazyk logiky, který se často používá jako dotazovací jazyk. Vzhledem k tomu, že QL je primárně logický jazyk, všechny operace v QL jsou logické operace. QL také dědí rekurzivní predikáty z datalogu. QL přidává podporu agregací, aby byly i složité dotazy stručné a jednoduché.
Jazyk QL se skládá z logických vzorců. Používá běžné logické spojovací prvky, jako jsou and, ora not, spolu s kvantifikátory, jako jsou forall a exists. Protože QL dědí rekurzivní predikáty, můžete také psát složité rekurzivní dotazy pomocí základní syntaxe QL a agregace jako count, suma average.
Další informace o jazyce QL najdete v dokumentaci codeQL.
Dotazy na cestu
Způsob, jakým informace procházejí programem, je důležité. Data, která se zdá neškodná, můžou proudit neočekávanými způsoby, které umožňují jejich použití se zlými úmysly.
Vytváření dotazů cest vám může pomoct vizualizovat tok informací prostřednictvím základu kódu. Dotaz může sledovat, kudy data procházejí od svých možných počátečních bodů (zdroj) ke svým možným koncovým bodům (cíl). Pokud chcete modelovat cesty, musí dotaz poskytnout informace o zdroji, jímce a krocích toku dat, které je propojí.
Nejjednodušší způsob, jak začít psát vlastní dotaz cesty, je použít jeden z existujících dotazů jako šablonu. Pokud chcete získat tyto dotazy pro podporované jazyky, přečtěte si dokumentaci CodeQL.
Dotaz cesty vyžaduje určitá metadata, predikáty dotazů a struktury příkazů select. Mnoho předdefinovaných dotazů na cestu v CodeQL se řídí základní strukturou. Struktura závisí na tom, jak CodeQL modeluje jazyk, který analyzujete.
Tady je příklad šablony pro dotaz cesty:
/**
* ...
* @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>"
V této šabloně:
-
MyConfigurationje modul, který obsahuje predikáty, které definují, jak data proudí mezi zdrojem a jímkou. -
Flowje výsledkem výpočtu toku dat na základěMyConfiguration. -
Flow::PathGraphje výsledný modul grafu toku dat, který potřebujete importovat, abyste do dotazu zahrnuli vysvětlení cest. -
sourceasinkjsou uzly v grafu definované v konfiguraci aFlow::PathNodeje jejich typ. -
DataFlow::Global<..>je vyvolání toku dat. Místo toho můžete použítTaintTracking::Global<..>k zahrnutí výchozí sady kroků ovlivnění.
Jak napsat dotaz na cestu
Váš dotaz potřebuje vypočítat graf cest, aby bylo možné vygenerovat vysvětlení cest. Chcete-li to provést, definujte predikát dotazu s názvem edges. Predikát dotazu je nečlenský predikát s poznámkami k dotazu. Anotace dotazu vrátí všechny n-tice, které predikát vyhodnocuje.
Predikát edges definuje hraniční vztahy grafu, který počítáte. Používá se k výpočtu cest souvisejících s každým výsledkem, který dotaz vygeneruje. Můžete také importovat předdefinovaný edges predikát z modulu grafu cest v jedné ze standardních knihoven toku dat.
Knihovny toku dat obsahují kromě modulů grafu cesty také další třídy, predikáty a moduly, které se běžně používají při analýze toku dat. Knihovny toku dat CodeQL fungují tak, že modelují graf toku dat nebo implementují analýzu toku dat. Normální knihovny toku dat se používají k analýze toku informací, ve kterém se hodnoty dat zachovají v každém kroku.
Tady je příklad příkazu, který importuje modul PathGraph z knihovny toku dat (DataFlow.qll), ve kterém je definován edges:
import DataFlow::PathGraph
Do CodeQL můžete importovat mnoho dalších knihoven. Můžete také importovat knihovny navržené speciálně pro implementaci analýzy toku dat v různých běžných architekturách a prostředích.
Třída PathNode je navržená k implementaci analýzy toku dat. Rozšiřuje se Node o kontext volání (s výjimkou jímek), přístupovou cestu a konfiguraci. Pouze hodnoty PathNode, které jsou dostupné ze zdroje, se vygenerují.
Tady je příklad cesty importu:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
Volitelně můžete definovat predikát dotazu nodes, který určuje uzly grafu cest pro všechny jazyky. Když definujete nodes, vybrané uzly definují pouze hrany s koncovými body. Pokud nedefinujete nodes, musíte vybrat všechny možné koncové body hran.
Analýza databáze
Když k analýze databáze CodeQL použijete dotazy, získáte smysluplné výsledky v kontextu zdrojového kódu. Výsledky jsou formátovány jako výstrahy nebo cesty v SARIF nebo v jiném interpretovaném formátu.
Tady je příklad databázového příkazu CodeQL, který analyzuje databázi spuštěním vybraných dotazů a interpretací výsledků:
codeql database analyze \
--format=<format> \
--output=<output> \
[--threads=<num>] \
[--ram=<MB>] \
<options>... \
-- <database> <query|dir|suite>...
Tento příkaz kombinuje efekt codeql database run-queries a codeql database interpret-results instalatérských příkazů.
Alternativně můžete spouštět dotazy, které nesplňují požadavky na interpretaci jako upozornění zdrojového kódu. Uděláte to takto:
codeql database run-queriescodeql query run
Pak použijte:
codeql bqrs decode
a převést nezpracované výsledky do čitelného formátu.
Úplný seznam dostupných příkazů rozhraní příkazového řádku CodeQL získáte v příručce k rozhraní příkazového řádku CodeQL.
Použití souboru SARIF s kategoriemi
CodeQL podporuje SARIF pro sdílení výsledků statické analýzy. SARIF je navržený tak, aby představoval výstup široké škály nástrojů pro statickou analýzu.
Při použití výstupu SARIF pro analýzu CodeQL je potřeba zadat kategorii . Kategorie mohou rozlišovat více analýz provedených ve stejném úložišti potvrzení a v různých jazycích nebo v různých částech kódu. Soubory SARIF se stejnou kategorií se však vzájemně přepíší.
Každý výstupní soubor SARIF můžete prohledávat pomocí CodeQL k analýze různých jazyků ve stejném základu kódu, pokud je hodnota kategorie konzistentní mezi spuštěním analýzy. Doporučujeme použít jazyk, který se prohledává jako identifikátor kategorie.
Zobrazí se například hodnota kategorie (s připojeným koncovým lomítkem, pokud ještě není k dispozici) jako:
-
<run>.automationIdv SARIF v1 -
<run>.automationLogicalIdv SARIF v2 -
<run>.automationDetails.idv SARIF v2.1.0
Publikování výsledků SARIF na GitHub
Jakmile je databáze připravená, můžete ji interaktivně dotazovat. Nebo můžete spustit sadu dotazů, které vygenerují sadu výsledků ve formátu SARIF a nahrají výsledky do cílového úložiště na 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>...
Pokud chcete nahrát výsledky na GitHub, ujistěte se, že každý server kontinuální integrace (CI) má pro použití rozhraní příkazového řádku CodeQL token aplikace GitHub nebo osobní přístupový token. Musíte použít přístupový token nebo aplikaci GitHubu s security_events oprávněním k zápisu.
Pokud servery CI již používají token s tímto oborem ke klonování úložišť z GitHubu, můžete potenciálně rozhraní příkazového řádku CodeQL povolit používat stejný token. V opačném případě vytvořte nový token s oprávněním k zápisu security_events a přidejte tento token do úložiště tajných kódů systému CI.
Osvědčeným postupem zabezpečení je použít --github-auth-stdin příznak a předat token příkazu standardním vstupem.
Nahrání výsledků SARIF
Aby kontrola kódu zobrazila výsledky z nástroje pro statickou analýzu jiného než Microsoftu v úložišti GitHub, musí být výsledky uložené v souboru SARIF, který podporuje konkrétní podmnožinu schématu JSON SARIF 2.1.0. Výsledky můžete nahrát pomocí rozhraní API pro kontrolu kódu nebo rozhraní příkazového řádku CodeQL.
Pokaždé, když nahrajete výsledky nové kontroly kódu, CodeQL zpracuje výsledky a přidá do úložiště upozornění. Aby se zabránilo duplicitním výstrahám pro stejný problém, vyhledávání kódu používá vlastnost SARIF partialFingerprints ke shodě výsledků napříč spuštěními, aby se zobrazovaly pouze jednou v nejnovějším spuštění pro vybranou větev.
Odstranění duplicit umožňuje shodovat výstrahy se správným řádkem kódu při úpravách souborů.
ID pravidla pro výsledek musí být stejné napříč analýzami. Data otisků prstů se automaticky zahrnou do souborů SARIF vytvořených prostřednictvím pracovního postupu analýzy CodeQL nebo spouštěče CodeQL.
Specifikace SARIF používají vlastnost JSON s názvem partialFingerprints, což je slovník z pojmenovaných typů otisků k otisku. Tato vlastnost obsahuje minimálně hodnotu pro primaryLocationLineHash, která poskytuje otisk prstu na základě kontextu primárního umístění.
GitHub se pokusí vyplnit pole partialFingerprints ze zdrojových souborů, pokud nahrajete soubor SARIF pomocí akce upload-sarif a tato data chybí.
Pokud navíc nahrajete soubor SARIF bez dat otisku prstu pomocí koncového /code-scanning/sarifs bodu rozhraní API, můžou se uživatelům při zpracování a zobrazení výstrah kontroly kódu zobrazit duplicitní výstrahy.
Abyste se vyhnuli duplicitním výstrahám při práci s nástroji pro statickou analýzu, vypočítejte data otisku prstu a před nahráním souboru SARIF naplňte partialFingerprints vlastnost. Užitečným výchozím bodem je použít stejný skript jako akci upload-sarif.