No. 16 · Technical
Technique : anatomie d'un gabarit
Une visite de bout en bout du gabarit d'application à code source ouvert livré avec le harnais bien construit, et pourquoi la meilleure spécification est du code qui fonctionne.
Abstract. Le harnais bien construit est livré avec un gabarit d'application monodépôt pleine pile, composé d'un client frontal Vue 3 et d'un serveur Node.js / Express appuyé par une base de données PostgreSQL. Fournir un gabarit à un agent d'IA lui permet de bâtir rapidement des solutions valables de calibre entreprise. Ce gabarit de base contient tout ce dont vous avez besoin pour démarrer, y compris la sécurité, la journalisation et des contrôles de base.
Fournir un bon gabarit à un agent d'IA est la façon la plus fiable d'échafauder une application de calibre entreprise. Un gabarit bien construit règle des centaines de décisions clés d'avance, de la sécurité à l'expérience utilisateur. Et jumelé à un harnais bien construit qui fournit les compétences nécessaires pour transformer le gabarit en application, un agent d'IA peut avancer rapidement et en toute sûreté. Un gabarit règle d'avance les défis techniques les plus critiques, du plus élémentaire, comme le langage de programmation, au plus précis, comme les protocoles de sécurité, le modèle de stockage des données et l'interface et l'expérience utilisateur. Dans leur ensemble, ce sont des centaines de décisions et de contrôles décidés d'avance, qui doivent être techniquement solides et compatibles avec l'environnement d'exploitation visé. Le gabarit ferme les vulnérabilités et les erreurs potentielles les plus courantes et les plus critiques dès la première étape et fournit à l'agent d'IA des modèles valables à imiter et à adopter. Le gabarit est la meilleure spécification, car l'agent maintient les décisions de conception tout en développant les fonctionnalités. Démarre au vert, pas à zéro 773 / 0. Le gabarit est livré avec 773 tests réussis et aucun échec, en plus de six analyseurs de sécurité automatisés. Une application bâtie à partir de celui-ci hérite d'une base de référence fonctionnelle, testée et conforme aux normes dès son premier jour. "La meilleure spécification est du code. Donnez à l'IA une plateforme sécurisée, fondée sur des normes et accessible dès le départ, et elle est beaucoup moins susceptible de commettre une erreur. La solution est conforme avant même que l'IA commence à construire." Il n'y a pas une seule bonne façon de construire un gabarit. Il y a en revanche beaucoup de mauvaises façons. La conception revient à l'organisation et aux décisions d'entreprise plus larges qui lui conviennent. Le gabarit qui suit est très tranché, c'est-à-dire qu'il est très précisément spécifié et laisse peu de place à la mauvaise interprétation ou au malentendu. Vous pouvez toutefois appliquer les mêmes contrôles à tout autre ensemble de technologies. ## §01 Le serveur : le cycle de vie d'une requête Le serveur est une seule application Express en TypeScript. Chaque requête est traitée de façon très cohérente, en franchissant des couches d'intergiciels dans un ordre rationnel et délibéré : d'abord les protections peu coûteuses et larges, et en dernier l'identité et l'intention. ## §02 Identité : connexion, jetons et témoins La connexion est déléguée à Google et à Microsoft au moyen de Passport et d'OpenID Connect. Le gabarit ne stocke aucun mot de passe. À la réussite de la connexion, le serveur émet deux jetons web JSON signés avec RS256, une paire de clés asymétriques, de sorte que le serveur peut vérifier un jeton sans détenir quoi que ce soit qui pourrait en forger un, et les définit comme des témoins httpOnly que les scripts du navigateur ne peuvent pas lire. L'intergiciel authenticate lit le jeton d'accès dans le témoin et, lorsqu'il a expiré, retourne un code 401 avec le code TOKEN_EXPIRED, qui est le signal de renouvellement pour le client. Les jetons de renouvellement ne sont stockés que sous forme d'empreintes SHA-256 dans une table refresh_token; chaque utilisation fait tourner le jeton et tout jeton peut être révoqué, de sorte qu'un jeton de renouvellement volé a une vie courte et que l'on peut clore. ## §03 CSRF et sûreté entre sites Les requêtes qui modifient l'état (comme écrire, mettre à jour, supprimer) sont protégées par une vérification à double soumission. Le serveur définit un témoin httpOnly csrf_token et retourne aussi la valeur du jeton une fois, dans le corps de GET /auth/csrf-token. Le client conserve ces valeurs uniquement en mémoire, jamais dans le stockage, et la renvoie en écho dans un en-tête X-CSRF-Token. L'intergiciel compare l'en-tête au témoin avec une comparaison à temps constant pour éviter les fuites de synchronisation. Il est monté globalement, de sorte qu'une route nouvellement ajoutée est protégée par défaut plutôt que seulement lorsqu'un développeur pense à l'activer. Les seules exemptions sont le démarrage OAuth, le rappel OAuth et le renouvellement de jeton, chacune portant une raison écrite expliquant pourquoi elle est sûre : le rappel est protégé par l'état et le nonce OAuth, et le renouvellement par le témoin sameSite et la rotation. ## §04 De la route à la base de données La logique applicative est en couches : route, puis contrôleur, puis service, puis modèle. Le contrôleur traite la requête et façonne la réponse, le service détient les règles d'affaires, et le modèle est propriétaire du SQL. Chaque requête est paramétrée avec $1, $2, et ainsi de suite; les valeurs ne sont jamais concaténées dans l'instruction, ce qui ferme l'injection SQL à la source. Le modèle est aussi la seule couche qui associe les noms de champ de l'API aux noms de colonne de la base de données, de sorte que le format de transmission et le schéma peuvent évoluer indépendamment. Postgres est atteint par une simple réserve de connexions, sans couche objet-relationnel qui masquerait ce qui s'exécute réellement. Chaque réponse utilise une seule enveloppe : succès avec une charge utile de données, ou échec avec un objet d'erreur portant un code et un message. Un seul gestionnaire d'erreurs intercepte tout ce qui est lancé, associe les erreurs Postgres connues à des codes HTTP sûrs (une violation de clé unique devient 409, une violation de clé étrangère 400), et en production retire les traces de pile et le SQL de la réponse. Les gestionnaires asynchrones sont enveloppés afin qu'une promesse rejetée atteigne ce gestionnaire d'erreurs au lieu de planter le processus. Le résultat : la forme des erreurs, les codes d'état et la dissimulation d'information sont uniformes sur chaque point de terminaison. ## §05 Mises à jour en temps réel par SSE Les mises à jour en direct, une diffusion d'administrateur ou une nouvelle notification, utilisent les événements envoyés par le serveur (SSE), un flux HTTP unidirectionnel plus léger qu'un WebSocket et qui se reconnecte de lui-même. Sur le serveur, un petit gestionnaire tient une carte associant l'identifiant d'utilisateur à l'ensemble des flux ouverts de cet utilisateur, un par onglet de navigateur et plafonné à dix, et leur écrit des événements de notification nommés. Sur le client, useNotificationStream ouvre un EventSource avec justificatifs afin que le témoin d'authentification accompagne la requête, et écoute l'événement nommé; le gestionnaire de message par défaut ne se déclenche pas pour les événements nommés, ce qui est un piège courant. Le flux est traité au mieux : la source faisant foi demeure le point de terminaison de la liste de notifications, de sorte qu'un événement perdu n'est jamais un enregistrement perdu. Le flux se ferme à pagehide et se rouvre à pageshow, de sorte qu'une connexion ouverte ne disqualifie pas la page du cache précédent-suivant du navigateur. ## §06 Le client d'API unique Chaque appel du navigateur passe par une seule instance axios configurée. Elle envoie les justificatifs afin que les témoins voyagent, attache un identifiant de requête et, sur les écritures, le jeton CSRF, et centralise les trois défaillances que chaque écran devrait autrement gérer seul. Comme la logique vit dans un seul fichier, aucune vue ne la réimplémente et aucune vue ne peut se tromper. C'est l'exemple le plus clair de ce que le gabarit règle au nom de l'IA. ## §07 Routage et gardes de navigation Le routeur du client utilise le mode historique et charge en différé chaque vue dans son propre paquet, de sorte que le code d'une page ne se télécharge que lorsqu'elle est visitée. Chaque route porte des métadonnées : si elle exige un utilisateur connecté, si elle exige un administrateur, si elle est réservée aux invités, quelle mise en page utiliser (par défaut, admin ou vierge), et un titre. Une seule garde beforeEach charge l'utilisateur courant une fois au besoin, envoie les utilisateurs non authentifiés vers la connexion tout en préservant leur destination, garde les non-administrateurs hors des routes d'administration, et détourne les utilisateurs connectés de la page de connexion. Une fonction d'assistance sanitizeRedirect rejette toute cible de redirection qui n'est pas un simple chemin relatif, fermant les attaques de redirection ouverte, et afterEach définit le titre du document. ## §08 État, composables et formulaires Il y a un seul magasin Pinia, pour l'authentification. Il détient l'utilisateur et les fonctions d'assistance de rôle, déconnecte après trente minutes d'inactivité, et écoute l'événement auth:expired que le client d'API émet. Tout le reste est un composable, qui est le modèle d'accès aux données du gabarit. useFetch(url) enveloppe un GET et retourne data, loading, error et une fonction de rafraîchissement; les composables de fonctionnalités, useServices, useResources, useForms, useFiles et les autres, enveloppent leurs points de terminaison de la même façon, de sorte qu'une vue se lie à une forme uniforme au lieu d'appeler directement l'API. useTheme conserve le mode clair ou sombre dans le stockage local, usePwa pilote les invites d'installation et de mise à jour, useToast enveloppe les notifications, et useNotificationStream alimente le compteur en direct. Le HTML fourni par l'utilisateur passe par DOMPurify avant d'être affiché. Les formulaires sont construits avec FormKit derrière une seule configuration partagée et thématisée, de sorte que la validation et le style sont cohérents plutôt que faits à la main par écran. ## §09 Interface, accessibilité et compilation L'interface est PrimeVue 4 sur le thème Aura, avec des composants importés automatiquement par un résolveur de sorte que seuls ceux utilisés sont empaquetés, par-dessus le style utilitaire de Tailwind 4. Le démarrage de l'application câble Pinia, PrimeVue, FormKit et le routeur, enregistre un gestionnaire d'erreurs global qui fait apparaître une notification éphémère, précharge la police d'icônes pour raccourcir le premier rendu, et ne monte l'application qu'après la récupération de la session utilisateur et du jeton CSRF. Le mode sombre est une classe sur l'élément racine. L'accessibilité est intégrée : un lien d'accès au contenu, une région dynamique qui annonce chaque changement de route aux lecteurs d'écran, et la conformité à la norme WCAG 2.1 AA que le harnais fait respecter. La compilation divise le code des fournisseurs en fragments distincts, Vue, PrimeVue, graphiques et cartes, de sorte que chacun est mis en cache séparément; elle minifie et supprime les instructions console et debugger en production; et elle sert les ressources compilées avec un cache immuable d'un an. L'application est une application web progressive installable : un service worker préenregistre dans le cache la coquille, se rabat sur l'application pour les routes du client mais jamais pour l'API, et invite l'utilisateur à mettre à jour lorsqu'une nouvelle compilation est livrée. La mise en page est adaptative sur mobile par défaut, et le manifeste comprend une icône masquable pour une installation propre sur l'écran d'accueil. ## §10 Les dix principaux risques de l'OWASP, traités d'entrée de jeu Les dix principaux risques de l'OWASP constituent la liste, établie par l'industrie de la sécurité, des dix classes les plus graves de vulnérabilité web. Le gabarit traite chacune d'elles dans le code, construit selon la norme de vérification de la sécurité des applications de l'OWASP au niveau 2, la norme à laquelle le harnais tient le travail. ## §11 Ce que le gabarit règle pour l'agent Lu de bout en bout, le motif est cohérent. Les décisions dangereuses sont prises une seule fois, dans le code, et centralisées afin qu'elles ne puissent pas être reprises de façon erronée : l'identité, le CSRF, le SQL paramétré, l'enveloppe de réponse, l'assainissement des erreurs, le repli des mises à jour en direct et la logique de réessai du client d'API sont chacun une implémentation unique et testée dont l'IA hérite plutôt qu'elle ne l'écrit. On remet à l'IA un système conforme et on lui demande de l'étendre, et le harnais vérifie de façon mécanique qu'il tient toujours à chaque changement, la même discipline que le harnais anti-dérive applique à mesure que la base de code grandit. Le gabarit ne règle pas tout. L'analyse antimaliciel des fichiers téléversés, la répartition de charge et la limitation de débit sont conservées en mémoire jusqu'à ce qu'un magasin partagé soit configuré. Les lacunes connues ou les décisions reportées sont documentées, et non cachées. La pile est entièrement à code source ouvert, Vue, Express, Postgres et leurs bibliothèques bien soutenues, de sorte qu'il n'y a pas de dépendance à un fournisseur et qu'une grande communauté en assure les correctifs, ce qui est la base de référence qu'une application gouvernementale destinée au public doit atteindre avant d'être livrée. Que vous utilisiez ce gabarit ou que vous construisiez le vôtre, ces principes devraient vous aider, vous et votre agent, à rester sur la bonne voie. ## §12 Tout le gabarit, sur une seule page Chaque partie ci-dessus est une décision dans un seul système cohérent. Le napperon ci-dessous les rassemble : le client Vue à gauche, le serveur Express au centre, Postgres et les fournisseurs d'identité à droite, et les décisions de sécurité et de normes qui valent pour tous, autour du pourtour. C'est la carte qu'un ingénieur peut garder à ses côtés pendant la construction, et la forme que l'agent étend.
Tags: template, harness, security, owasp, open-source, authentication, specification-driven