Exercice 04 : Intégrer l'analyse de code statique (SAST) dans le pipeline
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Configurer GitHub Actions avec Semgrep pour le SAST
- ✅ Utiliser Bandit pour scanner du code Python
- ✅ Utiliser ESLint avec le plugin sécurité pour JavaScript
- ✅ Trouver et corriger une vulnérabilité d'injection SQL
- ✅ Publier un rapport de sécurité comme artefact CI
Durée estimée : 25 minutes
Difficulté : ⭐⭐⭐☆☆ (Intermédiaire)
Prérequis : Notions de GitHub Actions, Python ou JavaScript
📖 Contexte
Le SAST (Static Application Security Testing) analyse le code source sans l'exécuter pour détecter des vulnérabilités comme les injections SQL, XSS, ou l'utilisation de fonctions dangereuses. L'intégrer dans la CI garantit que chaque pull request est analysée avant fusion.
📋 Énoncé
Créez un projet Python vulnérable, configurez des outils SAST pour détecter les problèmes, et corrigez les vulnérabilités trouvées.
🧭 Déroulement de l'exercice
Tâche 1 : Créer un code Python vulnérable
Créez app.py avec une injection SQL et une exécution de commande système non sécurisée :
import sqlite3
import subprocess
def get_user(username):
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
# VULNÉRABILITÉ : injection SQL
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
return cursor.fetchall()
def run_command(cmd):
# VULNÉRABILITÉ : injection de commande
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.stdoutIndice : L'injection SQL permet à un attaquant de passer
' OR '1'='1commeusernamepour récupérer tous les utilisateurs. L'injection de commande avecshell=Truepermet d'exécuter des commandes arbitraires sicmdvient d'une entrée utilisateur.
Vérification : Le code est syntaxiquement valide (python -c "import app"). Vous comprenez comment chaque vulnérabilité pourrait être exploitée.
Tâche 2 : Scanner avec Bandit
Installez Bandit et scannez app.py :
pip install bandit
bandit -r app.py -f json -o bandit-report.json
bandit -r app.py # Affichage lisibleIndice : Bandit identifie les problèmes par sévérité (HIGH/MEDIUM/LOW) et confiance (HIGH/MEDIUM/LOW). Les codes
B608(SQL injection) etB602/B603(subprocess) sont des HIGH à corriger en priorité.
Vérification : Bandit détecte au moins 2 problèmes HIGH dans app.py. Le rapport JSON est généré dans bandit-report.json.
Tâche 3 : Corriger les vulnérabilités
Réécrivez les fonctions vulnérables en appliquant les bonnes pratiques :
Indice :
`pythonimport sqlite3
import subprocess
import shlex
>
def get_user(username):
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
# CORRECTION : requête paramétrée (prepared statement)
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,))
return cursor.fetchall()
>
def run_command(cmd_parts):
# CORRECTION : liste de commandes, pas shell=True
# cmd_parts doit être une liste : ["ls", "-la", "/tmp"]
result = subprocess.run(cmd_parts, shell=False,
capture_output=True, text=True)
return result.stdout
`
Vérification : bandit -r app.py ne retourne plus de vulnérabilités HIGH. Les requêtes paramétrées empêchent toute injection SQL.
Tâche 4 : Configurer Semgrep dans GitHub Actions
Créez .github/workflows/sast.yml avec Semgrep :
Indice :
`yamlname: SAST - Analyse de sécurité
>
on:
push:
branches: [main]
pull_request:
branches: [main]
>
jobs:
semgrep:
name: Semgrep SAST
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
>
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/python
p/owasp-top-ten
p/sql-injection
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
>
- name: Upload rapport Semgrep
if: always()
uses: actions/upload-artifact@v4
with:
name: semgrep-report
path: semgrep.sarif
`
Vérification : Le workflow apparaît dans l'onglet Actions de GitHub. Il s'exécute sur chaque PR et bloque si des HIGH sont détectés.
Tâche 5 : Ajouter Bandit dans le pipeline CI
Ajoutez un job Bandit dans le même workflow :
Indice :
`yamlbandit:
name: Bandit - Python Security
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
>
- uses: actions/setup-python@v5
with:
python-version: "3.12"
>
- run: pip install bandit[toml]
>
- name: Run Bandit
run: bandit -r src/ -f json -o bandit-report.json -ll
continue-on-error: false
>
- name: Upload rapport Bandit
if: always()
uses: actions/upload-artifact@v4
with:
name: bandit-report
path: bandit-report.json
`
Vérification : Le job Bandit échoue si des vulnérabilités HIGH existent (-ll = niveau minimum LOW, ou -lll pour HIGH uniquement). Le rapport JSON est disponible en artefact.
🗂️ Mini-Projet
Configurez Bandit via pyproject.toml pour personnaliser les règles :
[tool.bandit]
exclude_dirs = ["tests", "venv"]
skips = ["B101"] # Ignorer les assert en test
tests = ["B201", "B301", "B602", "B608"]Puis intégrez un badge de sécurité dans le README :
Checkpoints :
bandit -r app.pydétecte l'injection SQL dans le code original- Le code corrigé passe Bandit sans HIGH
- Le workflow GitHub Actions s'exécute sur les PR
- Le rapport Bandit est disponible comme artefact
- Semgrep est configuré avec les règles OWASP Top 10