pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
62 lines (50 loc) • 1.64 kB
YAML
# Unsafe Dynamic Regex
# Detects re.X() calls where the pattern argument is a variable (not a string literal).
# Static regexes (r"...", "...") are safe.
id: python-unsafe-regex
name: Unsafe Dynamic Regex
severity: warning
category: security
defect_class: injection
inline_tier: blocking
language: python
message: "re.{{FUNC}}() with dynamic pattern — ReDoS risk if pattern is user-controlled"
description: |
Building regex patterns from variables can cause ReDoS (Regular Expression
Denial of Service) if the pattern contains user input. A crafted input can
cause catastrophic backtracking.
✅ FIX:
# If pattern must be dynamic, validate/escape it first
import re
safe_pattern = re.escape(user_input)
re.search(safe_pattern, text)
# Or use string methods instead
text.find(user_input)
query: |
(call
function: (attribute
object: (identifier) @MOD
attribute: (identifier) @FUNC)
arguments: (argument_list) @ARGS)
(#eq? @MOD "re")
(#match? @FUNC "^(compile|match|search|fullmatch|findall|finditer|sub|subn|split)$")
metavars:
- MOD
- FUNC
- ARGS
# Post-filter: fire ONLY when the first argument is an identifier (dynamic pattern),
# not a string literal (static pattern, which is safe).
post_filter: regex_first_arg_identifier
has_fix: false
tags:
- security
- regex
- ReDoS
- python-specific
examples:
bad: |
pattern = req.GET["q"] # attacker-controlled
re.search(pattern, text) # BAD - dynamic pattern
good: |
re.search(r"static_pattern", text) # OK - literal pattern
re.search(re.escape(user_input), text) # OK - escaped