Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add content base filters in events #3258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 133 additions & 3 deletions localstack/services/events/events_starter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import re
import datetime
import json
import uuid
import logging
import ipaddress
from moto.events.models import Rule as rule_model
from moto.events.responses import EventsHandler as events_handler
from localstack import config
Expand All @@ -24,6 +26,10 @@
DEFAULT_EVENT_BUS_NAME: set()
}

CONTENT_BASE_FILTER_KEYWORDS = [
'prefix', 'anything-but', 'numeric', 'cidr', 'exists'
]


def send_event_to_sqs(event, arn):
region = arn.split(':')[3]
Expand Down Expand Up @@ -52,12 +58,39 @@ def filter_event_with_target_input_path(target, event):


def filter_event_based_on_event_format(self, rule, event):
def filter_event(event_pattern, event):
for key, value in event_pattern.items():
event_value = event.get(key.lower())
if not event_value:
return False

if isinstance(value, list) and not identify_content_base_parameter_in_pattern(value):
if isinstance(event_value, list) and \
get_two_lists_intersection(value, event_value) == []:
return False
elif not isinstance(event_value, list) and \
isinstance(event_value, (str, int)) and \
event_value not in value:
return False

elif isinstance(value, list) and identify_content_base_parameter_in_pattern(value):
if not filter_event_with_content_base_parameter(value, event_value):
return False

elif isinstance(value, (str, int)):
try:
if isinstance(json.loads(value), dict) and \
not filter_event(json.loads(value), event_value):
return False
except json.decoder.JSONDecodeError:
return False
return True

rule_information = self.events_backend.describe_rule(rule)
if rule_information.event_pattern:
event_pattern = json.loads(rule_information.event_pattern)
for key, value in event_pattern.items():
if event.get(key.lower()) and event.get(key.lower()) not in value and event.get(key) != value:
return False
if not filter_event(event_pattern, event):
return False
return True


Expand Down Expand Up @@ -198,3 +231,100 @@ def start_events(port=None, asynchronous=None, update_listener=None):
asynchronous=asynchronous,
update_listener=update_listener
)


# ---------------
# HELPER METHODS
# ---------------


def get_two_lists_intersection(lst1, lst2):
lst3 = [value for value in lst1 if value in lst2]
return lst3


def identify_content_base_parameter_in_pattern(parameters):
if any([list(param.keys())[0] in CONTENT_BASE_FILTER_KEYWORDS for param in parameters if isinstance(param, dict)]):
return True


def filter_event_with_content_base_parameter(pattern_value, event_value):
for element in pattern_value:
if (isinstance(element, (str, int))) \
and (event_value == element or element in event_value):
return True
elif isinstance(element, dict):
element_key = list(element.keys())[0]
element_value = element.get(element_key)
if element_key.lower() == 'prefix':
if re.match(r'^{}'.format(element_value), event_value):
return True
elif element_key.lower() == 'exists':
if element_value and event_value:
return True
elif not element_value and not event_value:
return True
elif element_key.lower() == 'cidr':
ips = [str(ip) for ip in ipaddress.IPv4Network(element_value)]
if event_value in ips:
return True
elif element_key.lower() == 'numeric':
if check_valid_numeric_content_base_rule(element_value):
for index in range(len(element_value)):
if isinstance(element_value[index], int):
continue
if element_value[index] == '>' and \
isinstance(element_value[index + 1], int) and \
event_value <= element_value[index + 1]:
break
elif element_value[index] == '>=' and \
isinstance(element_value[index + 1], int) and \
event_value < element_value[index + 1]:
break
elif element_value[index] == '<' and \
isinstance(element_value[index + 1], int) and \
event_value >= element_value[index + 1]:
break
elif element_value[index] == '<=' and \
isinstance(element_value[index + 1], int) and \
event_value > element_value[index + 1]:
break
else:
return True

elif element_key.lower() == 'anything-but':
if isinstance(element_value, list) and \
event_value not in element_value:
return True
elif (isinstance(element_value, (str, int))) and \
event_value != element_value:
return True
elif isinstance(element_value, dict):
nested_key = list(element_value)[0]
if nested_key == 'prefix' and \
not re.match(r'^{}'.format(element_value.get(nested_key)), event_value):
return True
return False


def check_valid_numeric_content_base_rule(list_of_operators):
if len(list_of_operators) > 4:
return False

if '=' in list_of_operators:
return False

if len(list_of_operators) > 2:
upper_limit = None
lower_limit = None
for index in range(len(list_of_operators)):
if not isinstance(list_of_operators[index], int) and \
'<' in list_of_operators[index]:
upper_limit = list_of_operators[index + 1]
if not isinstance(list_of_operators[index], int) and \
'>' in list_of_operators[index]:
lower_limit = list_of_operators[index + 1]
if upper_limit and lower_limit and upper_limit < lower_limit:
return False
index = index + 1
return True
Loading