Tester une regex
Testeur de regex confronte un motif à un texte dans votre navigateur, dans un Web Worker avec un délai de deux secondes. Ce n’est pas de la prudence gratuite : JavaScript n’offre aucun moyen d’interrompre une expression régulière en cours d’exécution, donc un motif comme (a+)+$ sur une entrée longue monopolise son fil indéfiniment, et tuer le worker est la seule défense qui fonctionne vraiment.
Comment fonctionne Tester une regex ?
La plupart des testeurs de regex exécutent le motif sur le fil principal de la page. Cela fonctionne jusqu’à ce que quelqu’un colle un motif qui déclenche du backtracking, auquel cas l’onglet cesse de répondre et le seul remède est de le fermer.
Backtracking catastrophique, et pourquoi on ne peut pas l’intercepter
Le moteur de regex de JavaScript pratique le backtracking : quand une correspondance échoue, il réessaie chaque chemin alternatif. Pour (a+)+b face à une suite de a qui se termine par autre chose, les chemins sont toutes les façons de partager la suite entre les deux quantificateurs — ce qui est exponentiel. Trente caractères représentent environ un milliard de tentatives. Il n’y a ni option de timeout, ni signal d’abandon, ni point de reprise du contrôle, ni exception ; le moteur ne rend tout simplement jamais la main.
C’est la classe de vulnérabilité ReDoS, et elle fait tomber de vrais systèmes. La panne mondiale de Cloudflare en 2019, c’était une seule regex dans une seule règle WAF ; celle de Stack Overflow en 2016, un motif qui cherchait des espaces sur un billet en contenant une longue suite. Les deux cas étaient sur un serveur, où la conséquence est le blocage de toutes les requêtes plutôt que d’un seul onglet.
Un worker peut être tué, et c’est là tout le concept
La correspondance s’exécute donc dans un Web Worker, et un minuteur la termine après deux secondes. C’est le seul mécanisme disponible : rien ne peut arrêter une regex en pleine exécution, mais un fil peut être détruit. Quand le délai expire, vous recevez un message clair nommant la cause probable au lieu d’un onglet figé — c’est aussi un signal utile, car un motif qui expire ici est un motif qui ferait planter un serveur.
L’analyse statique nomme des formes, pas des verdicts
Le panneau de risque recherche les quantificateurs imbriqués, les alternances quantifiées dont les branches se chevauchent et les quantificateurs adjacents portant sur les mêmes caractères. Cela couvre la quasi-totalité des incidents réels. Ce qu’il ne fait délibérément pas, c’est déclarer un motif sûr : décider si une regex quelconque a un comportement exponentiel dans le pire cas est un vrai problème d’analyse, et un outil qui promettrait la sécurité ferait une promesse qu’il ne peut pas tenir.
Les correspondances de longueur nulle réclament un coup de pouce
Une boucle de correspondance globale sur un motif qui peut ne rien associer — a*, (?:), \b — ne fait jamais avancer lastIndex et tourne en boucle à l’infini. Toute implémentation correcte l’incrémente manuellement après une correspondance vide, et celle-ci le fait ; c’est un détail qui casse un nombre étonnant d’outils.
La décomposition se fait token par token
Le motif est décomposé en tokens, chacun accompagné d’une note en langage clair, car le problème habituel d’une regex écrite par quelqu’un d’autre n’est pas qu’elle soit fausse mais qu’elle soit illisible. Les suites de caractères littéraux sont regroupées plutôt que listées une à une.
Motif
(\w+)@(\w+\.\w+)
Correspondances
1 [email protected] at 58 $1 ada $2 example.com 2 [email protected] at 271 $1 grace $2 example.org
Quelles options et quels cas limites Tester une regex prend en charge ?
| Paramètre | Type | Valeur par défaut | Comportement et cas limites |
|---|---|---|---|
| g — global | drapeau | activé | Trouve chaque correspondance plutôt que la première. Sans lui, un seul résultat est renvoyé, quel que soit leur nombre. |
| i — ignorer la casse | drapeau | désactivé | Insensible à la casse. Avec le drapeau u, applique aussi le pliage de casse Unicode, qui couvre plus que ASCII. |
| m — multiligne | drapeau | activé | ^ et $ correspondent à chaque saut de ligne et pas seulement aux extrémités de la chaîne. C’est presque toujours ce que vous voulez pour une entrée de type log. |
| s — dotAll | drapeau | désactivé | Fait en sorte que . corresponde aussi à un saut de ligne. Sans lui, .* s’arrête en fin de ligne — c’est la raison habituelle de l’échec d’une correspondance multiligne. |
| u — Unicode | drapeau | désactivé | Active les échappements \u{…} et les classes de propriétés \p{…}, et fait travailler le moteur en points de code plutôt qu’en unités UTF-16 — c’est ce qui permet à . de correspondre à un emoji entier. |
| y — sticky | drapeau | désactivé | Correspond uniquement à lastIndex, sans balayage vers l’avant. Utilisé pour les tokeniseurs, où un trou signifie une erreur de syntaxe et non quelque chose à ignorer. |
| Délai d'expiration | millisecondes | 2000 | Passé ce délai, le worker est arrêté. Impossible de distinguer à l’avance un motif lent d’un motif exponentiel : le temps écoulé est le seul signal disponible. |
| Limite de correspondances | nombre | 5000 | Au-delà, les résultats sont tronqués, et la troncature est signalée. Rendre cinquante mille lignes n’aide personne. |
| Remplacement | $1, $<name>, $& | — | Groupes numérotés, groupes nommés et correspondance entière. $$ est un signe dollar littéral — c’est celui que tout le monde oublie. |
Questions fréquentes
Pourquoi mon motif a-t-il dépassé le délai ?
Il pratique presque certainement un backtracking catastrophique. Cherchez un quantificateur à l’intérieur d’un groupe quantifié — (a+)+, (\d*)* — ou une alternance quantifiée dont les branches peuvent associer le même texte, comme (a|ab)+. Le moteur essaie toutes les façons de partager l’entrée entre elles, ce qui est exponentiel en la longueur de l’entrée. Le panneau de risque nomme la forme s’il en trouve une.
Comment corriger un motif qui déclenche du backtracking ?
Levez l’ambiguïté sur quelle partie associe quoi. Remplacez (a+)+ par a+. Rendez les branches d’alternance mutuellement exclusives pour qu’une seule puisse correspondre à chaque position. Ancrez le motif, pour qu’un échec ne reprenne pas depuis chaque caractère. Bornez les répétitions ouvertes à ce que les données contiennent réellement. Et préférez une classe de caractères précise à un joker : [^"]* déclenche un backtracking bien moindre que .*.
Le ReDoS est-il un risque réel ou théorique ?
Réel, et coûteux. La panne mondiale de Cloudflare en juillet 2019, c’était une seule expression régulière dans une règle WAF ; Stack Overflow est tombé en 2016 sur un motif qui cherchait des espaces de fin de ligne quand un billet en contenait une longue suite. Sur un serveur, le mode de défaillance est pire qu’ici — chaque requête attend derrière celle qui est bloquée dans le moteur, si bien qu’une seule entrée forgée fait tomber le service.
Pourquoi mon .* ne traverse-t-il pas les lignes ?
Parce que . n’associe pas un retour à la ligne tant que le drapeau s n’est pas activé. C’est la surprise regex la plus courante en JavaScript, et il est facile de la confondre avec le drapeau m — mais m change ce que signifient ^ et $, pas ce que le point . associe. Pour une correspondance sur plusieurs lignes, il vous faut s ; pour une correspondance ancrée ligne à ligne, m ; les deux sont indépendants, et souvent on veut les deux.
Faut-il valider une adresse e-mail avec une regex ?
Seulement de façon lâche. La grammaire RFC 5322 d’une adresse s’étale sur plusieurs centaines de caractères en tant que motif, accepte des choses que personne n’attend — parties locales entre guillemets, commentaires, domaines en IP littérale — et ne peut toujours pas vous dire si la boîte existe. Le motif utile vérifie qu’il y a un @ avec quelque chose de chaque côté et un point dans le domaine ; la vraie validation, c’est d’envoyer un e-mail de confirmation.
Mon motif ou mon texte est-il envoyé ?
Non. La correspondance s’exécute dans un Web Worker au sein de votre navigateur — un fil séparé dans le même onglet, pas un serveur. Vous pouvez le vérifier dans le panneau Réseau. C’est important, car le texte sur lequel on teste est généralement un journal de production, et les journaux de production contiennent des adresses e-mail, des tokens et des identifiants clients.
Quels outils liés devriez-vous utiliser ensuite ?
- Comparer du texteCompare par ligne, mot ou caractère et exporte un patch unifié
- Formater du XMLRéindente le XML sans casser CDATA, commentaires ni DOCTYPE
- Visualiser du XMLLit une réponse SOAP en arborescence plutôt qu’en pavé de texte
- Convertir XML en JSONConvertit en gardant attributs et listes à un seul élément