Kør CodeQL i en database

Fuldført

Når din kode er udpakket til en database, kan du nu analysere den ved hjælp af CodeQL-forespørgsler. GitHub-eksperter, sikkerhedsforskere og communitybidragydere skriver og vedligeholder standard-CodeQL-forespørgsler. Du kan også skrive dine egne forespørgsler.

Du kan bruge CodeQL-forespørgsler i kodescanningsanalyse for at finde problemer i din kildekode og identificere potentielle sikkerhedssårbarheder. Du kan også skrive brugerdefinerede forespørgsler for at identificere problemer for hvert sprog, du bruger i din kildekode.

Der er to vigtige typer forespørgsler:

  • Beskedforespørgsler fremhæve problemer på bestemte placeringer af din kode.
  • Stiforespørgsler beskrive informationsflowet mellem en kilde og en vask i din kode.

Enkel CodeQL-forespørgsel

Den grundlæggende CodeQL-forespørgselsstruktur har filtypenavnet .ql og indeholder en select-delsætning. Her er et eksempel på en forespørgselsstruktur:

/**
 *
 * Query metadata
 *
 */
import /* ... CodeQL libraries or modules ... */

/* ... Optional, define CodeQL classes and predicates ... */

from /* ... variable declarations ... */
where /* ... logical formula ... */
select /* ... expressions ... */

Forespørgselstilpasning

CodeQL-analyse drives af forespørgsler. Selvom du kan bruge de standardforespørgsler, som GitHub tilbyder, kan du også tilpasse analysen ved at skrive dine egne forespørgsler og organisere dem i forespørgselspakker.

Forespørgsler grupperes typisk i forespørgselspakker, som er mapper, der indeholder forespørgsler, delte biblioteker og konfigurationsfiler. En forespørgselspakke giver dig mulighed for at definere et genanvendelig sæt analyseregler til dine projekter. Inden for en pakke kan du inkludere individuelle .ql filer, hjælpebiblioteker, der definerer genanvendelig logik, og forespørgselssuiter, der grupperer flere forespørgsler sammen.

En forespørgselssuite (.qls fil) bruges til at styre, hvilke forespørgsler der kører under analysen. I stedet for at køre forespørgsler én ad gangen, definerer du en suite, der lister alle de forespørgsler, du ønsker at udføre. Eksempel:

- description: Custom security queries
- queries:
  - ./queries/hardcoded-credentials.ql
  - ./queries/insecure-config.ql

Denne suite grupperer flere forespørgsler, så de kan køre sammen som en del af en enkelt analyse.

Du kan lave dine egne forespørgsler ved at skrive .ql filer. En forespørgsel beskriver et mønster i din kode, som du vil opdage. Den importerer typisk et sprogbibliotek, definerer betingelser og returnerer resultater ved hjælp af en select sætning.

For eksempel leder følgende forespørgsel efter strengliteraler, der kan indeholde hardkodede legitimationsoplysninger:

