Regex Timeout Guard for Untrusted Input
Run operator-supplied regular expressions with the regex module timeout, an input length cap, and explicit failure handling.
Code
import regex
USER_PATTERN = regex.compile(
r"^(?P<user>[a-z0-9_]{3,32})@(?P<domain>[a-z0-9.-]{1,120})$"
)
def safe_fullmatch(
pattern: regex.Pattern,
text: str,
*,
timeout: float = 0.05,
max_length: int = 4096,
) -> regex.Match | None:
if len(text) > max_length:
raise ValueError(f"input length {len(text)} exceeds {max_length}")
try:
return pattern.fullmatch(text, timeout=timeout)
except TimeoutError as error:
raise TimeoutError("pattern evaluation exceeded the time budget") from error
if __name__ == "__main__":
match = safe_fullmatch(USER_PATTERN, "[email protected]")
assert match is not None
print(match.group("user"), match.group("domain"))
try:
safe_fullmatch(USER_PATTERN, "x" * 5000)
except ValueError as error:
print("rejected:", error)
evil = regex.compile(r"^(a+)+$")
try:
safe_fullmatch(evil, "a" * 4000 + "b", timeout=0.001)
except TimeoutError as error:
print("timeout:", error)
Report a problem with this exampleOpens a pre-filled issue on GitHub.