ITSkillsCenter
Business Digital

CodeQL sur GitHub Advanced Security : démarrer une analyse pas-à-pas

13 min de lecture

📍 Guide principal du sujet : Pipeline SAST DAST SCA 2026 : architecture, outils et intégration CI/CD
Ce tutoriel détaille l’activation de CodeQL sur GitHub puis l’écriture d’une requête QL custom, en complément des autres briques SAST du pipeline.

Pourquoi adopter CodeQL en 2026

CodeQL est un moteur d’analyse statique différent des autres SAST par sa philosophie : au lieu d’appliquer des règles préconçues, il transforme le code en base de données interrogeable et expose un langage de requêtes (QL) qui ressemble à un SQL relationnel. La conséquence pratique : une équipe sécurité peut écrire une requête comme « trouve tous les chemins où une chaîne provenant d’un header HTTP atteint un appel Runtime.exec() sans passer par shellEscape() » et l’exécuter sur des millions de lignes de code en quelques minutes. Cette expressivité explique pourquoi GitHub a racheté Semmle en 2019 et fait de CodeQL le moteur derrière son Code Scanning.

La version 2.25.2 publiée le 15 avril 2026 améliore la détection des vulnérabilités Java (CFG réécrit) et corrige une faille DoS dans Jackson 2.16.1 utilisée pour parser les manifestes. CodeQL est gratuit pour les dépôts publics (analyse via GitHub Actions sans coût) et pour la recherche académique. Pour les dépôts privés, il fait partie de l’offre payante GitHub Advanced Security, désormais scindée depuis le 1er avril 2025 en deux produits facturés à l’usage par committer actif : GitHub Code Security (30 USD/mois/committer) et GitHub Secret Protection (19 USD/mois/committer). La CLI elle-même reste téléchargeable gratuitement, ce qui permet d’expérimenter et d’écrire des requêtes en local quel que soit le statut du dépôt.

Prérequis

  • Un dépôt GitHub auquel vous avez accès en écriture (pour activer Code Scanning).
  • Une version récente de Git, et 8 Go de RAM minimum si vous prévoyez d’indexer un projet Java ou C++ moyen en local.
  • VS Code avec l’extension CodeQL de GitHub installée pour l’écriture des requêtes (auto-complétion QL et résultats inline).
  • Niveau attendu : intermédiaire en développement, à l’aise avec un langage compilé et la lecture d’un AST.
  • Temps estimé : 90 minutes pour l’activation, l’analyse, et l’écriture d’une première requête QL.

Étape 1 — Activer Code Scanning en mode default

Le mode default est la voie la plus rapide pour bénéficier de CodeQL : GitHub détecte les langages présents dans le dépôt, choisit automatiquement les suites de requêtes adaptées, et déclenche le scan sur chaque push et pull request. Aucun fichier YAML à écrire. Pour un dépôt public, c’est gratuit ; pour un dépôt privé, il faut une licence GHAS.

Settings → Security → Code security
  → Code scanning : Set up
  → Default
  → Confirm

Quelques minutes après confirmation, la première analyse se lance dans l’onglet Actions. À la fin, l’onglet Security → Code scanning affiche les éventuels findings, classés par sévérité (Critical, High, Medium, Low) et accompagnés du chemin de propagation pour les vulnérabilités à flot de données. Si l’analyse échoue parce qu’un projet compilé exige des étapes de build particulières, le mode default n’est plus suffisant et il faut basculer vers le mode advanced.

Étape 2 — Passer en mode advanced pour personnaliser

Le mode advanced génère un fichier .github/workflows/codeql.yml que l’équipe peut éditer pour adapter le déclencheur, la matrice de langages, les packs de requêtes activés ou la commande de build. Cliquer sur Set up → Advanced dans l’écran Code Scanning génère le squelette ; le contenu typique pour un projet polyglotte ressemble à ceci.

name: CodeQL

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '17 3 * * 1'

permissions:
  security-events: write
  actions: read
  contents: read

jobs:
  analyze:
    runs-on: ubuntu-24.04
    strategy:
      fail-fast: false
      matrix:
        language: [java-kotlin, javascript-typescript, python]
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v4
        with:
          languages: ${{ matrix.language }}
          queries: security-extended,security-and-quality
      - uses: github/codeql-action/autobuild@v4
      - uses: github/codeql-action/analyze@v4
        with:
          category: "/language:${{ matrix.language }}"

Trois ajustements méritent l’attention. Le déclencheur schedule rejoue l’analyse chaque lundi nuit pour détecter les CVE nouvellement référencées dans les packs CodeQL même sans changement de code. Les suites security-extended,security-and-quality activent un superset de règles incluant les détections expérimentales — utile en mode informatif au démarrage, à durcir ensuite. La permission security-events: write est nécessaire pour pousser les findings vers Code Scanning, c’est un point souvent oublié quand on hérite d’un workflow d’une autre équipe.

Étape 3 — Installer la CLI CodeQL en local

