Le SAST classique trouvera votre injection SQL, votre XSS, votre crypto faible. Il ne trouvera pas que votre system prompt contient une clé API. Il ne verra pas que vous concaténez du contenu utilisateur dans une instruction LLM. Il ne sait pas que votre output LLM est passé à eval(). Voilà la checklist que j'utilise en revue de code quand le SAST passe à côté.
La règle d'amorçage
Avant la revue, identifier les points de contact LLM dans le code :
- Appels API LLM (provider, modèle local, MCP).
- Constructions de prompts (system, user, RAG).
- Réception et traitement d'outputs LLM.
- Définitions et exécutions de tools / function calls.
- Gestion de mémoire conversationnelle.
- Gestion d'embeddings et vector DB.
Sans cette carte, on revoit dans le vide.
Les 18 points que je vérifie
Côté construction de prompts
1. Pas de secrets dans le system prompt
Cherche : tokens API, URLs internes, IDs clients, chemins systèmes. Tout ça finit envoyé au LLM provider. Mauvaise idée.
2. Séparation système / contenu utilisateur
Le contenu utilisateur ne doit pas se faire passer pour une instruction système. Utiliser des marqueurs clairs (), du structured prompting, ou un protocole supporté par le modèle.
3. Sanitization du contenu RAG
Les documents qu'on injecte au contexte peuvent contenir des instructions injection. Soit on filtre, soit on encode, soit on isole (modèle séparé pour résumer puis re-prompt).
4. Pas de concaténation naïve f-string
prompt = f"Process: {user_input}" — vulnérable à injection directe. Préférer structured prompting (messages séparés).
Côté appels LLM
5. Clés API en secrets manager
Jamais hardcodées, jamais en variable d'env vue en logs. Rotation documentée.
6. Configuration modèle versionnée
Le model name, temperature, top_p, top_k doivent être en config versionnée, pas en magic numbers dans le code.
7. Timeout, retry, fallback configurés
Sans timeout, un appel LLM qui hang = goroutine/thread bloqué. Sans fallback, panne provider = panne app.
Côté traitement des outputs
8. Pas d'eval / exec sur l'output
Si l'output est exécuté (code généré), c'est dans une sandbox isolée (Pyodide, container limité, micro-VM). Jamais en process principal.
9. Validation structurée des outputs
Si l'output doit être JSON, le valider contre un schema. Si tool calls, valider les paramètres avant exécution.
10. Output escaping côté UI
Le rendu d'un output LLM en HTML doit échapper. Si Markdown rendu, attention aux balises actives (img, iframe, script).
11. Filtering PII sortants
Pattern de scan PII sur les outputs avant retour à l'utilisateur (en multi-tenant surtout).
Côté tools / function calls
12. Catalogue de tools minimal
Un agent n'a accès qu'aux tools strictement nécessaires. Pas de "on lui donne tout au cas où".
13. Validation des paramètres tool
Avant exécution d'un tool call, validation stricte des paramètres (schema, type, ranges, allowlist).
14. Authentification au niveau tool
Le tool authentifie l'agent et vérifie son scope, ne fait pas confiance au prompt.
15. Audit log par tool call
Chaque tool call loggué avec : agent ID, user ID, paramètres, résultat, statut validation humaine.
Côté mémoire conversationnelle
16. Cloisonnement strict par utilisateur/tenant
Pas de fuite de la conversation d'un utilisateur vers un autre. Vérifier les jointures vector DB / cache.
17. TTL et purge automatique
Politique de rétention claire, purge automatisée, droit à l'effacement RGPD opérable.
18. Pas de PII en clair dans la mémoire long-terme
Si mémoire long-terme (mem0, custom), chiffrement et masquage PII. Ou alors pas de PII tout court.
Comment je l'opère
En audit ponctuel (mission 3-7 jours)
- Cartographie des points de contact LLM (1 jour).
- Revue ciblée des 18 points (2-4 jours selon taille du code).
- Livrable : findings par point + criticité + remédiation chiffrée.
En continu (intégré dans le pipeline)
- Linter custom avec règles Semgrep maison pour points 1, 4, 8, 14 (les plus mécaniques).
- Code review obligatoire avec checklist intégrée à la PR template.
- Quarterly walkthrough avec un externe (moi ou équivalent) sur l'évolution.
Les pièges qui reviennent
Croire que le SAST commercial couvre
Snyk, Checkmarx, SonarQube ne savent pas qu'un prompt avec un secret est un problème. Ils ne le verront pas. C'est de la revue humaine + Semgrep custom.
Faire la revue après le go-live
Plus la dette s'accumule, plus c'est cher à remédier. Idéalement la revue commence dès le premier prototype.
Ne pas former les devs
La checklist est utile si les devs comprennent pourquoi chaque point existe. Sinon ils contournent sans se rendre compte. 2h de formation interne, deux fois par an.
Ne pas mettre à jour la checklist
Les modèles évoluent (multi-modal, agents avec computer use). La checklist doit suivre. Voir Claude computer use et Operator politique 30 jours.
Mon avis en 1 ligne
Le SAST classique reste indispensable mais ne suffit plus dès qu'un LLM entre dans l'application. Ajouter les 18 points en revue de code dédiée + 4-5 règles Semgrep custom. Compter 2-3 jours pour outiller, puis intégrer à chaque PR. C'est le ratio effort/impact le plus rapide que je connaisse pour limiter la dette LLM.