/**
 * @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"

I denne forespørgsel:

  • Sætningen import indlæser sprogmodellen for JavaScript.
  • Klausulen from definerer de data, der analyseres.
  • Klausulen where filtrerer efter matchende mønstre.
  • Udsagnet select definerer, hvilke resultater der returneres.

Du kan bygge brugerdefinerede forespørgsler ved at starte med standardforespørgsler og ændre deres betingelser eller output.

For at bruge en forespørgsel med GitHub-kodescanning skal du inkludere forespørgselsmetadata. Metadata defineres i en kommentarblok øverst i filen og styrer, hvordan resultaterne fortolkes og vises.

Som minimum bør metadata omfatte:

  • En unik identifikator (@id)
  • Et navn (@name)
  • En beskrivelse (@description)
  • En resultattype (@kind, såsom problem eller path-problem)

Yderligere egenskaber såsom @severity og @precision hjælper med at bestemme, hvordan advarsler vises i GitHub.

Metadata er nødvendige for integration med kodescanning. Når metadata er til stede, vises resultaterne som advarsler i arkivet. Hvis metadata mangler, kører CodeQL stadig forespørgslen, men resultaterne vises kun som rå output og vises ikke som kodescanningsadvarsler.

Når du har defineret dine forespørgsler eller din forespørgselssuite, kan du inkludere dem i din analysekonfiguration. I GitHub Actions specificerer du forespørgslerne under initialiseringstrinnet:

- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    queries: ./path/to/query-suite.qls

Under arbejdsgangen:

  1. CodeQL opretter databasen.
  2. Kører de valgte forespørgsler.
  3. Genererer resultater i SARIF-format.
  4. Uploader resultaterne til GitHub.

Brugerdefinerede forespørgselsresultater vises sammen med standard CodeQL-resultater i fanen Sikkerhed. Dette giver dig mulighed for at udvide standardanalysen med kontroller, der er specifikke for din kodebase, samtidig med at du drager fordel af GitHub's vedligeholdte forespørgselssæt.

Forespørgselsmetadata

I det forrige afsnit tilføjede du metadata til en forespørgsel, så den kunne bruges i kodescanning. Dette afsnit forklarer, hvordan metadata bruges, og hvordan det påvirker forespørgselsresultater.

Forespørgselsmetadata er defineret i en kommentarblok øverst i en .ql fil. Den giver information om forespørgslen og styrer, hvordan resultaterne fortolkes og vises.

Metadata bruges af CodeQL og GitHub-kodescanning til at:

  • Identificer forespørgslen og dens formål.
  • Bestem hvordan resultaterne klassificeres (for eksempel problem eller path).
  • Tildel sværhedsgrad og præcisionsniveauer.
  • Formater resultater til visning i repositoryet.

For eksempel kan en forespørgsel indeholde metadata som denne:

/**
 * @name Hardcoded credential detection
 * @description Finds string literals that may contain passwords
 * @kind problem
 * @id example/hardcoded-credentials
 * @severity warning
 */

Når denne metadata er til stede:

  • Resultaterne konverteres til SARIF-format.
  • Advarsler vises i GitHub-kodescanning.
  • Resultaterne inkluderer kontekst såsom alvorlighed og beskrivelse.

Når metadata mangler:

  • Forespørgslen kører stadig.
  • Resultater vises ikke som advarsler.
  • Output vises kun som rå tabeller.

Metadata bestemmer også, hvordan resultaterne grupperes og spores på tværs af scanninger. For eksempel bruges forespørgslen @id til at matche advarsler mellem kørsler.

GitHub har en anbefalet typografivejledning til forespørgselsmetadata. Du kan finde det i CodeQL-dokumentationen.

I dette eksempel vises metadata for en af Java-standardforespørgslerne:

Skærmbillede af forespørgselsmetadata for en standard Java CodeQL-forespørgsel.

CodeQL fortolker ikke forespørgsler, der ikke har metadata. Det viser disse resultater som en tabel og viser dem ikke i kildekoden.

Skrivning, test og kørsel af forespørgsler

Efter du har oprettet brugerdefinerede forespørgsler, er næste skridt at teste dem, køre dem i dine arbejdsgange og vedligeholde dem over tid.

Når du skriver en forespørgsel, definerer du et mønster, som CodeQL skal opdage i din kodebase. Den mest effektive måde at udvikle forespørgsler på er at iterere lokalt, før du tilføjer dem til dit repository.

Testforespørgsler lokalt

Du kan teste forespørgsler ved hjælp af CodeQL CLI eller Visual Studio Code-udvidelsen.

Med CodeQL CLI kører du forespørgsler mod en database, du allerede har oprettet:

codeql database analyze <database> <query.ql>

Denne kommando kører forespørgslen og giver resultater, som du kan gennemgå i SARIF eller et andet outputformat.

Du kan også løbe:

codeql query run <query.ql> --database=<database>

Lokal test giver dig mulighed for at:

  • Verificér, at forespørgslen returnerer de forventede resultater.
  • Forfin forespørgselslogikken.
  • Identificer falske positive eller manglende tilfælde.

Visual Studio Code-udvidelsen giver en mere interaktiv oplevelse. Du kan:

  • Åbn en database.
  • Kør forespørgsler direkte fra editoren.
  • Se resultater sammen med kildekoden.

Det gør det lettere at forstå, hvordan din forespørgsel opfører sig, og justere den hurtigt.

Kør forespørgsler i GitHub-kodescanning

Når din forespørgsel har givet de forventede resultater, kan du inkludere den i din kodescanningsarbejdsgang.

