Rexact
Cheatsheet

Regex Apache log line (Common Log Format)

A developer trying to parse Apache logs hit a real snag: some URLs in their access log contained spaces, and their regex cut them off mid-way. Our ReDoS analysis also flags it as vulnerable. Here's a pattern fixed for that, compared against their original attempt, a GitHub gist that doesn't even capture the status code, and another answer that accepts lines missing an HTTP version.

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

Comparison of existing patterns

SourcepatternScore on the 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 / 20vulnerable (+ " + [/ +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 / 20safe
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 / 20safe

Implementation

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

Limits

  • 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).