Spec it safe : décalez vers la gauche et durcissez vos Specs avec des exigences de sécurité
Rajdeep Mukherjee
Applied Science
Debashis Das
Information Security
Le développement piloté par les Specs (SDD) transforme la façon dont les grandes équipes conçoivent des logiciels. Au lieu de vous fier à une simple requête, vous rédigez une spécification qui guide un agent de programmation IA à travers la conception, les tâches et l'implémentation; un flux que nous abrégeons ici en Spécifier → Planifier → Exécuter. Mais la plupart des Specs décrivent ce que le logiciel doit faire, les fonctionnalités qu'un utilisateur peut voir et utiliser, sans préciser comment il doit résister à un usage malveillant. Les exigences fonctionnelles se démontrent naturellement d'elles-mêmes : si la connexion est brisée, quelqu'un le remarque. Ce n'est souvent pas le cas des exigences de sécurité; une application peut réussir toutes les vérifications fonctionnelles tout en laissant grande ouverte une faille d'injection ou d'autorisation, parce que rien n'exerce le cas d'échec. Ainsi, lorsqu'une contrainte de sécurité manque dans la Spec, l'agent n'a aucune cible axée sur la sécurité vers laquelle construire et itérer. Il se rabat sur ce que font les comportements par défaut du cadriciel, ce qui peut suffire ou laisser une brèche, et rien dans la Spec ne force la question dans un sens ou dans l'autre.
Combler cet écart ne consiste pas à ajouter un meilleur modèle ni à greffer une revue de sécurité à la fin. Il s'agit de déplacer le travail plus tôt, vers une couche que vous contrôlez entièrement : la spécification. Lorsque vous inscrivez une obligation de sécurité dans la Spec, l'agent de programmation dispose d'une cible concrète vers laquelle construire et itérer, au lieu de laisser un réviseur la repérer une fois le code déjà écrit. C'est le passage d'une sécurité traitée après coup, greffée une fois la conception arrêtée, à une sécurité intégrée dès le départ. Cela porte généralement ses fruits dans l'architecture aussi. Énoncer tôt une exigence de sécurité peut vous orienter vers une conception plus propre, où satisfaire l'exigence est plus simple parce que vous avez construit autour d'elle plutôt que de l'ajouter après coup. La qualité de la spécification est quelque chose que vous pouvez façonner avant que le moindre code existe, et c'est précisément ce pour quoi le durcissement des spécifications a été conçu.
Nous présentons un Power Kiro pour le durcissement des spécifications, un module d'extension qui ajoute une étape supplémentaire au flux de travail SDD. Une fois vos exigences fonctionnelles rédigées et avant le début de la planification, il lit vos exigences EARS, signale les types de faiblesses que leur formulation laisse entrevoir et propose des obligations de sécurité que vous pouvez retracer et tester. Ces propositions proviennent d'une bibliothèque organisée qui associe des classes de faiblesses CWE précises à des obligations de sécurité réutilisables, de sorte que les suggestions du durcissement des spécifications reposent sur une faiblesse connue plutôt que sur une supposition d'un modèle. La pertinence vient des exigences elles-mêmes : le durcissement des spécifications ne propose une obligation que lorsque votre formulation initiale pointe vers la faiblesse qu'elle traite, comme des entrées utilisateur, l'authentification ou le traitement des données, de sorte que vous voyez des contraintes adaptées à ce que fait l'application. La sécurité entre dans la Spec elle-même, et non dans une revue de code ultérieure, bien que nous vous recommandions fortement d'inclure aussi une étape de revue de sécurité par la suite.

