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
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| 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 / 20 | vulnerable (+ " + [/ +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 / 20 | safe |
| 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 / 20 | safe |
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).