DevSecOps

Semgrep : écrire des règles SAST personnalisées pour votre stack

Les règles génériques d'un SAST sont du bruit pour vos vrais risques. Semgrep permet d'écrire en 30 minutes des règles qui détectent les patterns dangereux propres à votre entreprise.

Aroua Biri 8 min

Semgrep est devenu en 2025-2026 le SAST de choix pour les équipes DevSecOps qui veulent du vrai signal et peu de bruit. Sa force : la possibilité d'écrire en quelques minutes des règles spécifiques à votre stack, qui détectent vos patterns dangereux à vous, pas seulement les généralités OWASP. Voici comment construire votre catalogue de règles maison.

Pourquoi les règles génériques ne suffisent pas

Un SAST générique (SonarQube, Snyk, Checkmarx) détecte :

  • Injection SQL via concaténation de strings.
  • XSS via output non-échappé.
  • Secrets hardcodés.
  • Crypto faible.

C'est utile, mais ne détecte pas :

  • Vous avez une fonction unsafe_db_query() qu'aucun nouveau code ne devrait appeler.
  • Vous avez une API interne qui ne doit jamais être exposée publiquement.
  • Vous avez un SDK fournisseur dont une méthode est dangereuse.
  • Vous avez un pattern de rate-limiting interne qu'il faut systématiquement appliquer.

Ces détections nécessitent de la connaissance métier. Semgrep permet de la coder.

Anatomie d'une règle Semgrep

Une règle est un YAML qui décrit un pattern AST à matcher. Exemple basique :

``yaml rules: - id: no-print-in-production pattern: print(...) message: "print() ne doit pas exister en production. Utiliser le logger structuré." languages: [python] severity: WARNING ``

Cette règle détecte tout print() dans du code Python.

Patterns avancés

Match avec contexte

``yaml rules: - id: dangerous-eval-with-user-input patterns: - pattern: eval($X) - pattern-inside: | def $F(request, ...): ... message: "eval() avec input depuis la request HTTP — vulnérabilité de code injection." languages: [python] severity: ERROR ``

Détecte eval() uniquement quand il est dans une fonction qui prend request en paramètre.

Match avec exclusion

``yaml rules: - id: hardcoded-api-key patterns: - pattern-regex: '["\']sk-[a-zA-Z0-9]{32,}["\']' - pattern-not-inside: | # test setup ... message: "Clé API hardcodée détectée." severity: ERROR ``

Détecte les patterns ressemblant à des clés API, mais ignore les blocs marqués comme tests.

Taint mode (data flow)

``yaml rules: - id: user-input-to-shell mode: taint pattern-sources: - pattern: request.GET.get(...) - pattern: request.POST.get(...) pattern-sinks: - pattern: subprocess.run(...) - pattern: os.system(...) message: "User input flows to shell command — command injection possible." languages: [python] severity: ERROR ``

Détecte le data flow d'un input utilisateur vers une commande shell.

Exemples pratiques de règles entreprise

Règle 1 — Forbidden API

Vous avez décrécié legacy_db_query(), plus aucun nouveau code ne doit l'appeler.

``yaml rules: - id: no-legacy-db-query pattern: legacy_db_query(...) message: "legacy_db_query() est dépréciée. Utiliser db.query() avec ORM." languages: [python] severity: ERROR ``

Règle 2 — Required wrapper

Toutes les routes admin doivent passer par @require_admin.

``yaml rules: - id: admin-route-without-auth patterns: - pattern: | @app.route("/admin/...", ...) def $F(...): ... - pattern-not: | @app.route("/admin/...", ...) @require_admin def $F(...): ... message: "Route admin sans @require_admin." languages: [python] severity: ERROR ``

Règle 3 — Token scope check

Tout appel à votre API privée doit passer un check de scope.

``yaml rules: - id: private-api-without-scope-check patterns: - pattern: api.private.$M(...) - pattern-not-inside: | if ensure_scope($SCOPE): ... message: "Appel API privée sans check de scope." languages: [python] severity: WARNING ``

Règle 4 — Crypto choice

Forcer l'usage d'AES-GCM, interdire AES-CBC sans HMAC.

``yaml rules: - id: aes-cbc-without-hmac patterns: - pattern-either: - pattern: AES.new($KEY, AES.MODE_CBC, ...) - pattern-not-inside: | # validate with HMAC ... message: "AES-CBC nécessite un HMAC séparé. Préférer AES-GCM." languages: [python] severity: ERROR ``

Règle 5 — Tenant isolation

Vos requêtes DB doivent toujours inclure tenant_id.

``yaml rules: - id: db-query-without-tenant-filter pattern: | db.query($MODEL).filter(...) pattern-not: | db.query($MODEL).filter(tenant_id=..., ...) message: "Requête DB sans filtre tenant_id — risque de cross-tenant leak." languages: [python] severity: ERROR ``

C'est la règle la plus précieuse pour un SaaS multi-tenant. Empêche les fuites cross-tenant à la racine.

Workflow d'écriture

  1. Identifier le pattern dangereux récurrent (post-mortem incident, code review répétitive).
  2. Écrire 2-3 cas d'exemple de code à détecter et 2-3 cas à ne pas détecter.
  3. Tester la règle sur le repo : semgrep --config myrule.yaml.
  4. Ajuster pour minimiser faux positifs.
  5. Intégrer dans la CI.

Temps typique : 30-90 minutes par règle.

Intégration CI/CD

```yaml

  • name: Semgrep scan
  • uses: returntocorp/semgrep-action@v1 with: config: | .semgrep/ # vos règles p/security-audit # pack OWASP p/secrets # pack secrets ```

Bloquant sur ERROR, warning sur WARNING.

Versioning et review

Vos règles Semgrep sont du code :

  • Stockées dans .semgrep/ dans votre repo principal (ou un repo dédié).
  • Reviewées comme du code (PR, CODEOWNERS).
  • Documentées : pourquoi cette règle, quel risque, qui l'a écrite.
  • Testées : exemples de code attendu détecté ou ignoré.

Stratégie d'adoption

Semaine 1 — Inventaire

Lister les patterns dangereux récurrents à interdire. Output : une liste de 10-20 règles candidates.

Semaine 2 — Écriture des 5 règles les plus impactantes

Tester sur le code existant, ajuster.

Semaine 3 — Intégration CI bloquante

D'abord en mode "warning" pour évaluer le bruit, puis bloquant après ajustement.

Mois 2-3 — Croissance organique

Chaque post-mortem incident ajoute potentiellement une règle qui aurait évité le problème.

Mois 6+ — Catalogue stable

50-100 règles entreprise, maintenues, comprises par l'équipe. C'est un actif technique réel.

ROI réel

Une règle Semgrep bien écrite :

  • Détecte des bugs sécurité au commit, plus en runtime ou en pen-test.
  • Évite la dette technique de re-explication ("encore une fois, ne fais pas X").
  • Documente exécutablement les patterns dangereux propres à votre stack.

Coût : 30 min d'écriture × 50-100 règles = 25-50 heures d'investissement initial.

Bénéfice : prévention quasi-systématique de nouvelles instances des patterns détectés.

Un sujet connexe chez vous ?

20 minutes pour cadrer ensemble. Aucune offre commerciale envoyée à froid.

Réserver un échange Calendly