2017-07-14 10:31:42 +02:00
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
|
|
class UnsafePatternError(Exception):
|
|
|
|
pass
|
|
|
|
|
2017-07-15 01:30:35 +02:00
|
|
|
cached_patterns = {}
|
|
|
|
|
2017-07-14 10:31:42 +02:00
|
|
|
|
|
|
|
def isSafePattern(pattern):
|
|
|
|
if len(pattern) > 255:
|
2017-07-27 16:29:12 +02:00
|
|
|
raise UnsafePatternError("Pattern too long: %s characters in %s" % (len(pattern), pattern))
|
2017-07-14 10:31:42 +02:00
|
|
|
|
2019-01-06 15:15:47 +01:00
|
|
|
unsafe_pattern_match = re.search(r"[^\.][\*\{\+]", pattern) # Always should be "." before "*{+" characters to avoid ReDoS
|
2017-07-14 10:31:42 +02:00
|
|
|
if unsafe_pattern_match:
|
2017-07-27 16:29:12 +02:00
|
|
|
raise UnsafePatternError("Potentially unsafe part of the pattern: %s in %s" % (unsafe_pattern_match.group(0), pattern))
|
2017-07-15 01:30:53 +02:00
|
|
|
|
2019-01-06 15:15:47 +01:00
|
|
|
repetitions = re.findall(r"\.[\*\{\+]", pattern)
|
2017-07-15 01:30:53 +02:00
|
|
|
if len(repetitions) >= 10:
|
2017-07-27 16:29:12 +02:00
|
|
|
raise UnsafePatternError("More than 10 repetitions of %s in %s" % (repetitions[0], pattern))
|
2017-07-15 01:30:53 +02:00
|
|
|
|
2017-07-14 10:31:42 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def match(pattern, *args, **kwargs):
|
2017-07-15 01:30:35 +02:00
|
|
|
cached_pattern = cached_patterns.get(pattern)
|
|
|
|
if cached_pattern:
|
|
|
|
return cached_pattern.match(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
if isSafePattern(pattern):
|
|
|
|
cached_patterns[pattern] = re.compile(pattern)
|
|
|
|
return cached_patterns[pattern].match(*args, **kwargs)
|