Rexact
Cheatsheet

Regex ligne de log Apache (Common Log Format)

Un développeur qui essayait de parser des logs Apache a buté sur un cas réel : certaines URL de son access log contenaient des espaces, et sa regex les coupait en plein milieu. Notre analyse ReDoS la classe en plus vulnérable. Voici un motif corrigé, comparé à sa tentative d'origine, à un gist GitHub qui ne capture même pas le code de statut, et à une autre réponse qui accepte des lignes sans version HTTP.

/^(\S+) (\S+) (\S+) \[([\w:\/]+\s[+\-]\d{4})\] "(\S+) (.+?) (\S+)" (\d{3}) (\S+)$/
passe 20/20 cas

Comparatif des patterns existants

SourcepatternScore sur le corpusReDoS
Stack Overflow — tentative de l'auteur de la question (casse sur un espace dans l'URL)^(\S+) (\S+) (\S+) \[([\w:\/]+\s[+\-]\d{4})\] "(\S+) (\S+)\s*(\S*)" (\d{3}) (\S+)$18 / 20vulnérable (+ " + [/ +0000] "/ +""""""""""""""""""""""""""""""""""""""""…)
GitHub Gist — motif court avec groupes nommés, ne capture ni le statut ni la taille^(?<IP>[^ ]*) \- \- \[(?<Datetime>[^\]]+)\] \"(?<Verb>[^\ ]+)\ (?<Path>.*) (?<HTTP>HTTP\/\d\.\d)"$10 / 20sûr
Stack Overflow — réponse alternative, requête non décomposée en méthode/chemin/protocole^(\S+) - - \[([\w:\/]+\s[+\-]\d{4})\] "(.*?)" (\d{3}) (\d+)$14 / 20sûr

Implémentation

const re = new RegExp("^(\\S+) (\\S+) (\\S+) \\[([\\w:\\/]+\\s[+\\-]\\d{4})\\] \"(\\S+) (.+?) (\\S+)\" (\\d{3}) (\\S+)$", "");
re.test(input);

Limites

  • Valide seulement la forme d'une ligne du Common Log Format (%h %l %u %t \"%r\" %>s %b) : un serveur configuré avec un LogFormat personnalisé (champs supplémentaires, ordre différent) ne produira pas de lignes reconnues par ce motif.
  • N'exige pas qu'une ligne sans protocole HTTP soit rejetée pour une bonne raison technique universelle : certains vieux logs (dont le jeu de données NASA-HTTP de 1995 utilisé dans les exemples ci-dessus) omettent parfois la version HTTP sur des requêtes malformées — ce motif les exclut volontairement, un serveur plus tolérant peut vouloir les accepter.
  • Ne valide pas que le code de statut (3 chiffres) correspond à un code HTTP réellement défini : « 999 » passe la forme sans être un code valide.
  • Le composant [\w:/]+ du timestamp accepte n'importe quelle combinaison de lettres/chiffres avant le fuseau horaire — il ne vérifie pas qu'il s'agit d'une date calendaire réelle (jour 01-31, mois existant).