Travailler uniquement via GitHub Actions limite la rapidité d’itération. La CLI permet de générer la base de données localement et de lancer des requêtes en quelques secondes, ce qui est indispensable pour développer une requête custom. Télécharger le bundle officiel.

cd /opt
sudo curl -sLO https://github.com/github/codeql-cli-binaries/releases/download/v2.25.2/codeql-linux64.zip
sudo unzip codeql-linux64.zip
sudo ln -s /opt/codeql/codeql /usr/local/bin/codeql
codeql --version

git clone --depth 1 https://github.com/github/codeql.git /opt/codeql-libraries
codeql resolve languages

La sortie de codeql --version doit afficher CodeQL command-line toolchain release 2.25.2. Le clone du repository github/codeql contient les bibliothèques de requêtes officielles et les packs standards. La commande codeql resolve languages liste les extracteurs disponibles : cpp, csharp, go, java (alias java-kotlin), javascript (alias javascript-typescript), python, ruby, swift, plus l’extracteur actions qui analyse les workflows YAML.

Étape 4 — Créer une base de données CodeQL en local

Avant d’écrire une requête, il faut indexer le code source dans une base CodeQL. Pour les langages interprétés (Python, JavaScript, Ruby), l’indexation parcourt simplement les fichiers ; pour les langages compilés (Java, C++, C#, Go), CodeQL doit observer la chaîne de compilation pour extraire les types et la résolution de symboles.

cd /chemin/vers/projet-python
codeql database create ./codeql-db \
  --language=python \
  --source-root=.

# Pour un projet Java avec Maven
cd /chemin/vers/projet-java
codeql database create ./codeql-db \
  --language=java \
  --command="mvn -B clean install -DskipTests"

L’opération produit un dossier codeql-db/ qui contient les tables relationnelles décrivant le code (classes, méthodes, expressions, types, références). Sur un projet de 100 000 lignes Java, l’indexation prend trois à dix minutes selon la machine. Le dossier généré peut peser plusieurs centaines de mégaoctets ; le placer dans un volume séparé et l’exclure du dépôt Git via .gitignore.

Étape 5 — Exécuter une requête de la suite officielle

Avant d’écrire ses propres requêtes, valider la base avec les requêtes officielles. Les packs codeql/python-queries, codeql/java-queries, etc., contiennent les détections livrées par GitHub. Lancer la suite security-extended sur la base.

codeql database analyze ./codeql-db \
  codeql/python-queries:codeql-suites/python-security-extended.qls \
  --format=sarif-latest \
  --output=results.sarif

# Pour obtenir aussi un CSV lisible en terminal, relancer l'analyze avec --format=csv
codeql database analyze ./codeql-db \
  codeql/python-queries:codeql-suites/python-security-extended.qls \
  --format=csv \
  --output=results.csv

Le format SARIF est lu par GitHub, par DefectDojo, par VS Code et par la majorité des plateformes d’agrégation. La sortie CSV obtenue par un second appel à analyze --format=csv donne un tableau lisible en terminal sans passer par jq. Si vous travaillez avec VS Code, ouvrir le fichier results.sarif via l’extension CodeQL pour naviguer interactivement entre les findings et le code source. Cette première analyse confirme que la base est bien construite avant d’investir dans l’écriture de requêtes maison.

Étape 6 — Écrire une première requête QL custom

Imaginons que votre code Python utilise une fonction interne internal_audit.log_action(user_input) qui finit dans un fichier de log non assaini. Si user_input contient un retour à la ligne suivi d’une fausse entrée, l’attaquant peut injecter de fausses lignes dans le log et masquer son activité (log forging, CWE-117). Aucune requête officielle ne couvre cette fonction interne. Créer le fichier queries/log-forging.ql.

/**
 * @name Log forging via internal_audit.log_action
 * @description User-controlled input flows into log_action without
 *              newline sanitization, enabling log injection.
 * @kind path-problem
 * @problem.severity error
 * @security-severity 6.5
 * @precision high
 * @id custom/log-forging
 * @tags security
 *       external/cwe/cwe-117
 */

import python
import semmle.python.security.dataflow.LogInjectionQuery
import LogInjectionFlow::PathGraph

class InternalLogSink extends DataFlow::Node {
  InternalLogSink() {
    exists(API::CallNode call |
      call = API::moduleImport("internal_audit").getMember("log_action").getACall() and
      this = call.getArg(0)
    )
  }
}

from LogInjectionFlow::PathNode source, LogInjectionFlow::PathNode sink
where
  LogInjectionFlow::flowPath(source, sink) and
  sink.getNode() instanceof InternalLogSink
select sink, source, sink, "User input flows from $@ to internal log without sanitization.", source, source.getNode().toString()

La structure suit la convention : commentaire de métadonnées (nom, description, type de problème, sévérité), imports des bibliothèques nécessaires, définition d’une classe qui caractérise le sink (la fonction internal_audit.log_action), puis la requête principale qui sélectionne tous les chemins de flot allant d’une source non fiable au sink. La directive @kind path-problem indique à CodeQL de produire un chemin complet (et non un simple finding ponctuel), ce qui aide les développeurs à comprendre la propagation. Tester la requête avec codeql query run queries/log-forging.ql --database=./codeql-db.

Étape 7 — Empaqueter les requêtes dans un pack QL distribuable

Quand plusieurs requêtes existent, les versionner dans un pack QL permet de les distribuer aux différents dépôts sans copier-coller. Créer un fichier qlpack.yml à la racine du dossier de requêtes.

name: votreorg/security-queries
version: 0.1.0
dependencies:
  codeql/python-all: '*'
  codeql/python-queries: '*'
suites:
  - codeql-suites/security.qls
defaultSuiteFile: codeql-suites/security.qls

Publier le pack vers le registry GitHub avec codeql pack publish après authentification (gh auth token exporté en GITHUB_TOKEN). Une fois publié, n’importe quel workflow CodeQL peut référencer le pack via la clé packs: dans le YAML : packs: votreorg/security-queries@~0.1. La gestion sémantique des versions évite que la mise à jour d’une règle dans le pack casse les analyses des consommateurs sans contrôle.

Étape 8 — Trier les findings dans GitHub Code Scanning

Une fois les requêtes en production, l’onglet Security → Code scanning de GitHub liste les alertes. Trois actions de triage structurent le travail. Dismiss with reason ferme un finding avec l’une des trois raisons (false positive, used in tests, won’t fix) ; cette action génère une trace consultable dans l’historique. Open in IDE ouvre le code en VS Code via le protocole vscode:// avec curseur sur la ligne incriminée. Enfin, Branch protection rules permet d’exiger une absence de findings critiques avant de pouvoir merger une PR vers main, ce qui formalise la politique fail-on-new-issue au niveau du dépôt.

Pour exporter les alertes vers DefectDojo ou un SIEM, utiliser l’API REST de GitHub : GET /repos/{owner}/{repo}/code-scanning/alerts retourne le JSON complet, scriptable en cron. La sortie SARIF est également récupérable par alerte si l’analyse de précision est nécessaire.

Erreurs fréquentes

Symptôme Cause Solution
Analyse échoue avec The autobuilder was unable to build the project Le projet a une étape de build personnalisée non détectée Remplacer autobuild par une commande run: explicite (mvn package, ./gradlew build)
Une langue présente dans le repo n’apparaît pas dans la matrice Code Scanning n’inclut pas YAML par défaut Ajouter explicitement actions dans la liste des langages pour scanner les workflows GitHub Actions
Findings dupliqués entre deux runs successifs Pas de catégorie unique par langue Toujours inclure category: "/language:${{ matrix.language }}" dans l’étape analyze
Indexation locale prend plus de 30 minutes Mémoire insuffisante (par défaut 2 Go) Allouer plus avec codeql database create --ram=8000
Une requête custom retourne 0 résultat alors qu’on en attend Mauvaise classe parente ou import oublié Tester d’abord avec codeql query run --warnings=show pour repérer les erreurs silencieuses
Conflit entre packs locaux et packs publics Versions incompatibles dans qlpack.yml Pinner les versions exactes (codeql/python-all: 1.2.3) plutôt que *

FAQ

CodeQL est-il vraiment gratuit pour des dépôts privés open source ? Non. La gratuité concerne uniquement les dépôts publics. Les dépôts privés, même open source dans l’esprit, requièrent GitHub Code Security (volet de GHAS depuis avril 2025). La CLI reste téléchargeable et utilisable gratuitement en local, mais l’intégration GitHub Actions sur dépôt privé est facturée à l’usage par committer actif.

Quelle différence avec Semgrep en mode taint ? CodeQL produit une vraie analyse interprocédurale globale, suivant le flot de données à travers tout le programme et toutes les bibliothèques liées. Semgrep CE est intra-procédural ; Semgrep Pro Engine est interprocédural mais payant. Sur un même cas réel, CodeQL trouve souvent des chemins de propagation que Semgrep manque, au prix d’un temps d’indexation 10 à 50 fois supérieur.

Comment combiner CodeQL avec SonarQube ? Les deux outils ne se substituent pas : SonarQube agrège la dette de qualité et de sécurité dans un dashboard historique, CodeQL excelle dans la détection de chemins de propagation complexes. Une équipe mature exécute les deux en CI et importe les SARIF des deux dans DefectDojo pour l’agrégation finale.

Le langage QL ressemble-t-il à un langage de programmation ? QL est déclaratif et logique, plus proche de Datalog ou Prolog que de Python. La courbe d’apprentissage initiale est réelle (1 à 2 semaines pour devenir productif), mais la puissance d’expression compense largement l’investissement pour les équipes sécurité dédiées.

Comment scanner des workflows GitHub Actions pour détecter une supply-chain attack ? Inclure actions dans la matrice de langages. CodeQL chargera automatiquement les requêtes du pack codeql/actions-queries qui détectent les patterns dangereux (utilisation d’actions tierces sans pin SHA, secret leak, injection dans run:, permissions excessives).

Tutoriels associés

Lectures recommandées

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité