<p align="center">
<img src="https://raw.githubusercontent.com/nolindnaidoo/regex-le/main/src/assets/images/icon.png" alt="regex-le logo" width="96" height="96"/>
</p>
<h1 align="center">regex-le</h1>
<p align="center">
<b>Find every regex in a codebase and report which can be driven into catastrophic backtracking</b><br/>
<i>nothing is executed — the verdict comes from the shape of the pattern</i>
</p>
<p align="center">
<a href="https://crates.io/crates/regex-le">
<img src="https://img.shields.io/crates/v/regex-le.svg" alt="regex-le on crates.io" />
</a>
<a href="https://crates.io/crates/regex-le">
<img src="https://img.shields.io/crates/d/regex-le.svg" alt="crates.io downloads" />
</a>
<a href="https://github.com/nolindnaidoo/regex-le/actions/workflows/ci-crate.yml">
<img src="https://github.com/nolindnaidoo/regex-le/actions/workflows/ci-crate.yml/badge.svg" alt="Build Status" />
</a>
<img src="https://img.shields.io/badge/rustc-1.88+-93450a.svg" alt="MSRV: Rust 1.88+" />
<a href="https://github.com/nolindnaidoo/regex-le/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT" />
</a>
<a href="https://letools.dev/tools/regex-le">
<img src="https://img.shields.io/badge/web-letools.dev-00A0FF.svg" alt="letools.dev" />
</a>
</p>
<p align="center">
<img src="https://raw.githubusercontent.com/nolindnaidoo/regex-le/main/assets/demo.gif" alt="regex-le demo — the real binary, recorded by assets/demo.tape" width="100%"/>
</p>
> **Useful?** A star is how other developers find it —
> [★ GitHub](https://github.com/nolindnaidoo/regex-le) ·
> [letools.dev/tools/regex-le](https://letools.dev/tools/regex-le)
A regex that backtracks catastrophically is a denial of service with a
code review that approved it. `(\w+)+@` looks like an email check and
hangs a request thread on forty characters of input. regex-le finds
every pattern in a tree — JavaScript and TypeScript literals and
`RegExp` constructors, and the call sites Python, Rust, Go, Java, Ruby,
PHP and C# write a pattern at, not the division signs and URLs a grep
would hand you — and tells you which ones have that shape.
It never runs them. The verdict reads the pattern *text*, so scanning a
repository is a cheap deterministic CI step with no engine, no timeout
and nothing to sandbox.
It is the second frontend of
[Regex-LE](https://github.com/nolindnaidoo/regex-le#readme), the VS Code
extension — one product, two frontends, one repository, so the two can
never read a document differently. The corpus both build against lives
at
[`crate/fixtures/`](https://github.com/nolindnaidoo/regex-le/tree/main/crate/fixtures),
and CI fails on drift.
## Sixty seconds
```bash
regex-le . # every vulnerable pattern in the tree
regex-le --severity high src/ # only the exponential shapes
regex-le --all src/ # every pattern, vulnerable or not
# the point of the whole thing:
```
./src/validate.js:1:15 /(\w+)+@/g [high] Nested unbounded quantifiers can cause exponential backtracking
- **`extract_patterns`** — content in, patterns and verdicts out.
Touches no filesystem. The npm server ships the same tool with
byte-identical output; one corpus runs against both.
- **`regex_le_lint`** — files or directories in, the same reports the
CLI writes.
`ok` means the scan ran, never that it found something. A file with no
vulnerable pattern is a result, not an error.
## The other four ways to run it
| **VS Code** | The lint *and* the tester, in your editor | [Marketplace](https://marketplace.visualstudio.com/items?itemName=nolindnaidoo.regex-le) |
| **Cursor, VSCodium, Windsurf** | The same extension | [Open VSX](https://open-vsx.org/extension/OffensiveEdge/regex-le) |
| **Any MCP agent, via Node** | `extract_patterns` over stdio | `npx regex-le-mcp` · [npm](https://www.npmjs.com/package/regex-le-mcp) |
| **Zed** | The MCP server as a context server | [add it by hand](https://zed.dev/docs/ai/mcp) *(no listing yet)* |
All sixteen LE tools are on **[letools.dev](https://letools.dev)**.
## Documentation
| What this tool is allowed to say — scope, output contract, refusals, non-goals | [SPEC.md](https://github.com/nolindnaidoo/regex-le/blob/main/crate/SPEC.md) |
| How the code is written and held together — architecture, invariants, the gates | [AGENTS.md](https://github.com/nolindnaidoo/regex-le/blob/main/crate/AGENTS.md) |
| The VS Code extension this shares its extraction with | [README.md](https://github.com/nolindnaidoo/regex-le/blob/main/README.md) |
| What changed | [CHANGELOG.md](https://github.com/nolindnaidoo/regex-le/blob/main/crate/CHANGELOG.md) |
| The tool's page, and the other fifteen | [letools.dev/tools/regex-le](https://letools.dev/tools/regex-le) |
## More from the LE family
Sixteen single-purpose tools for the work in front of every model. Each ships
a Rust CLI and an MCP server. One page: **[letools.dev](https://letools.dev)**
**Get it out**
- **[String-LE](https://letools.dev/tools/string-le)** — Extract every string in a codebase, with its position, so a person can read them
- **[Numbers-LE](https://letools.dev/tools/numbers-le)** — Extract every hardcoded number in a codebase, so a person can check them
- **[Units-LE](https://letools.dev/tools/units-le)** — Extract every quantity with its unit, normalized, and refuse the ambiguous ones by name
- **[Dates-LE](https://letools.dev/tools/dates-le)** — Extract every date and timestamp, and the exact instant each one resolves to
- **[IDs-LE](https://letools.dev/tools/ids-le)** — Extract every UUID, ULID, NanoID, ObjectId and Snowflake, and decode the time inside
- **[IPs-LE](https://letools.dev/tools/ips-le)** — Extract every IP address, CIDR block and MAC, normalized and classified by scope
- **[URLs-LE](https://letools.dev/tools/urls-le)** — Extract every URL in a codebase, with its protocol and exact position
- **[Paths-LE](https://letools.dev/tools/paths-le)** — Extract every file path in a codebase, and say whether it still points at anything
- **[Colors-LE](https://letools.dev/tools/colors-le)** — Extract every color in a codebase, and say which ones are not in your palette
**Check it**
- **[Regex-LE](https://letools.dev/tools/regex-le)** — Find every regex in a codebase, and report which can be driven into catastrophic backtracking
- **[Versions-LE](https://letools.dev/tools/versions-le)** — Find where one dependency is constrained differently across a repository's manifests
- **[i18n-LE](https://letools.dev/tools/i18n-le)** — Identify the i18n library a project uses, then audit its catalogs by that library's rules
- **[Scrape-LE](https://letools.dev/tools/scrape-le)** — Check whether a page is scrapeable before the scraper is written, and say when it cannot tell
**Guard it**
- **[Secrets-LE](https://letools.dev/tools/secrets-le)** — Find hardcoded credentials in a codebase, and never print one into the report
- **[EnvSync-LE](https://letools.dev/tools/envsync-le)** — Compare the dotenv files in a tree, and say which keys are missing from which
- **[Unicode-LE](https://letools.dev/tools/unicode-le)** — Find the Unicode that hides meaning — bidi controls, invisibles, homoglyphs, mixed scripts
Each stands on its own: no shared crate, no published core. Where two of them
agree, it is because the same answer was right twice.
**Contact** — [nolindnaidoo.com](https://nolindnaidoo.com) · [GitHub](https://github.com/nolindnaidoo) · [LinkedIn](https://www.linkedin.com/in/nolindnaidoo/)
## Also by nolindnaidoo
**Rust** — pixelcoords and pixelactions are one loop: pixelcoords answers
*where*, pixelactions *acts* there. Their own tools, their own voice — not
part of the LE family.
- **[pixelcoords](https://github.com/nolindnaidoo/pixelcoords)** — Freeze your screen, mark regions, get pixel-exact coordinates and crops
[pixelcoords.dev](https://pixelcoords.dev) · [crates.io](https://crates.io/crates/pixelcoords) · [docs.rs](https://docs.rs/pixelcoords)
- **[pixelactions](https://github.com/nolindnaidoo/pixelactions)** — Consume human-verified coordinates, perform the interaction, confirm it landed
[pixelactions.dev](https://pixelactions.dev) · [crates.io](https://crates.io/crates/pixelactions) · [docs.rs](https://docs.rs/pixelactions)
## License
MIT — see [LICENSE](https://github.com/nolindnaidoo/regex-le/blob/main/LICENSE).