Bibliothèque de playbooks
Socle de sécurité des API
Le code d'endpoint généré est sûr de lui et structurellement correct, et c'est précisément le problème : il a l'air terminé alors qu'il fait confiance à ses entrées, renvoie des lignes de base entières et autorise sur la route plutôt que sur l'enregistrement. Ces règles visent les défauts qui survivent à un linter et à une relecture humaine rapide.
- Écrit pour
- Les équipes qui livrent des API HTTP — Node, Python, Go, quel que soit le framework.
- Non couvert
- Rien ici ne remplace un scanner de dépendances, un scanner de secrets ou un pentest. Ces règles jugent le code du diff — elles ne voient ni la configuration de votre gateway, ni vos politiques IAM, ni une vulnérabilité dans un paquet que vous avez ajouté.
10 règles6 bloquantes4 warning
01 Parsez les entrées de requête avant de les utiliser
errorTout handler qui lit un body, une query string, un paramètre de chemin ou un en-tête doit le parser avec un schéma (zod, pydantic, JSON Schema…) avant que la valeur n'atteigne un service ou une requête. Passer un objet non validé tel quel est refusé, y compris quand une annotation de type prétend en décrire la forme. Une annotation de type est une affirmation faite à la compilation sur des données d'exécution que vous ne maîtrisez pas — le mass assignment et l'injection entrent tous deux par l'écart entre les deux.
Bloque : une frontière non validée est exploitable dès sa mise en production, et elle saute aux yeux dans le diff.
Portéesrc/routes/**src/**/*.controller.*apps/api/**
Tagssecurityvalidationapi
02 Autorisez sur l'enregistrement, pas sur la route
errorUn handler qui charge une ressource par son id doit aussi vérifier que cette ressource appartient bien à l'appelant — propriétaire, tenant, ou contrôle de permission explicite — dans la même requête ou juste après. Authentifié ne veut pas dire autorisé. Un middleware au niveau de la route ne voit pas quelle ligne vous avez chargée : l'IDOR est la faille qui survit à toutes les couches de protection générique.
Bloque : c'est la faille exploitable la plus fréquente dans le code CRUD écrit par une IA.
Portéesrc/routes/**src/**/*.controller.*apps/api/**
Tagssecurityauthorization
03 Ne sérialisez jamais une ligne de base brute
errorLes réponses sont construites à partir d'un DTO explicite ou d'une sélection de colonnes nommées. Renvoyer une entité directement, ou l'étaler dans l'objet de réponse, est refusé. Un `select *` suivi d'un spread laisse fuir la prochaine colonne que quelqu'un ajoutera — hash de mot de passe, drapeaux internes, marqueurs de suppression logique — et rien, dans le diff qui ajoute la colonne, n'aura l'air anormal.
Bloque : la fuite est silencieuse et rétroactive — elle apparaît sans que personne n'ait touché à ce fichier.
Portéesrc/routes/**src/**/*.controller.*apps/api/**
Tagssecurityapiprivacy
04 Vérifiez les signatures de webhook sur le body brut
errorUn handler de webhook vérifie la signature du provider sur le corps de requête non parsé, avant tout parsing, toute écriture en base et tout effet de bord. Le middleware de body-parsing doit être désactivé sur ces routes. Un endpoint de webhook non vérifié est un endpoint d'écriture non authentifié, appelable par quiconque lit votre documentation.
Bloque : une route de webhook non signée est un accès direct à votre modèle d'écriture.
Portée**/webhooks/****/*webhook*
Tagssecurityintegrations
05 Les secrets viennent de la config, sans valeur de repli
errorLes identifiants, les clés d'API et les secrets de signature sont lus depuis le module de configuration typé. Un secret en dur, une valeur committée dans une fixture ou un repli du style `process.env.X || "dev-secret"` sont refusés. Un secret de repli devient le secret de production au premier déploiement où la variable d'environnement manque, et rien n'échoue assez fort pour que quelqu'un le remarque.
Bloque : un secret committé ou pourvu d'une valeur par défaut est divulgué dès le merge.
Portéetout le repo
Tagssecurityconfig
06 Les origines CORS sont une liste blanche explicite
errorLes origines autorisées forment une liste explicite issue de la configuration. Renvoyer `req.headers.origin` en écho, ou combiner un joker avec les credentials, est refusé. Réfléchir l'origine de l'appelant avec les credentials activés supprime d'un coup la same-origin policy pour toutes les routes authentifiées.
Bloque : cela désactive en silence la protection navigateur sur laquelle repose tout le reste de votre modèle d'authentification.
Portéesrc/**apps/api/**
Tagssecurityapi
07 Comparez les secrets en temps constant
warningLes clés d'API, les tokens de session et les signatures sont comparés avec une fonction à temps constant (`crypto.timingSafeEqual`, `hmac.compare_digest`), jamais avec `===` ni `==`. Une comparaison de chaînes s'arrête au premier octet différent : le temps de réponse révèle donc quelle part du secret l'attaquant a déjà devinée — et comme la correction tient en un appel de fonction, il n'y a aucune raison de laisser la fuite en place.
Avertit plutôt que bloquer : l'attaque est réelle, mais lente et bruyante, et la correction tient en une ligne.
Portéesrc/**/auth/**src/**/*token*src/**/*signature*
Tagssecurityauth
08 Les routes non authentifiées déclarent une limite de débit
warningToute route accessible sans session — connexion, inscription, réinitialisation de mot de passe, recherche publique, magic link — doit porter un rate limiter, ou renvoyer en commentaire à la politique de gateway qui la couvre. Sans cela, le credential stuffing et le flood d'e-mails de réinitialisation ne coûtent rien à l'attaquant.
Avertit : le limiteur vit souvent au niveau de la gateway plutôt que dans le diff — la règle demande donc au reviewer de confirmer qu'il existe quelque part, sans faire échouer le build.
Portéesrc/routes/**apps/api/**
Tagssecurityabuse
09 Les erreurs renvoient un code, pas un détail interne
warningLes erreurs exposées au client portent un code stable, lisible par une machine, et un message générique. La stack trace, la requête en échec et le corps de réponse du service amont ne vont que dans les logs. Renvoyer `err.message` au client, c'est ainsi que des chaînes de connexion, des fragments de SQL et des noms d'hôtes internes finissent dans la capture d'écran d'un rapport de bug.
Avertit : c'est une fuite d'information, pas une compromission directe — mais elle alimente toutes les autres attaques.
Portéesrc/routes/**src/**/*.controller.*apps/api/**
Tagssecurityerrorsapi
10 Les logs portent des identifiants, pas des payloads
warningJournalisez un nom d'événement et les identifiants concernés. Ne journalisez jamais un corps de requête, un en-tête d'autorisation, un token ou une fiche utilisateur complète — y compris dans un bloc catch. Les logs partent chez des tiers et sont conservés bien plus longtemps que ne l'autorise la politique de données qui couvre la base.
Avertit : c'est un problème de conformité et de rétention plutôt qu'une brèche immédiate.
Portéetout le repo
Tagssecurityprivacylogging
Avant de tout importer
La sévérité est le levier qui décide si l'équipe garde la gate activée. Seul error bloque un merge — importez d'abord ces règles en warning, regardez ce qui se déclenche réellement sur quelques semaines de vraies pull requests, et promouvez celles qui l'ont mérité. Et réécrivez les globs de portée : les chemins ci-dessus sont des exemples, et une règle ne s'applique qu'aux fichiers correspondant à ses globs.
Les règles entrent dans Normik une par une — via le formulaire du playbook, ou en laissant un agent de code les proposer par MCP à partir d'un repo existant. Il n'y a pas d'import markdown en masse : le bouton de copie ci-dessus sert à votre AGENTS.md, à une PR où l'équipe débat de la liste, et à garder les règles sous les yeux pendant que vous saisissez celles que vous avez retenues.
Chaque règle ici est jugée comme Normik la juge : sur les lignes que la pull request ajoute. Rien dans ce playbook ne dépend de la description de la PR, d'un fichier que le diff ne touche pas, ni de l'historique d'un fichier — ce sont de vrais standards de revue, mais une gate qui n'en voit pas la preuve ne pourrait que rester muette ou deviner.
L'anatomie d'une règle, le comportement des sévérités et des globs, le versionnage et l'export AGENTS.md sont détaillés dans la documentation du playbook.