Le durcissement des spécifications est un point de contrôle en amont, et non une frontière de sécurité à l'exécution. Il réduit le risque qu'une classe de faiblesses courante soit omise et rend l'intention de sécurité explicite, traçable et testable. Il ne vérifie pas le code généré, ne prouve pas l'absence de vulnérabilités et ne remplace ni les tests SAST, ni les tests DAST, ni l'analyse des dépendances, ni la revue humaine.
Ce que cible le durcissement des spécifications : les classes de faiblesses qui peuvent à la fois s'exprimer sous forme d'exigence et se détecter à partir de la formulation EARS : injection, authentification manquante, stockage faible des identifiants, sortie non encodée et omissions semblables.
Ce que ne cible pas le durcissement des spécifications : les faiblesses qui dépendent d'une analyse du flux de données ou du code, les lacunes d'autorisation qui ne sont pas signalées lexicalement, et tout ce qui concerne l'exécution, l'infrastructure ou les dépendances. Ces aspects nécessitent toujours un réviseur humain et des outils en aval.
Toutes les faiblesses ne peuvent pas être corrigées dans une Spec; bon nombre sont architecturales ou visibles seulement dans le code. Mais un ensemble choisi de classes CWE exprimables sous forme d'exigences peut être associé à des obligations de sécurité réutilisables qui atténuent la forme courante de chaque faiblesse lorsqu'elles sont correctement mises en œuvre et vérifiées. Le durcissement des spécifications fournit ces associations sous la forme d'une bibliothèque TypeScript et d'une CLI déterministes, de sorte que chaque contrainte est reproductible.
| CWE | Faiblesse | Condition de déclenchement | Contrainte EARS requise |
|---|---|---|---|
| CWE-89 | Injection SQL | L'exigence interroge une base de données à l'aide d'une entrée externe | Exécuter toutes les requêtes uniquement au moyen d'instructions paramétrées ou préparées; ne jamais concaténer. |
| CWE-862 | Autorisation manquante | Renvoie ou modifie une ressource appartenant à un principal | Vérifier que le principal demandeur possède la ressource ou détient une autorisation explicite. |
| CWE-306 | Authentification manquante | Expose un point de terminaison ou une ressource | Exiger une session authentifiée valide ou un jeton signé avant le traitement. |
| CWE-79 | Script intersite (XSS) | Affiche du contenu fourni par l'utilisateur dans un navigateur | Encoder toute sortie selon le contexte; ajouter un en-tête Content Security Policy comme défense en profondeur. |
| CWE-522 | Identifiants insuffisamment protégés | Stocke ou vérifie des mots de passe | Hacher les mots de passe avec une fonction adaptative robuste (jamais en texte clair ni de façon réversible). |
| CWE-434 | Téléversement de fichiers sans restriction | Accepte des téléversements de fichiers | Valider les octets magiques par rapport à une liste de types MIME autorisés; limiter la taille; stocker hors de la racine Web. |
| CWE-639 | IDOR / BOLA | Récupère un objet à l'aide d'un identifiant fourni par l'appelant | Vérifier que le locataire propriétaire de l'objet correspond à l'appelant authentifié avant de le renvoyer. |
La bibliothèque comprend 60 associations CWE réparties dans huit domaines de sécurité (injection, authentification, autorisation, exposition des données, cryptographie, validation des entrées, mauvaise configuration de sécurité et logique métier). Les faiblesses proviennent de la Common Weakness Enumeration (CWE) de MITRE; leur sélection et leurs motifs de détection s'appuient principalement sur le OWASP Top 10:2021 et sur la Amazon Q Developer Detector Library (anciennement CodeGuru). Chaque association comporte une catégorie OWASP et une gravité attribuée manuellement. Les contraintes du tableau sont des paraphrases représentatives, et non des contrôles complets applicables à toutes les architectures.
Le durcissement des spécifications exécute un pipeline en trois étapes sur chaque fichier d'exigences. Le moteur est déterministe : avec la même version de l'outil, les mêmes associations, les mêmes options et la même entrée non durcie, une même Spec obtient toujours le même durcissement. La revue sémantique de l'agent qui suit (figure 2) n'est pas déterministe.
Le moteur compare chaque exigence aux 60 associations CWE à l'aide de deux signaux lexicaux : des mots-clés en sous-chaîne (« database », « upload », « session ») et des motifs d'expressions régulières. Il combine les correspondances en un score borné, et une CWE est signalée lorsque le score dépasse un seuil (0,3 par défaut). La correspondance est lexicale, et non sémantique, de sorte qu'elle peut manquer des faiblesses que la formulation ne décrit pas explicitement.
Pour chaque CWE signalée, le moteur vérifie si la Spec énonce déjà la primitive nécessaire, comme une clause de requête paramétrée, une vérification de propriété ou un encodage de sortie. Seules les lacunes non couvertes donnent lieu à des propositions. La vérification est lexicale et porte sur l'ensemble du fichier, de sorte qu'un contrôle énoncé pour une exigence peut supprimer une proposition pour une autre.
Au lieu de bloquer, le moteur insère chaque contrainte manquante sous forme d'élément EARS. Il remplit les espaces réservés à partir de l'exigence source lorsqu'il le peut et se rabat sur une formulation générique (« a user », « the resource ») lorsqu'il ne le peut pas. Chaque ligne insérée comprend son identifiant de proposition et une étiquette [SECURITY: …], afin que l'auteur puisse examiner les modifications sous forme de diff. Certaines associations superposent des contrôles : CWE-89 ajoute à la fois une clause de requête paramétrée et une clause de validation des entrées. L'API programmatique renvoie un rapport structuré indiquant la CWE, la priorité et la justification de chaque contrainte. La CLI écrit les contraintes sur place et n'enregistre pas ce rapport.
Le durcissement des spécifications lit uniquement la Spec, et non le code. Il propose les contraintes dérivées des CWE que signale le texte de l'exigence, et l'auteur les confirme par rapport au système en fonctionnement. Rendre ces invariants explicites les rend plus faciles à préserver et à tester.
![Diagramme de flux de travail en deux bandes. La bande violette du haut, « Specification Hardening Engine · Kiro Power that runs over requirements.md », se lit de gauche à droite : Requirements (requirements.md en EARS, avec une boîte pointillée « review marker » indiquant qu'une nouvelle exécution ne durcit que les exigences nouvelles ou modifiées), Detect CWE triggers (mots-clés + motifs d'expressions régulières comparés à 61 associations CWE, un score ≥ 0.3 signifie que la CWE s'applique), Close coverage gaps (vérifie chaque contrainte CWE par rapport à l'ensemble de la Spec et écrit celles qui manquent en EARS dans les termes de l'exigence) et Hardened spec (modifiée sur place, éléments [SECURITY:] imbriqués plus une Security Baseline). Une flèche « weak signal » descend de Detect CWE triggers vers la bande orange du bas, « Agent semantic review », qui indique que la correspondance lexicale ne peut pas voir une vérification de propriété ou de locataire manquante, et passe par Review candidates (correspondances faibles notées de 0.05 à 0.3 sur les CWE d'autorisation au niveau des objets comme CWE-639/862), Boundary sweep (l'accès appelant-ressource est-il contraint sur chaque ressource appartenant à un principal?) et Add constraint (chaque lacune confirmée devient un élément EARS [SECURITY:] dans les termes du domaine, comme « active care relationship »), qui remonte vers Hardened spec. Une boucle pointillée en haut indique « Spec changed → re-run on the same file (only new/changed requirements hardened) ».](/images/blogs/spec-it-safe/hardening-engine-workflow.png?h=4afaaf98)
La façon la plus simple de comprendre pourquoi c'est important est d'observer la même fonctionnalité livrée deux fois : une fois à partir d'une Spec uniquement fonctionnelle, une fois à partir d'une Spec durcie par le moteur de durcissement des spécifications. Une jeune entreprise en santé numérique conçoit un portail patient dans lequel un médecin consulte des notes cliniques par nom de patient. La gestionnaire de produit rédige l'exigence que la plupart des gens rédigeraient :
Exigence : minimalement fonctionnelle et muette sur la sécurité (EARS, déclenchée par un événement)
Elle semble complète, mais elle n'est que minimalement fonctionnelle. Elle ne dit rien de la sémantique de correspondance, de la pagination, des résultats vides, des champs pouvant être renvoyés ni de qui peut l'exécuter. Un agent de programmation pourrait produire un point de terminaison Express.js comme celui-ci (pseudocode illustratif) :
Sans contraintes de sécurité explicites (pseudocode illustratif)
Deux faiblesses sont intégrées ici. L'interpolation de chaîne dans la clause SQL est un cas d'école de CWE-89 : injection SQL (une entrée comme %' OR '1'='1' -- ajoute une clause toujours vraie et met le reste en commentaire, ce qui renvoie toutes les lignes; la syntaxe exacte varie selon la base de données). Et le point de terminaison n'a aucune vérification d'authentification, ce qui correspond à CWE-306, authentification manquante pour une fonction critique. Un troisième problème, plus profond, à savoir si un clinicien authentifié devrait voir les notes de tous les patients, est abordé plus loin. L'omission touche les exigences, et l'implémentation n'était pas défensive par défaut.
Faites maintenant passer la même exigence par le moteur de durcissement des spécifications. Son détecteur lexical signale deux classes de faiblesses (CWE-89 injection SQL et CWE-306 authentification manquante) et réécrit sept contraintes EARS sous-numérotées dans l'exigence, chacune avec un identifiant de proposition traçable et une étiquette [SECURITY: …], comme illustré ci-dessous. Une clause proposée sous trois exigences ou plus est déplacée une seule fois dans une section commune ## Security Baseline (Cross-Cutting); il s'agit d'une heuristique, alors vérifiez-la dans le diff.
Résultat du durcissement des spécifications après revue, réécrit dans requirements.md. Le moteur ajoute sept obligations réparties sur deux CWE : 1.1–1.2 pour CWE-89 (requête paramétrée), 1.3–1.4 pour CWE-89 (validation des entrées) et 1.5–1.7 pour CWE-306 (authentification). La revue, effectuée par l'agent Kiro dans le Power ou par vous dans la CLI, ajoute ensuite 1.8 (minimisation des données, CWE-200) et 1.9 (autorisation fondée sur la relation de soins, CWE-862). Des clauses représentatives sont présentées; les autres sont omises par souci de concision. L'API programmatique renvoie aussi un rapport structuré indiquant la CWE, la priorité et la justification de chaque contrainte du moteur; la CLI ne conserve pas ce rapport.
Lecture des identifiants : SEC-REQUIREMENTS-REQ-1-EC-89-1-1 se lit de gauche à droite comme SEC (une contrainte de sécurité), REQUIREMENTS-REQ-1 (la première exigence de requirements.md; dans une Spec comportant des titres « Requirement N », cela devient REQ-1.1, le critère 1 de l'exigence 1) et EC-89-1 (le premier modèle de contrainte EARS de l'association CWE-89). Le 1 final numérote la clause et n'apparaît que lorsqu'un modèle se déploie en plusieurs lignes EARS, ce qui explique pourquoi EC-306-1, qui ne compte qu'une seule clause, n'a pas de suffixe. Les clauses ajoutées lors de la revue utilisent SEM à la place de EC, par exemple SEM-862-1.
Un développeur examine le diff de la Spec durcie et fait preuve de jugement pour chaque clause. Les clauses du moteur couvrent l'injection et l'authentification, mais rien dans la formulation ne lui indiquait de limiter les champs renvoyés, alors la revue ajoute 1.8 (l'exécution avec --semantic-all répertorie aussi CWE-200 comme candidate). Une fois la Spec jugée satisfaisante, l'agent génère le code à partir de celle-ci, en construisant selon des obligations explicites plutôt qu'en devinant. Ce candidat protège le point de terminaison derrière une erreur 401 à sécurité intégrée (1.5–1.7), valide le terme de recherche (1.3), le lie comme paramètre avec un échappement LIKE explicite (1.1–1.2) et renvoie uniquement les champs que nomme 1.8 :
Ébauche de conformité : satisfait aux clauses de requête paramétrée (1.1–1.2), de validation des entrées (1.3), d'authentification (1.5–1.7) et de minimisation des données (1.8), mais elle n'est PAS encore sûre pour de vraies données de patients : elle n'applique pas encore 1.9.
Tout aussi important est ce que l'outil a signalé sans l'écrire. N'importe quel médecin authentifié peut lire les notes de n'importe quel patient, car il n'y a aucune vérification de la relation de soins : une vérification d'autorisation manquante (CWE-862) dont le score est inférieur au seuil. Au lieu de l'écrire, l'outil la présente comme une candidate sémantique que le réviseur doit confirmer. Dans le Power Kiro, ce réviseur est l'agent; dans la CLI, c'est vous. La lacune confirmée devient la clause 1.9, adaptée au domaine : « return clinical notes only for patients with whom the requesting physician has an active care relationship ». La requête devient alors limitée par l'autorisation :
Requête limitée par l'autorisation : la lecture paramétrée et authentifiée, plus le prédicat de relation de soins ajouté lors de la revue (1.9, CWE-862). Elle suppose des paramètres régionaux et une collation PostgreSQL offrant le comportement ILIKE requis; le schéma et la politique sont illustratifs.
Le durcissement déterministe a fait ressortir ici sept obligations réparties sur deux CWE. La règle de minimisation des données et la règle d'autorisation propre au domaine sont tout de même venues de la revue.
Le durcissement des spécifications enrichit le jugement en matière de sécurité, il ne le remplace pas.
Le durcissement des spécifications est offert sous forme de Power Kiro, disponible dans le dépôt sample-kiro-spec-security-hardening sur GitHub. Vous pouvez l'exécuter de deux façons.
Avec le Power Kiro. Une fois vos exigences fonctionnelles finalisées, demandez à Kiro de « durcir mes Specs ». Le Skill harden-spec :
- trouve le moteur fourni et le compile à la première utilisation;
- l'exécute sur vos
.kiro/specs/*/requirements.md; - effectue la revue sémantique pour repérer les vérifications d'autorisation manquantes que la correspondance par mots-clés ne peut pas voir;
- résume les contraintes qu'il a ajoutées et les avertissements qui requièrent votre attention.
Depuis la CLI. Compilez une seule fois avec npm install dans cwe-to-spec-mapping-library/. Pointez ensuite la CLI compilée vers un fichier de Spec ou un répertoire de Specs, en utilisant des chemins absolus :
L'option --semantic-candidates affiche les correspondances d'autorisation faibles pour examen, mais ne les écrit pas dans le fichier.
Les deux méthodes modifient requirements.md sur place. Le durcissement s'intègre donc à la gestion de versions et à la revue de code. Le moteur ne fait qu'ajouter des lignes; il ne modifie, ne divise ni ne réordonne jamais une exigence, et il signale les exigences qui ne respectent pas strictement le format EARS au lieu de les réécrire. Examinez toujours le diff git. Les nouvelles exécutions sont incrémentielles : un marqueur de revue caché consigne les exigences déjà durcies, de sorte que le durcissement des spécifications ne durcit que les exigences nouvelles ou modifiées et ne propose pas de nouveau les contraintes qu'un réviseur a retirées. Exécutez-le sur la Spec telle qu'elle est; vous n'avez jamais besoin d'une copie antérieure au durcissement.



Comme le durcissement des spécifications agit sur la Spec plutôt que sur le code, la même étape de durcissement s'intègre à trois types de travail. Ils ne diffèrent que par la provenance des exigences et le moment où le durcissement des spécifications s'exécute :
Nouveaux projets (greenfield). Le durcissement s'exécute comme une étape du flux de travail de la Spec, avant l'écriture de tout code : à partir des exigences EARS, le durcissement des spécifications ajoute les contraintes [SECURITY: …] manquantes, et l'agent effectue l'implémentation selon la Spec durcie. L'intention de sécurité est présente dans la Spec dès le premier commit plutôt qu'ajoutée après coup.
Code existant (brownfield). Lorsqu'une exigence est ajoutée ou modifiée, exécutez de nouveau le durcissement des spécifications sur la Spec existante. Il ne durcit que les exigences nouvelles ou modifiées et laisse intactes celles déjà examinées. Les contraintes SEC- ajoutées apparaissent sous forme de diff à examiner à côté du travail sur la fonctionnalité, de sorte que la modification reste limitée à ce qui a changé. Comme le durcissement des spécifications lit la Spec, et non le code, vérifiez chaque obligation proposée par rapport au comportement réel du système existant. Là où ils diffèrent, vous avez trouvé soit une lacune dans le code, soit une contrainte à adapter.
Modernisation pilotée par les Specs (réécritures de systèmes hérités). Les exigences sont d'abord extraites du système hérité sous forme de Specs EARS, par exemple avec AWS Transform; le durcissement des spécifications durcit ensuite par lots cet ensemble récupéré avant la réimplémentation. Le système modernisé est reconstruit selon une spécification explicitement durcie au lieu d'hériter des lacunes d'origine.

Le durcissement des spécifications est un premier pas dans un domaine assez nouveau : sécuriser la spécification elle-même et s'en servir pour orienter les agents de programmation vers un code sécurisé dès la conception. Nous pensons que la Spec est le bon point de départ, car c'est le tout premier artéfact à partir duquel travaillent à la fois les personnes et les agents. Mais le durcissement des spécifications est un prototype de recherche et une hypothèse, et ce billet plaide en faveur de contrôles au niveau de la Spec.
Le durcissement des Specs n'est pas non plus la seule façon d'obtenir du code sécurisé d'un agent, et il fonctionne mieux en complément des autres. La modélisation des menaces repère les risques au niveau de la conception que les correspondances par mots-clés peuvent manquer. Les directives de programmation sécurisée dans le contexte de l'agent, comme les fichiers de Steering de Kiro, façonnent le code au moment où il est écrit. Les cadriciels dotés de valeurs par défaut sûres éliminent des catégories entières d'erreurs. En aval, les tests SAST et DAST, l'analyse des dépendances, l'analyse de sécurité du code assistée par l'IA (comme celle offerte par AWS Continuum), les tests de sécurité et la revue humaine vérifient toujours ce qui a été construit. Le durcissement des spécifications ajoute une couche de plus, au moment où l'intention est mise par écrit.
La Spec durcie reste dans le dépôt, de sorte que le raisonnement derrière chaque obligation peut encore être vérifié une fois les tâches exécutées. Si votre équipe adopte le SDD, ajoutez une revue de sécurité de la Spec à votre définition de « terminé ». Les agents peuvent intégrer la sécurité dès le départ, mais seulement si nous leur disons ce que cela signifie.