File: //proc/self/root/usr/local/lib/python3.9/site-packages/wordfence/scanning/filtering.py
import re
import os
from typing import Optional, List, Callable, Pattern, AnyStr
class FilterCondition:
def __init__(self, test: Callable[[str], bool], allow: bool = True):
self.test = test
self.allow = allow
def evaluate(self, path: bytes) -> bool:
return self.test(path)
class FileFilter:
def __init__(self, conditions: Optional[List[FilterCondition]] = None):
self._conditions = conditions if conditions is not None else []
def add_condition(self, condition: FilterCondition) -> None:
self._conditions.append(condition)
def add(self, test: Callable[[str], bool], allow: bool = True):
self.add_condition(FilterCondition(test, allow))
def filter(self, path: bytes) -> bool:
allowed = False
for condition in self._conditions:
if condition.allow and allowed:
continue # Only a single allow condition needs to match
matched = condition.evaluate(path)
if matched:
if condition.allow:
allowed = True
else:
return False # Any disallowed condition takes precedence
return allowed
def matches_regex(regex: re.Pattern, string: bytes) -> bool:
return regex.search(string) is not None
def filter_any(path: bytes) -> bool:
return True
PATTERN_PHP = re.compile(
br'\.(?:php(?:\d+)?|phtml)(\.|$)',
re.IGNORECASE
)
PATTERN_HTML = re.compile(
br'\.(?:html?)(\.|$)',
re.IGNORECASE
)
PATTERN_JS = re.compile(
br'\.(?:js|svg)(\.|$)',
re.IGNORECASE
)
PATTERN_IMAGES = re.compile(
(
br'\.(?:jpg|jpeg|mp3|avi|m4v|mov|mp4|gif|png|tiff?|svg|sql|js|tbz2?' # noqa: E501
br'|bz2?|xz|zip|tgz|gz|tar|log|err\d+)(\.|$)'
),
re.IGNORECASE
)
def filter_php(path: bytes) -> bool:
return matches_regex(PATTERN_PHP, path)
def filter_html(path: bytes) -> bool:
return matches_regex(PATTERN_HTML, path)
def filter_js(path: bytes) -> bool:
return matches_regex(PATTERN_JS, path)
def filter_images(path: bytes) -> bool:
return matches_regex(PATTERN_IMAGES, path)
class FilenameFilter:
def __init__(self, value: bytes):
self.value = value
def __call__(self, path: bytes):
filename = os.path.basename(path)
return filename == self.value
class Filter:
def __init__(self, pattern: Pattern[AnyStr]):
self.pattern = pattern
def __call__(self, path: bytes) -> bool:
return matches_regex(self.pattern, path)
class InvalidPatternException(Exception):
def __init__(self, pattern: bytes):
self.pattern = pattern
def filter_pattern(regex: bytes) -> Callable[[bytes], bool]:
try:
pattern = re.compile(regex)
return Filter(pattern)
except re.error:
raise InvalidPatternException(regex)