Configuration codée en dur.
Les assistants IA intègrent des valeurs propres à l’environnement — URL, ports, chemins, délais d’expiration, clés et nombres magiques — directement dans la logique au lieu de les lire depuis la configuration ou l’environnement, parce qu’un littéral est le jeton suivant le plus probable et que le modèle n’a pas conscience de votre couche de configuration existante.
##Signs and Symptoms
Un assistant IA a tendance à émettre un littéral plausible exactement là où une référence devrait se trouver. Le code s’exécute dans la démo, si bien que l’odeur survit à la revue à moins que vous ne la cherchiez.
Signes révélateurs :
- Points de terminaison, ports, chemins et délais d’expiration en ligne parsemés dans la logique métier au lieu d’un module de configuration :
http://localhost:3000,/tmp/cache,5432,setTimeout(..., 30000). - Nombres/chaînes magiques sans constante nommée — nombres de tentatives, tailles de page, limites de débit, seuils de fonctionnalités.
- Le même littéral répété dans plusieurs fichiers parce que le modèle l’a régénéré au lieu d’importer une constante existante (le schéma "copier-coller en hausse, refactorisation en baisse" de GitClear en miniature).
- Identifiants / clés codés en dur — jetons d’API, clés de chiffrement ou chaînes d’authentification
Basiccollées depuis les données d’entraînement. - Un objet
config.ts/.env/ de paramètres existant que le nouveau code ignore totalement. - Valeurs intégrées propres à l’environnement : une URL de dev/préproduction ou un identifiant de compte de test câblé en dur dans un chemin qui part en production.
// 🚩 généré par IA : chaque réglage est un littéral, valeurs de dev intégrées
export async function syncOrders() {
const res = await fetch("https://api.staging.acme.dev/v1/orders", {
headers: { Authorization: "Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc" },
signal: AbortSignal.timeout(30000),
});
const orders = (await res.json()).slice(0, 50); // pourquoi 50 ?
for (let i = 0; i < 3; i++) { /* nouvelle tentative 3 fois... pourquoi 3 ? */ }
}
La ligne Authorization est détectée par gitleaks/Semgrep ; l’URL, 30000, 50 et 3 sont des odeurs de valeurs magiques ; et api.staging.acme.dev est une valeur de dev sur le point d’atteindre la production.
##Reasons for the Problem
Pourquoi les modèles le produisent
- Un littéral est le jeton suivant le plus probable. Étant donné
fetch(, la complétion la moins coûteuse qui satisfait l’invite immédiate est une chaîne d’URL concrète, et nonconfig.apiBaseUrl. Résoudre une référence exige de savoir qu’un symbole existe ailleurs ; émettre une valeur n’exige rien. Les modèles optimisent pour un extrait localement plausible et exécutable. - Le corpus d’entraînement est fait d’extraits autonomes. Les tutoriels, les exemples de README et les réponses de Stack Overflow insèrent leurs valeurs en ligne pour qu’ils s’exécutent de manière autonome. Le modèle a appris qu’un "bon exemple de code" code les valeurs en dur — l’opposé de l’hygiène en production.
- Aucun contexte du dépôt / fenêtre limitée. Le modèle n’a généralement pas lu votre
config/, votre schéma d’environnement ou votre fichier de constantes, et ne peut donc pas les réutiliser. GitClear relie précisément cela à la hausse de la duplication : les assistants sont "moins susceptibles de proposer la réutilisation d’une fonction similaire… en partie à cause de la taille limitée du contexte", les blocs dupliqués de plus de 5 lignes ayant été multipliés par environ 8 en 2024, tandis que les lignes refactorisées ("déplacées") sont passées d’environ 24 % à environ 9,5 %. - Flagornerie / biais consistant à répondre à l’invite. Quand on lui demande d’"ajouter une synchronisation des commandes", le modèle livre quelque chose qui fonctionne maintenant ; externaliser la configuration est un échafaudage supplémentaire qu’il ne proposera pas spontanément, sauf si on le lui demande.
- Obsolescence due à la date de coupure d’entraînement. Les littéraux intégrés sont souvent des littéraux obsolètes — anciens points de terminaison, versions d’API dépréciées, ports par défaut ou constantes cryptographiques faibles (MD5, clés codées en dur). OX Security a constaté que 62 % du code généré par IA est livré avec des problèmes et attribue les clés/secrets/chemins codés en dur à des schémas "appris à partir de bases de code héritées… [avec] aucune conscience du fait que les normes de sécurité ont évolué".
Pourquoi c’est nuisible
- Maintenabilité — chirurgie au fusil de chasse. Modifier un seul délai d’expiration ou une seule URL de base implique de traquer chaque littéral dupliqué dans toute la base de code ; oubliez-en un et le comportement diverge silencieusement selon le site d’appel.
- Exactitude entre environnements. Une URL de préproduction ou un identifiant de compte de développement câblé en dur dans la logique part en production ; des seuils magiques sans nom se désynchronisent des valeurs qu’ils devraient refléter.
- Sécurité. Les jetons/clés codés en dur sont des fuites d’identifiants — une fois validés, ils restent à jamais dans l’historique git. L’étude arXiv sur le code de build (2601.16839) a relevé des chemins/URL codés en dur et des identifiants codés en dur de haute gravité récurrents dans les fichiers de build générés par IA.
- Charge de revue & dette technique. Les relecteurs doivent vérifier la provenance de chaque littéral. Les travaux sur les odeurs propres à l’IA (arXiv 2509.20491) notent que les modèles gèrent bien les "littéraux dans la portée" mais peinent sur les cas sensibles à la valeur, où l’exactitude dépend de seuils qui se propagent à travers des fonctions utilitaires — précisément les valeurs codées en dur que les humains doivent désormais auditer à la main. D’après les questions-réponses de Stack Overflow avec Eno Reyes de Factory, la qualité de base du code est "le seul signal" indiquant si les agents accélèrent ou ralentissent une équipe — et la prolifération du codage en dur érode précisément cette base.
##Treatment
Tactiques de prompt / de revue
- Orientez le modèle vers votre surface de configuration : "Lis
src/config.tset.env.example; utiliseconfig.*/process.envpour chaque URL, port, délai d’expiration et identifiant. N’introduis pas de littéraux." Les modèles intègrent les valeurs en ligne parce qu’ils ignorent que le symbole existe — nommez-le. - Interdisez explicitement les valeurs magiques : "Pas de nombres ni de chaînes magiques — extrais des constantes nommées." Faites-lui ensuite exécuter
eslint --rule no-magic-numberset un scanner de secrets (gitleaks/Semgrep), puis corrigez ce qu’ils signalent. Suivant les recommandations de Factory, intégrez les linters/scanners dans la boucle pour que l’agent s’auto-corrige au lieu de s’appuyer sur la revue humaine. - Fournissez le schéma d’environnement (zod/envalid/
.env.example) afin que le modèle ait un endroit où placer les valeurs plutôt que de les deviner. - Passez les nouveaux diffs au grep à la recherche de
http,localhost, de littéraux d’IP, deBeareret de chiffres bruts dans les arguments d’appel avant de fusionner.
Le refactoring — nommez les manœuvres classiques :
- Remplacer un nombre magique par une constante symbolique pour les seuils, les compteurs, les tailles.
- Extraire une fonction / extraire un module de configuration pour rassembler les réglages dans un seul endroit typé et validé.
- Externaliser vers l’environnement (12-factor) pour tout ce qui diffère selon l’environnement ou qui est secret ; ne validez jamais de secrets.
// config.ts — source de vérité unique et validée
import { z } from "zod";
const env = z.object({
ORDERS_API_BASE_URL: z.string().url(),
ORDERS_API_TOKEN: z.string().min(1),
ORDERS_TIMEOUT_MS: z.coerce.number().default(30_000),
ORDERS_PAGE_SIZE: z.coerce.number().default(50),
ORDERS_MAX_RETRIES: z.coerce.number().default(3),
}).parse(process.env);
export const config = env;
// ✅ après : des références, pas des littéraux — un seul endroit à modifier, secret hors du code source
import { config } from "./config";
export async function syncOrders() {
const res = await fetch(`${config.ORDERS_API_BASE_URL}/v1/orders`, {
headers: { Authorization: `Bearer ${config.ORDERS_API_TOKEN}` },
signal: AbortSignal.timeout(config.ORDERS_TIMEOUT_MS),
});
const orders = (await res.json()).slice(0, config.ORDERS_PAGE_SIZE);
for (let i = 0; i < config.ORDERS_MAX_RETRIES; i++) { /* ... */ }
}
Si un secret a déjà atterri dans un commit, changez-le (rotation) — supprimer la ligne ne purge pas l’historique git.
##Detected by
- ESLint no-magic-numbers — Interdire les nombres magiques
- SonarSource typescript:S109 / java:S109 — "Magic numbers should not be used" — Nombres magiques
- SonarSource java:S1075 — "URIs should not be hardcoded" — URI codés en dur
- SonarSource java:S2068 — "Hard-coded credentials are security-sensitive" — Identifiants codés en dur
- Checkstyle MagicNumber — Vérification des nombres magiques
- PMD AvoidUsingHardCodedIP — Éviter les adresses IP codées en dur
- Bandit B105 hardcoded_password_string / B108 hardcoded_tmp_directory — Mots de passe / répertoires temporaires codés en dur (Python)
- gitleaks secret detection (e.g. generic-api-key, regex/entropy rules) — Scanner de secrets codés en dur
- Semgrep generic.secrets / hardcoded-credentials registry rules — Règles pour secrets/configuration codés en dur
- eslint-plugin-no-secrets no-secrets/no-secrets — Détection de chaînes à forte entropie (secrets)