pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
77 lines (61 loc) • 2.06 kB
YAML
# Unsafe Regex Construction
# Detects dynamic regex construction that can lead to ReDoS
id: unsafe-regex
name: Dynamic Regex Construction
severity: warning
category: security
defect_class: injection
inline_tier: warning
language: typescript
message: "Dynamic regex from user input — can cause ReDoS (Regular Expression Denial of Service)"
description: |
Building regular expressions from user input or dynamic strings can
lead to ReDoS attacks. An attacker can craft input that causes
catastrophic backtracking.
❌ NEVER:
const regex = new RegExp(userInput, 'i'); // ReDoS risk!
const pattern = new RegExp(`\\b${term}\\b`); // Also risky!
✅ SAFE ALTERNATIVES:
1. Escape special regex characters before interpolation
2. Use string methods instead of regex when possible
3. Validate input against a whitelist
Example escaping:
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const regex = new RegExp(escapeRegExp(userInput), 'i');
query: |
(new_expression
constructor: (identifier) @CTOR
(#eq? @CTOR "RegExp")
arguments: (arguments
(template_string
(template_substitution) @INTERPOLATION) @PATTERN)
(#not-match? @INTERPOLATION "escape|Escape|replace"))
metavars:
- CTOR
- INTERPOLATION
- PATTERN
post_filter: unsafe_regex_dynamic_identifier
has_fix: false
tags:
- security
- regex
- redos
- injection
examples:
bad: |
// ReDoS vulnerability!
const searchRegex = new RegExp(`\\b${userSearch}\\b`, 'gi');
// Also vulnerable
const validator = new RegExp(req.body.pattern);
good: |
// Safe: Escape special characters
function escapeRegExp(string: string): string {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const safeRegex = new RegExp(escapeRegExp(userSearch), 'i');
// Safe: Use string methods
const found = text.toLowerCase().includes(userSearch.toLowerCase());
// Safe: Static regex
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;