ASVS 5.0 V1.2.9 (Level 2) requires that when untrusted text becomes part of a regular expression, its special characters are escaped (usually with a backslash) so they are read as literal characters and not as metacharacters. Search boxes and filter rules are the typical places this goes wrong.
Why it matters
A user who types .* into a search that is compiled as a pattern matches everything. A user who types (a+)+$ may freeze your server, and an unescaped [ can throw an exception that leaks a stack trace. Pattern based access rules can also be bypassed when a dot in example.com matches any character.
How to implement it
Use the standard library function every time, and prefer plain substring search when you do not need a pattern.
import re
pattern = re.compile(re.escape(user_text), re.IGNORECASE)
const escapeRegex = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const re = new RegExp(escapeRegex(userText), 'i');
Pattern p = Pattern.compile(Pattern.quote(userText), Pattern.CASE_INSENSITIVE);
In Go, use regexp.QuoteMeta. Anchor patterns with ^ and $ where whole value matching is intended.
How to test it
Search for ., *, (, [a- and a|b. Results must match only literal occurrences of those strings, and invalid ones must not raise errors. Grep for new RegExp( and re.compile( calls that include variables.
Common mistakes
- Escaping once in the client and assuming the server did it.
- Using a hand written escape that forgets the backslash itself.
- Escaping then lowercasing in a way that alters the escapes.
Where this goes wrong in practice
A typical case is a URL allowlist implemented as a regex built from a configured domain: new RegExp(domain). Without escaping, the dots match any character, so exampleXcom passes, and without anchors, evil.example.com.attacker.net passes too. Escape the domain, anchor the pattern with ^ and $, and better yet parse the URL and compare the host exactly.
Search features are the second case. A user types c++ and the server returns a 500 error because the plus sign made an invalid pattern. That is a reliability bug first, and a denial of service vector when the pattern also causes heavy backtracking.
Quick check
When can you skip regex escaping altogether?
When a plain substring or equality check does the job. Then no pattern is built from user input.