Sécurité applicative

Revue de code spécifique LLM — la checklist au-delà du SAST classique

Le SAST ne voit pas les vrais bugs d'une app LLM. Les 18 points que je passe systématiquement en revue de code quand l'app intègre un LLM ou un agent.

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)

  1. Cartographie des points de contact LLM (1 jour).
  2. Revue ciblée des 18 points (2-4 jours selon taille du code).
  3. 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.

Un sujet connexe chez vous ?

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

Réserver un échange Calendly