SAST avec Semgrep¶
Analyser votre propre code source pour détecter les failles de sécurité avant qu'elles n'atteignent la production.
SCA vs SAST¶
Deux approches complementaires, pas interchangeables :
- SCA (Software Composition Analysis) — scanne les dépendances (le code des autres)
- SAST (Static Application Security Testing) — scanne votre code source (votre logique)
graph LR
A["Votre code"] -->|"SAST (Semgrep)"| B["Failles dans votre logique"]
C["Dependances"] -->|"SCA (Trivy/pip-audit)"| D["CVE connues"] Complementaires, pas substituables
Un projet peut avoir zero CVE dans ses dépendances et être truffe d'injections SQL dans son propre code. Inversement, un code source impeccable peut embarquer une librairie vulnerable. Les deux scans sont nécessaires.
Pourquoi Semgrep¶
Semgrep se distingue des autres outils SAST par plusieurs caractéristiques :
- Léger — pas besoin de serveur, un simple binaire CLI
- Offline natif — fonctionne sans connexion internet une fois les règles telechargees
- Règles YAML lisibles — chaque règle est un fichier YAML comprehensible par un humain
- Pas de compilation — analyse le code source directement, sans build préalable
- Multi-langage — Python, JavaScript, TypeScript, Java, Go, Ruby, C, et bien d'autres
Installation¶
Télécharger les règles OWASP pour usage hors-ligne¶
Pour un environnement airgap ou simplement pour maîtriser les règles utilisées :
# Cloner le depot officiel de regles
git clone https://github.com/semgrep/semgrep-rules.git
# Puis pointer vers le dossier local lors du scan
semgrep --config ./semgrep-rules/python/ .
Environnement airgap
Copiez le dossier semgrep-rules/ sur la machine cible et lancez les scans avec --config /chemin/vers/semgrep-rules/. Aucune connexion internet n'est nécessaire une fois les règles en local.
Premier scan¶
Créer des fichiers volontairement vulnerables¶
Pour tester Semgrep, créez deux fichiers avec des failles classiques.
vulnerable.py — injection SQL :
import sqlite3
def get_user(user_id):
conn = sqlite3.connect("app.db")
cursor = conn.cursor()
# DANGER : injection SQL
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
return cursor.fetchone()
vulnerable.js — Cross-Site Scripting (XSS) :
function displayMessage(userInput) {
// DANGER : XSS
document.getElementById("output").innerHTML = userInput;
}
Lancer le scan¶
Ou avec les règles en ligne :
Lire le rapport¶
Semgrep affiche pour chaque détection :
| Élément | Description |
|---|---|
| Rule ID | Identifiant unique de la règle déclenchée (ex: python.lang.security.audit.sqli) |
| Severity | Niveau de gravite : ERROR, WARNING, INFO |
| File:line | Fichier et numéro de ligne concernes |
| Message | Explication de la faille détectée |
| CWE | Lien vers la faiblesse cataloguee (ex: CWE-89 pour l'injection SQL) |
Code corrige¶
Python — requête parametree :
import sqlite3
def get_user(user_id):
conn = sqlite3.connect("app.db")
cursor = conn.cursor()
# SECURISE : requete parametree
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
return cursor.fetchone()
JavaScript — textContent au lieu de innerHTML :
function displayMessage(userInput) {
// SECURISE : textContent echappe automatiquement le HTML
document.getElementById("output").textContent = userInput;
}
Faux positifs¶
Comment les identifier¶
Un faux positif est une alerte qui ne correspond pas a une vraie faille exploitable. Cas courants :
- Sanitisation en amont — l'entree utilisateur est déjà validee/echappee avant d'atteindre le code signale
- Contexte non exploitable — le code flagge n'est jamais expose a des données utilisateur
- Code de test — les fixtures de test contiennent volontairement des patterns dangereux
Suppression inline¶
Ajoutez un commentaire nosemgrep avec une justification :
# nosemgrep: python.lang.security.audit.sqli -- user_id est un entier valide par le schema
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
Fichier .semgrepignore¶
Excluez les dossiers qui génèrent du bruit sans valeur :
Ne jamais ignorer sans comprendre
Chaque suppression de finding doit être justifiee. Un nosemgrep sans commentaire est un signal d'alerte pour les reviewers. Si vous ne comprenez pas pourquoi Semgrep signale un pattern, investigez avant de le supprimer.
Règles custom¶
Anatomie d'une règle YAML¶
rules:
- id: no-eval-python
patterns:
- pattern: eval(...)
message: "eval() est dangereux — utiliser ast.literal_eval() pour les cas legitimes"
severity: ERROR
languages: [python]
metadata:
cwe: ["CWE-95"]
Chaque règle contient :
id— identifiant uniquepatterns— le(s) pattern(s) de code a détectermessage— l'explication affichee au développeurseverity—ERROR,WARNINGouINFOlanguages— langages concernesmetadata— informations supplémentaires (CWE, références)
Tester les règles¶
Cas d'usage¶
Les règles custom permettent d'interdire des patterns spécifiques a votre projet :
- Interdire
print()en production (utiliser le logger) - Forcer l'utilisation d'un client HTTP interne plutôt que
requestsdirectement - Détecter les secrets hardcodes dans le code source
- Imposer des patterns de gestion d'erreurs spécifiques
Comparaison SonarQube vs Semgrep¶
| Semgrep | SonarQube | |
|---|---|---|
| Focus | Sécurité | Qualité + sécurité |
| Exécution | CLI, rapide | Serveur, analyse profonde |
| Règles | YAML lisible | Internes, UI |
| Dashboard | Non | Oui |
| Historique | Non | Oui |
| Custom rules | Facile (YAML) | Possible (Java) |
| Idéal pour | CI gate rapide | Vue d'ensemble projet |
En pratique
Les deux outils ne sont pas en compétition. Utilisez Semgrep comme gate rapide dans votre CI (quelques secondes) et SonarQube pour le suivi long terme de la qualité et de la dette technique.