Pattern Delimiters And Modifiers
ID |
php.regular_expr_valid_delimiters |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Php |
Tags |
regex, reliability |
Description
Reports a preg_* pattern that PHP cannot split into a pattern and its modifiers: an empty pattern, an alphanumeric or backslash delimiter, a missing or unbalanced closing delimiter, or a modifier letter PCRE does not have.
Unlike the other regular-expression rules, this one has no counterpart in another language, because no other language spells a pattern this way. In Java or JavaScript the pattern is the string. In PHP the string carries its own delimiters and trailing modifiers, so extracting a pattern from it is a parse that can fail on its own terms — before PCRE ever sees a metacharacter.
Rationale
// Bad
preg_match('/abc', $s); // Warning: No ending delimiter '/' found
preg_match('aabca', $s); // Warning: Delimiter must not be alphanumeric or backslash
preg_match('/abc/g', $s); // Warning: Unknown modifier 'g'
preg_replace('/(\d+)/e', $r, $s); // Warning: The /e modifier is no longer supported
preg_match('', $s); // Warning: Empty regular expression
-
Every one of these makes the call return
falseand match nothing, on every run and for every input, so a validation guard silently accepts nothing and a filter silently filters nothing. -
The warning is normally invisible in production, where
display_errorsis off. -
/abc/gis the most common of these by far: the trailinggis JavaScript’s global flag, which PHP does not have —preg_replaceandpreg_match_allalready replace or match every occurrence.
Remediation
Close the pattern, pick a delimiter that is neither alphanumeric nor a backslash, and drop any modifier PCRE does not define.
// Good
preg_match('/abc/', $s);
preg_match('#^/api/v[0-9]+$#', $s); // another delimiter, so slashes need no escaping
preg_match_all('/abc/', $s, $m); // "match them all" is the function, not a modifier
The modifiers PCRE accepts in PHP are i, m, s, x, A, D, S, U, X, u, J and n. e was removed in PHP 7.
What is accepted
-
Both delimiter families: the bracket pairs
(…),[…],{...}and<…>, whose nesting is tracked, and any other non-alphanumeric character repeated, such as/,#or~. -
A backslash-escaped delimiter inside the pattern —
/^https?:\/\/\w+/— which does not close it. -
Whitespace before the opening delimiter and between the modifier letters, both of which PHP skips.
A pattern built at run time is not readable here and is not judged.