Skip to content

Commit

Permalink
Merge pull request #26 from certego/update_check_value
Browse files Browse the repository at this point in the history
Update check value
  • Loading branch information
ManofWax committed Mar 12, 2024
2 parents f70c81a + fc1d1f0 commit 7f0ecef
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 44 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
## 2.2.x
### 2.2.9
#### Bugfix
* Fixed customer routing history
#### Changes
* Updated _check_value() method in filters
### 2.2.8
#### Bugfix
* Fixed a bug when an existing fields didn't match
Expand Down
7 changes: 7 additions & 0 deletions routing_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ def test_routing_history(self):
self.assertTrue("Workshop" in self.test_event_1["certego"]["routing_history"])
self.assertTrue("TyreFit" in self.test_event_1["certego"]["routing_history"])

def test_routing_history_no_customer(self):
self.routing.load_from_dicts([load_test_data("test_rule_24_routing_history"), load_test_data("test_customer_1")])
self.routing.match(self.test_event_1)
self.routing.match(self.test_event_1, type_="customers")
self.assertTrue(self.test_event_1["certego"]["routing_history"]["Workshop"])
self.assertNotIn("customer", self.test_event_1["certego"]["routing_history"])

def test_routing_history_stream_none(self):
self.routing.load_from_dicts([load_test_data("test_rule_1_equals")])
self.routing.match(self.test_event_1)
Expand Down
118 changes: 76 additions & 42 deletions routingfilter/filters/filters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import re
from abc import ABC, abstractmethod
from typing import Optional
from typing import NoReturn, Optional

import macaddress
from IPy import IP
Expand All @@ -19,21 +19,25 @@ def __init__(self, key, value, **kwargs):
def match(self, event: DictQuery) -> bool:
return NotImplemented

def _check_value(self) -> Optional[Exception]:
@abstractmethod
def _check_value(self) -> Exception | NoReturn:
"""
Check if values in self._value are correct and raise an exception if they are incorrect.
Check if values in self._value are correct and raise an exception if they are incorrect. If necessary, it converts value in lower case.
:return: no value or raise an exception
:rtype: Optional[Exception]
:rtype: NoReturn | Exception
"""
return None
return NotImplemented


class AllFilter(AbstractFilter):
def __init__(self):
key = value = []
super().__init__(key, value)

def _check_value(self) -> Exception | NoReturn:
return

def match(self, event: DictQuery) -> bool:
"""
Return always true.
Expand All @@ -51,6 +55,9 @@ def __init__(self, key):
value = []
super().__init__(key, value)

def _check_value(self) -> Exception | NoReturn:
return

def match(self, event: DictQuery) -> bool:
"""
Return True if one of the key exists in the event.
Expand Down Expand Up @@ -83,6 +90,13 @@ class EqualFilter(AbstractFilter):
def __init__(self, key, value):
super().__init__(key, value)

def _check_value(self) -> Exception | NoReturn:
tmp = []
for value in self._value:
value = value.lower() if isinstance(value, str) else str(value)
tmp.append(value)
self._value = tmp

def match(self, event: DictQuery):
"""
Check if at least a key matches at least one value.
Expand All @@ -92,16 +106,12 @@ def match(self, event: DictQuery):
:return: true if event matches, false otherwise
:rtype: bool
"""
filter_value = []
for value in self._value:
value = value.lower() if isinstance(value, str) else str(value)
filter_value.append(value)
for key in self._key:
event_value = event.get(key, [])
event_value = event_value if isinstance(event_value, list) else [event_value]
for value in event_value:
value = value.lower() if isinstance(value, str) else str(value)
if value in filter_value:
if value in self._value:
return True
return False

Expand All @@ -120,6 +130,13 @@ def match(self, event: DictQuery) -> bool:


class StartswithFilter(AbstractFilter):
def _check_value(self) -> Exception | NoReturn:
tmp = []
for prefix in self._value:
prefix = str(prefix).lower()
tmp.append(prefix)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Return True if at least one event value corresponding to a key starts with one of the value.
Expand Down Expand Up @@ -148,13 +165,19 @@ def _check_startswith(self, value: str) -> bool:
"""
value = value.lower()
for prefix in self._value:
prefix = str(prefix).lower()
if value.startswith(prefix):
return True
return False


class EndswithFilter(AbstractFilter):
def _check_value(self) -> Exception | NoReturn:
tmp = []
for suffix in self._value:
suffix = str(suffix).lower()
tmp.append(suffix)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Return True if at least one event value corresponding to a key ends with one of the value.
Expand All @@ -172,7 +195,7 @@ def match(self, event: DictQuery) -> bool:
return True
return False

def _check_endswith(self, value):
def _check_endswith(self, value: str) -> bool:
"""
Check if the value end with one of the suffix given.
Expand All @@ -183,13 +206,19 @@ def _check_endswith(self, value):
"""
value = value.lower()
for suffix in self._value:
suffix = str(suffix).lower()
if str(value).endswith(suffix):
if value.endswith(suffix):
return True
return False


class KeywordFilter(AbstractFilter):
def _check_value(self) -> Exception | NoReturn:
tmp = []
for keyword in self._value:
keyword = str(keyword).lower()
tmp.append(keyword)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Return True if at least one value is present in the event value of corresponding key.
Expand All @@ -216,29 +245,28 @@ def _check_keyword(self, value: str) -> bool:
:return: true or false
:rtype: bool
"""
value = value.lower()
for keyword in self._value:
keyword = str(keyword).lower()
if keyword in value:
if keyword in value.lower():
return True
return False


