Post
Regex Builder: a simple page for building regular expressions
One HTML file that turns a value from your logs into a correctly escaped regular expression, with toggles for match type, case, URL encoding and negation. What the Regex Builder does, how to use it, and one lesson in reading a pattern before you deploy it.
5 min read
Most of my blocking rules begin as a suspicious line in a log. The Cosmic Mind project runs Caddy with the Coraza WAF in front of it, and a path, an address or a user agent that should never come back has to become a regular expression that matches it and nothing else. That is where the mistakes live. A dot left bare matches any character, and the attacker who sent ../ yesterday sends %2e%2e%2f today.
Regex Builder is the page I made to stop making those mistakes by hand. Paste the value, click a few toggle buttons, and it prints the pattern as you type. It is a single HTML file, 21,370 bytes in 432 lines, with no dependencies, no build step and no external requests. Everything runs in your browser, so nothing from your logs leaves the page, and a hosted copy lives on GitHub Pages.
What it does and how to use it
The page calls itself WAF Regex Builder, and its first audience writes Coraza or ModSecurity rules, CrowdSec scenarios, Nginx or Caddy location blocks and grep patterns from suspicious logs. It helps anyone who has wondered which characters need a backslash, because what it shows is the correctly escaped pattern for a literal string.
| Group | Choices | Effect |
|---|---|---|
| Match type | Contains, Exact, Starts With, Ends With | ^, $, both or neither |
| Case | Insensitive (default), Sensitive | (?i) on or off |
| Encoding | URL encoded %XX, nested %25XX |
Also matches encoded forms |
| Extras | Negate, Wildcard Segments, Word Boundaries, IP Address Mode | Lookahead, /.*?/, \b, escaped dots |
Under the output, up to nine smart suggestions offer ready-made variants you click to copy, and the dark page stays usable down to a 600 px phone screen.
Open the hosted page and paste something from a log. Paste xmlrpc.php, leave the defaults alone, and you get (?i)xmlrpc\.php: a case-insensitive pattern that matches the file name anywhere, with the dot escaped so that it means a dot. Click Ends With and it becomes (?i)xmlrpc\.php$.
To host your own copy, put index.html wherever your static files live, or serve it from any folder for a quick test:
Code, shellcp index.html /var/www/html/regex-builder/index.html
python3 -m http.server 8080Or fork the repository and turn on GitHub Pages from the root of the main branch.
How it builds the pattern
Five steps run in a fixed order. First, the special characters . * + ? ^ $ { } ( ) | [ ] and \ each get a backslash. Second, with an encoding option on, each character becomes an alternation of its literal form and its %XX code in both cases; the nested option adds %25XX, which catches %252e%252e%252f standing in for ../. Third come the extras from the table, then the anchors for the match type, and last Negate wraps everything as ^(?!.*...) while case-insensitive mode, the default because attackers randomize case, prepends (?i).
The suggestions follow the shape of the input: a leading / means a path and brings a directory block and a negate variant; digits, dots, colons and the letters a to f mean an IP and bring block-this-IP plus /24 and /16 ranges; anything else is treated as a user agent and gets word boundaries.
One lesson in reading a pattern
The real value is not the typing saved but the reading: a pattern you can read is a pattern you can trust. Escaping comes first: an unescaped . matches any character, so a rule for 1.2.3.4 also matches 1a2b3c4. Anchors come second: ^ and $ turn "contains" into "starts with", "ends with" and "exactly this". Flags come third, and (?i) at the front changes the meaning of the whole pattern behind it. Take the generated output apart piece by piece, name what each piece does, and only then paste it into a rule. A pattern you cannot read is a pattern you should not deploy.
What to watch for
Reading the code back turned up some edges. With either encoding option on, IP mode and Wildcard Segments are ignored and Word Boundaries is skipped. The Block /24 and Block /16 suggestions print CIDR notation such as 1.2.3.0/24, which is not a regex and belongs in an IP-list rule, and Block this IP prints the raw address, dots unescaped. The negated path suggestion puts (?i) inside the lookahead, which many engines reject.
The README also mentions Cosmic Mind, the Caddy and Coraza WAF stack that these rules end up in. The rest of what I publish is in the Aeon Forge catalog, and the one rule for contributions here is that the tool stays a single HTML file.
Sources
- Repository: AEON-7/regex-builder (README, LICENSE, index.html; MIT license)
- Live demo: aeon-7.github.io/regex-builder
- Related stack: AEON-7/cosmic-mind
- Maker account: AEON-7 on GitHub