|
| 1 | +import re |
1 | 2 | import datetime
|
2 | 3 | import json
|
3 | 4 | import uuid
|
4 | 5 | import logging
|
| 6 | +import ipaddress |
5 | 7 | from moto.events.models import Rule as rule_model
|
6 | 8 | from moto.events.responses import EventsHandler as events_handler
|
7 | 9 | from localstack import config
|
|
24 | 26 | DEFAULT_EVENT_BUS_NAME: set()
|
25 | 27 | }
|
26 | 28 |
|
| 29 | +CONTENT_BASE_FILTER_KEYWORDS = [ |
| 30 | + 'prefix', 'anything-but', 'numeric', 'cidr', 'exists' |
| 31 | +] |
| 32 | + |
27 | 33 |
|
28 | 34 | def send_event_to_sqs(event, arn):
|
29 | 35 | region = arn.split(':')[3]
|
@@ -52,12 +58,39 @@ def filter_event_with_target_input_path(target, event):
|
52 | 58 |
|
53 | 59 |
|
54 | 60 | def filter_event_based_on_event_format(self, rule, event):
|
| 61 | + def filter_event(event_pattern, event): |
| 62 | + for key, value in event_pattern.items(): |
| 63 | + event_value = event.get(key.lower()) |
| 64 | + if not event_value: |
| 65 | + return False |
| 66 | + |
| 67 | + if isinstance(value, list) and not identify_content_base_parameter_in_pattern(value): |
| 68 | + if isinstance(event_value, list) and \ |
| 69 | + get_two_lists_intersection(value, event_value) == []: |
| 70 | + return False |
| 71 | + elif not isinstance(event_value, list) and \ |
| 72 | + isinstance(event_value, (str, int)) and \ |
| 73 | + event_value not in value: |
| 74 | + return False |
| 75 | + |
| 76 | + elif isinstance(value, list) and identify_content_base_parameter_in_pattern(value): |
| 77 | + if not filter_event_with_content_base_parameter(value, event_value): |
| 78 | + return False |
| 79 | + |
| 80 | + elif isinstance(value, (str, int)): |
| 81 | + try: |
| 82 | + if isinstance(json.loads(value), dict) and \ |
| 83 | + not filter_event(json.loads(value), event_value): |
| 84 | + return False |
| 85 | + except json.decoder.JSONDecodeError: |
| 86 | + return False |
| 87 | + return True |
| 88 | + |
55 | 89 | rule_information = self.events_backend.describe_rule(rule)
|
56 | 90 | if rule_information.event_pattern:
|
57 | 91 | event_pattern = json.loads(rule_information.event_pattern)
|
58 |
| - for key, value in event_pattern.items(): |
59 |
| - if event.get(key.lower()) and event.get(key.lower()) not in value and event.get(key) != value: |
60 |
| - return False |
| 92 | + if not filter_event(event_pattern, event): |
| 93 | + return False |
61 | 94 | return True
|
62 | 95 |
|
63 | 96 |
|
@@ -198,3 +231,100 @@ def start_events(port=None, asynchronous=None, update_listener=None):
|
198 | 231 | asynchronous=asynchronous,
|
199 | 232 | update_listener=update_listener
|
200 | 233 | )
|
| 234 | + |
| 235 | + |
| 236 | +# --------------- |
| 237 | +# HELPER METHODS |
| 238 | +# --------------- |
| 239 | + |
| 240 | + |
| 241 | +def get_two_lists_intersection(lst1, lst2): |
| 242 | + lst3 = [value for value in lst1 if value in lst2] |
| 243 | + return lst3 |
| 244 | + |
| 245 | + |
| 246 | +def identify_content_base_parameter_in_pattern(parameters): |
| 247 | + if any([list(param.keys())[0] in CONTENT_BASE_FILTER_KEYWORDS for param in parameters if isinstance(param, dict)]): |
| 248 | + return True |
| 249 | + |
| 250 | + |
| 251 | +def filter_event_with_content_base_parameter(pattern_value, event_value): |
| 252 | + for element in pattern_value: |
| 253 | + if (isinstance(element, (str, int))) \ |
| 254 | + and (event_value == element or element in event_value): |
| 255 | + return True |
| 256 | + elif isinstance(element, dict): |
| 257 | + element_key = list(element.keys())[0] |
| 258 | + element_value = element.get(element_key) |
| 259 | + if element_key.lower() == 'prefix': |
| 260 | + if re.match(r'^{}'.format(element_value), event_value): |
| 261 | + return True |
| 262 | + elif element_key.lower() == 'exists': |
| 263 | + if element_value and event_value: |
| 264 | + return True |
| 265 | + elif not element_value and not event_value: |
| 266 | + return True |
| 267 | + elif element_key.lower() == 'cidr': |
| 268 | + ips = [str(ip) for ip in ipaddress.IPv4Network(element_value)] |
| 269 | + if event_value in ips: |
| 270 | + return True |
| 271 | + elif element_key.lower() == 'numeric': |
| 272 | + if check_valid_numeric_content_base_rule(element_value): |
| 273 | + for index in range(len(element_value)): |
| 274 | + if isinstance(element_value[index], int): |
| 275 | + continue |
| 276 | + if element_value[index] == '>' and \ |
| 277 | + isinstance(element_value[index + 1], int) and \ |
| 278 | + event_value <= element_value[index + 1]: |
| 279 | + break |
| 280 | + elif element_value[index] == '>=' and \ |
| 281 | + isinstance(element_value[index + 1], int) and \ |
| 282 | + event_value < element_value[index + 1]: |
| 283 | + break |
| 284 | + elif element_value[index] == '<' and \ |
| 285 | + isinstance(element_value[index + 1], int) and \ |
| 286 | + event_value >= element_value[index + 1]: |
| 287 | + break |
| 288 | + elif element_value[index] == '<=' and \ |
| 289 | + isinstance(element_value[index + 1], int) and \ |
| 290 | + event_value > element_value[index + 1]: |
| 291 | + break |
| 292 | + else: |
| 293 | + return True |
| 294 | + |
| 295 | + elif element_key.lower() == 'anything-but': |
| 296 | + if isinstance(element_value, list) and \ |
| 297 | + event_value not in element_value: |
| 298 | + return True |
| 299 | + elif (isinstance(element_value, (str, int))) and \ |
| 300 | + event_value != element_value: |
| 301 | + return True |
| 302 | + elif isinstance(element_value, dict): |
| 303 | + nested_key = list(element_value)[0] |
| 304 | + if nested_key == 'prefix' and \ |
| 305 | + not re.match(r'^{}'.format(element_value.get(nested_key)), event_value): |
| 306 | + return True |
| 307 | + return False |
| 308 | + |
| 309 | + |
| 310 | +def check_valid_numeric_content_base_rule(list_of_operators): |
| 311 | + if len(list_of_operators) > 4: |
| 312 | + return False |
| 313 | + |
| 314 | + if '=' in list_of_operators: |
| 315 | + return False |
| 316 | + |
| 317 | + if len(list_of_operators) > 2: |
| 318 | + upper_limit = None |
| 319 | + lower_limit = None |
| 320 | + for index in range(len(list_of_operators)): |
| 321 | + if not isinstance(list_of_operators[index], int) and \ |
| 322 | + '<' in list_of_operators[index]: |
| 323 | + upper_limit = list_of_operators[index + 1] |
| 324 | + if not isinstance(list_of_operators[index], int) and \ |
| 325 | + '>' in list_of_operators[index]: |
| 326 | + lower_limit = list_of_operators[index + 1] |
| 327 | + if upper_limit and lower_limit and upper_limit < lower_limit: |
| 328 | + return False |
| 329 | + index = index + 1 |
| 330 | + return True |
0 commit comments