I GitHub Actions konfigureres forespørgsler i initialiseringstrinnet:

- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    queries: ./path/to/query-suite.qls

Når arbejdsgangen kører:

  1. CodeQL opretter en database til repositoryet.
  2. Udfører de valgte forespørgsler.
  3. Konverterer resultater til SARIF.
  4. Uploader resultater til GitHub.

Resultaterne vises som advarsler i fanen Sikkerhed , sammen med standard CodeQL-fund.

Kørsel af forespørgsler i arbejdsgange sikrer, at:

  • Analyse kører automatisk på pull requests og branches.
  • Nye problemer opdages ved kodeændringer.
  • Resultaterne er synlige for teamet.

Vedligehold og opdater forespørgsler

Efter du har tilføjet en brugerdefineret forespørgsel til din arbejdsgang, vil du måske bemærke, at resultaterne ikke altid er, som du forventer.

Eksempel:

  • En forespørgsel kan give for mange resultater (falske positiver).
  • Den kan overse sager, som du forventede, den ville opdage.
  • Nye kodemønstre i dit repository er måske ikke dækket.

I disse tilfælde opdaterer du forespørgslen for at forbedre dens nøjagtighed.

Start med at køre forespørgslen lokalt og gennemgå resultaterne. Se på de kodeplaceringer, der blev markeret, og vurder, om de repræsenterer reelle problemer. Hvis ikke, forfines betingelserne i klausulen where for at indsnævre resultaterne.

Du kan f.eks.:

  • Tilføj yderligere betingelser for at udelukke sikre mønstre.
  • Juster strengmatchning eller dataflow-logik.
  • Genbrug prædikater fra eksisterende biblioteker for at forbedre nøjagtigheden.

Efter opdatering af forespørgslen skal du køre den igen mod din database for at bekræfte, at resultaterne er blevet bedre.

Når du committer den opdaterede forespørgsel, kører den automatisk i din kodescanningsarbejdsgang. Det betyder følgende:

  • Eksisterende advarsler kan blive opdateret eller fjernet.
  • Nye advarsler kan dukke op baseret på den opdaterede logik.

Over tid gentager du denne proces, efterhånden som din kodebase udvikler sig. Vedligeholdelse af forespørgsler er en løbende opgave, der hjælper med at sikre, at din analyse forbliver nøjagtig og relevant.

QL-syntaks

QL er et deklarativt objektorienteret forespørgselssprog. Den er optimeret til at muliggøre effektiv analyse af hierarkiske datastrukturer og især databaser, der repræsenterer softwareartefakter.

QL-syntaksen ligner SQL, men semantikken i QL er baseret på Datalog. Datalog er et deklarativt programmeringssprog til logik, som ofte bruges som forespørgselssprog. Da QL primært er et logiksprog, er alle handlinger i QL logiske handlinger. QL arver også rekursive prædikater fra Datalog. QL tilføjer understøttelse af aggregeringer for at gøre selv komplekse forespørgsler præcise og enkle.

QL-sproget består af logiske formler. Der bruges almindelige logiske forbindelsesstoffer, f.eks. and, orog notsammen med kvantificerbare elementer, f.eks. forall og exists. Da QL nedarver rekursive prædikater, kan du også skrive komplekse rekursive forespørgsler ved hjælp af grundlæggende QL-syntaks og samlinger som count, sumog average.

For mere information om QL-sproget, se CodeQL-dokumentationen.

Stiforespørgsler

Den måde, oplysninger flyder gennem et program på, er vigtig. Data, der ser ud til at være godartede, kan flyde på uventede måder, der gør det muligt at bruge dem skadeligt.

Oprettelse af stiforespørgsler kan hjælpe dig med at visualisere informationsflowet via en kodebase. En forespørgsel kan spore den sti, som dataene tager fra dens mulige startpunkter (kilde) til dens mulige endepunkter (sink). I forbindelse med modelleringsstier skal forespørgslen indeholde oplysninger om kilden, vasken og de dataflowtrin, der linker dem.

Den nemmeste måde at begynde at skrive din egen stiforespørgsel på er ved at bruge en af de eksisterende forespørgsler som en skabelon. For at få disse forespørgsler for understøttede sprog, se CodeQL-dokumentationen.

