<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>
> **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 — literals and `RegExp` constructors, 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 ten LE tools are on **[letools.dev](https://letools.dev)**.
## Also by nolindnaidoo
**Rust** — pixelcoords and pixelactions are one loop: pixelcoords answers *where*, pixelactions *acts* there. The five LE crates are the terminal half of the extensions they sit in — the same detection, held to the extension's own corpus, and an exit code instead of a results editor.
- **[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)
- **[paths-le](https://github.com/nolindnaidoo/paths-le/tree/main/crate)** — Find every path in a codebase and report whether it still points at anything
[crates.io](https://crates.io/crates/paths-le)
- **[secrets-le](https://github.com/nolindnaidoo/secrets-le/tree/main/crate)** — Find hardcoded credentials, and never print one
[crates.io](https://crates.io/crates/secrets-le)
- **[urls-le](https://github.com/nolindnaidoo/urls-le/tree/main/crate)** — Extract every URL from a codebase, with its protocol and exact position
[crates.io](https://crates.io/crates/urls-le)
- **[scrape-le](https://github.com/nolindnaidoo/scrape-le/tree/main/crate)** — Check whether a page is scrapeable before the scraper is written
[crates.io](https://crates.io/crates/scrape-le)
**Contact Developer** — [nolindnaidoo.com](https://nolindnaidoo.com) · [GitHub](https://github.com/nolindnaidoo) · [LinkedIn](https://www.linkedin.com/in/nolindnaidoo/)
## License
MIT — see [LICENSE](https://github.com/nolindnaidoo/regex-le/blob/main/LICENSE).