class RegexpFilter(AbstractFilter):
def _check_value(self) -> Optional[Exception]:
def _check_value(self) -> Exception | NoReturn:
"""
Check if values in self._value are valid regexes.
:return: none or error generated:
:rtype: Optional[Exception]
"""
tmp = []
for value in self._value:
try:
re.compile(value)
tmp.append(re.compile(value))
except re.error as e:
self.logger.error(f"Invalid regex {value}, during check of value list {self._value}. Error message: {e}")
raise ValueError(f"Regex check failed: error for value {value}. Error message: {e}")
return None
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Expand Down Expand Up @@ -267,7 +295,7 @@ def _check_regex(self, value: str) -> bool:
:rtype: bool
"""
for regex in self._value:
if re.search(regex, value):
if regex.search(value):
return True
return False

Expand All @@ -276,13 +304,14 @@ class NetworkFilter(AbstractFilter):
def __init__(self, key, value):
super().__init__(key, value)

def _check_value(self) -> Optional[Exception]:
def _check_value(self) -> Exception | NoReturn:
"""
Check if the values in self._value are valid IP addresses.
:return: none or error generated
:rtype: Optional[Exception]
"""
tmp = []
for value in self._value:
try:
value = IP(value)
Expand All @@ -292,7 +321,8 @@ def _check_value(self) -> Optional[Exception]:
except TypeError as e:
self.logger.error(f"IP address (type error) error, during check of value {value} in list {self._value}. Error was: {e}.")
raise ValueError(f"IP address check failed: type error for value {value}.")
return None
tmp.append(value)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Expand Down Expand Up @@ -323,7 +353,7 @@ def _check_network(self, ip_address: str) -> bool:
try:
ip_address = IP(ip_address)
for value in self._value:
if ip_address in IP(value):
if ip_address in value:
return True
except ValueError as e:
self.logger.debug(f"Error in parsing IP address (value error): {e}. ")
Expand All @@ -349,17 +379,20 @@ class DomainFilter(AbstractFilter):
def __init__(self, key, value):
super().__init__(key, value)

def _check_value(self) -> Optional[Exception]:
def _check_value(self) -> Exception | NoReturn:
"""
Check if values in self._value are string.
:return: none or error generated
:rtype: bool
"""
for value in self._value:
if not isinstance(value, str):
raise ValueError(f"Domain check failed: value {value} is not a string.")
return None
tmp = []
for domain in self._value:
if not isinstance(domain, str):
raise ValueError(f"Domain check failed: value {domain} is not a string.")
domain = str(domain).lower()
tmp.append(domain)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Expand Down Expand Up @@ -388,8 +421,7 @@ def _check_domain(self, value: str) -> bool:
"""
value = value.lower()
for domain in self._value:
domain = str(domain).lower()
if value == domain or str(value).endswith(f".{domain}"):
if value == domain or value.endswith(f".{domain}"):
return True
return False

Expand All @@ -400,32 +432,32 @@ def __init__(self, key, value, comparator_type):
self._check_comparator_type()
super().__init__(key, value)

def _check_value(self) -> Optional[Exception]:
def _check_value(self) -> Exception | NoReturn:
"""
Check if values in self._value are float.
:return: none or error generated
:rtype: Optional[Exception]
:rtype: Exception | NoReturn
"""
tmp = []
for value in self._value:
try:
float(value)
tmp.append(float(value))
except ValueError:
self.logger.error(f"Comparator check failed: value {value} of list {self._value} is not a float")
raise ValueError(f"Comparator check failed: value {value} is not a float")
return None
self._value = tmp

def _check_comparator_type(self) -> Optional[Exception]:
def _check_comparator_type(self) -> Exception | NoReturn:
"""
Check if comparator is valid.
:return: none or error generated
:rtype: Optional[Exception]
:rtype: Exception | NoReturn
"""
if self._comparator_type not in ["GREATER", "LESS", "GREATER_EQ", "LESS_EQ"]:
self.logger.error(f"Comparator check failed: value {self._comparator_type} is not valid.")
raise ValueError(f"Comparator type check failed. {self._comparator_type} is not a valid comparator.")
return None

def match(self, event: DictQuery) -> bool:
"""
Expand Down Expand Up @@ -455,7 +487,6 @@ def _compare(self, value: float) -> bool:
"""
for term in self._value:
try:
term = float(term)
value = float(value)
except ValueError as e:
self.logger.debug(f"Error in parsing value to float in comparator filter: {e}. ")
Expand All @@ -480,19 +511,22 @@ class TypeofFilter(AbstractFilter):
def __init__(self, key, value):
super().__init__(key, value)

def _check_value(self) -> Optional[Exception]:
def _check_value(self) -> Exception | NoReturn:
"""
Check if value is a correct type.
:return: no value or raised an exception
:rtype: Optional[Exception]
:rtype: NoReturn | Exception
"""
valid_type = ["str", "int", "float", "bool", "list", "dict", "ip", "mac"]
tmp = []
for value in self._value:
value = str(value).lower()
if value not in valid_type:
self.logger.error(f"Type check failed: value {value} of list {self._value} is invalid.")
raise ValueError(f"Type check failed: value {value} is invalid.")
return None
tmp.append(value)
self._value = tmp

def match(self, event: DictQuery) -> bool:
"""
Expand Down
3 changes: 2 additions & 1 deletion routingfilter/filters/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def match(self, event: DictQuery) -> Results | None:
if key in routing_history:
output_copy.pop(key)
else:
routing_history.update({key: now})
if key != "customer":
routing_history.update({key: now})
results = Results(rules=self.uid, output=output_copy)
return results

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="routingfilter",
version="2.2.8",
version="2.2.9",
packages=find_packages(include=["routingfilter", "routingfilter.*"]),
include_package_data=True,
install_requires=["IPy~=1.1", "macaddress~=2.0.2"],
Expand Down
Loading

0 comments on commit 7f0ecef

Please sign in to comment.