Stiforespørgslen kræver visse metadata, forespørgsels prædikater og select sætningsstrukturer. Mange af de indbyggede stiforespørgsler i CodeQL følger en grundlæggende struktur. Strukturen afhænger af, hvordan CodeQL modellerer det sprog, du analyserer.

Her er en eksempelskabelon til en stiforespørgsel:

/**
 * ...
 * @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>"

I denne skabelon:

  • MyConfiguration er en modul, der indeholder de prædikater, der definerer, hvordan data flyder mellem kilde og sænk.
  • Flow er resultatet af dataflowberegningen baseret på MyConfiguration.
  • Flow::PathGraph er det resulterende dataflowdiagrammodul, som du skal importere for at medtage stiforklaringer i forespørgslen.
  • source og sink er noder i grafen som defineret i konfigurationen, og Flow::PathNode er deres type.
  • DataFlow::Global<..> er en aktivering af dataflowet. Du kan i stedet bruge TaintTracking::Global<..> til at inkludere et standardsæt af tainttrin.

Sådan skriver du en stiforespørgsel

Forespørgslen skal beregne et kurvediagram for at kunne generere forklaringer af stien. Det gør du ved at definere et forespørgsels prædikat med navnet edges. Et forespørgselsprædikat er et ikke-medlemsprædikat med en forespørgselsannotation. Forespørgselsanmærkningen returnerer alle de tuples, som prædikatet evaluerer.

Prædikatet edges definerer kantrelationerne for den graf, du beregner. Den bruges til at beregne de stier, der er relateret til hvert resultat, som din forespørgsel genererer. Du kan også importere et foruddefineret edges prædikat fra et stidiagrammodul i et af standardbibliotekerne for dataflow.

Dataflowbibliotekerne indeholder de andre klasser, prædikater og moduler, der ofte bruges i dataflowanalyse ud over stidiagrammodulet. Funktionen CodeQL-dataflowbiblioteker ved at modellere dataflowgrafen eller implementere dataflowanalyse. Normale dataflowbiblioteker bruges til at analysere det informationsflow, hvor dataværdier bevares på hvert trin.

Her er et eksempel på en sætning, der importerer PathGraph-modulet fra det dataflowbibliotek (DataFlow.qll), hvor edges er defineret:

import DataFlow::PathGraph

Du kan importere mange andre biblioteker, der er inkluderet i CodeQL. Du kan også importere biblioteker, der er udviklet specielt til at implementere dataflowanalyser i forskellige fælles strukturer og miljøer.

Klassen PathNode er designet til at implementere dataflowanalyse. Den er Node udvidet med en kaldskontekst (undtagen for sinks), en adgangssti og en konfiguration. Der genereres kun PathNode værdier, der kan nås fra en kilde.

Her er et eksempel på importstien:

import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl

Du kan eventuelt definere et nodes prædikat for en forespørgsel, som angiver noderne i stigrafen for alle sprog. Når du definerer nodes, definerer de valgte noder kun kanter med slutpunkter. Når du ikke definerer nodes, skal du vælge alle mulige endepunkter af kanter.

Databaseanalyse

Når du bruger forespørgsler til at analysere en CodeQL-database, modtager du meningsfulde resultater i forbindelse med kildekoden. Resultaterne er formateret som beskeder eller stier i SARIF eller et andet fortolket format.

Her er et eksempel på en CodeQL-databasekommando, der analyserer databasen ved at køre valgte forespørgsler mod den og fortolke resultaterne:

codeql database analyze \
  --format=<format> \
  --output=<output> \
  [--threads=<num>] \
  [--ram=<MB>] \
  <options>... \
  -- <database> <query|dir|suite>...

Denne kommando kombinerer effekten af codeql database run-queries og codeql database interpret-results VVS-kommandoer.

Du kan også køre forespørgsler, der ikke opfylder kravene til at blive fortolket som kildekodebeskeder. For at gøre dette, brug følgende:

  • codeql database run-queries
  • codeql query run

Brug derefter:

codeql bqrs decode

for at konvertere råresultaterne til en læsbar notation.

Du kan få en komplet liste over tilgængelige CodeQL CLI-kommandoer i CodeQL CLI-manualen.

Brug en SARIF-fil med kategorier

CodeQL understøtter SARIF til deling af statiske analyseresultater. SARIF er designet til at repræsentere resultatet af en lang række statiske analyseværktøjer.

Du skal specificere en kategori, når du bruger SARIF-output til CodeQL-analyse. Kategorier kan skelne mellem flere analyser, der udføres på det samme bekræftelseslager og på forskellige sprog eller forskellige dele af koden. SARIF-filer med samme kategori overskriver dog hinanden.

Du kan scanne hver SARIF-outputfil ved hjælp af CodeQL for at analysere forskellige sprog inden for den samme kodebase, når kategoriværdien er konsistent mellem analysekørslerne. Vi anbefaler, at du bruger det sprog, der scannes, som et id for kategorien.

For eksempel vises kategoriværdien (med en efterslæbende skråstreg tilføjet, hvis den ikke allerede er til stede) som:

  • <run>.automationId i SARIF v1
  • <run>.automationLogicalId i SARIF v2
  • <run>.automationDetails.id i SARIF v2.1.0

Send SARIF-resultaterne til GitHub

Når databasen er klar, kan du forespørge den interaktivt. Du kan også køre en pakke med forespørgsler for at generere et sæt resultater i SARIF-format og uploade resultaterne til et destinationslager på 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>...

Hvis du vil uploade resultater til GitHub, skal du sørge for, at hver kontinuerlig integrationsserver (CI) har en GitHub-app eller et personligt adgangstoken, som CodeQL-kommandolinjegrænsefladen kan bruge. Du skal bruge et adgangstoken eller en GitHub-app med skrivetilladelsen security_events.

Du kan potentielt tillade, at CodeQL-kommandolinjegrænsefladen bruger det samme token, hvis CI-servere allerede bruger et token med dette område til at tjekke lagre fra GitHub ud. Ellers skal du oprette et nyt token med skrivetilladelsen security_events og føje dette token til CI-systemets hemmelige lager.

Som en sikkerhedsmæssig best practice skal du bruge flaget --github-auth-stdin og sende tokenet videre til kommandoen via standardinput.

Upload SARIF-resultater

Hvis kodescanning skal vise resultater fra et statisk analyseværktøj, der ikke er fra Microsoft, i dit GitHub-lager, skal dine resultater gemmes i en SARIF-fil, der understøtter et bestemt undersæt af SARIF 2.1.0 JSON-skemaet. Du kan uploade resultaterne ved enten at bruge kodescannings-API'en eller CodeQL CLI.

Hver gang du uploader resultaterne af en ny kodescanning, behandler CodeQL resultaterne og føjer beskeder til lageret. For at forhindre duplikerede advarsler for det samme problem bruger kodescanning SARIF-egenskaben partialFingerprints til at matche resultater på tværs af kørsler, så de kun optræder én gang i den seneste kørsel for den valgte gren.

Fjernelse af dubletter gør det muligt at matche beskeder med den korrekte kodelinje, når filer redigeres.

Regel-id'et for et resultat skal være det samme på tværs af analyser. Fingeraftryksdata inkluderes automatisk i SARIF-filer, der oprettes via CodeQL-analysearbejdsprocessen eller CodeQL-løberen.

SARIF-specifikationer bruger JSON-egenskabsnavnet partialFingerprints, en ordbog fra navngivne fingeraftrykstyper til fingeraftryk. Denne egenskab indeholder som minimum en værdi for primaryLocationLineHash, som giver et fingeraftryk baseret på konteksten for den primære placering.

GitHub forsøger at udfylde feltet partialFingerprints fra kildefilerne, hvis du uploader en SARIF-fil ved hjælp af handlingen upload-sarif, og disse data mangler.

Derudover, hvis du uploader en SARIF-fil uden fingeraftryksdata ved hjælp af API-endpointet /code-scanning/sarifs , kan brugere se dublerede advarsler, når kodescanningsadvarsler behandles og vises.

For at undgå dublerede advarsler, mens du arbejder med statiske analyseværktøjer, skal du beregne fingeraftryksdata og udfylde ejendommen, partialFingerprints før SARIF-filen uploades. Et nyttigt udgangspunkt er at bruge det samme script som handlingen upload-sarif.