8000 add more cases around anything-but and type matching for str checks · localstack/localstack@f0fcef2 · GitHub
[go: up one dir, main page]

Skip to content

Commit f0fcef2

Browse files
committed
add more cases around anything-but and type matching for str checks
1 parent a85a36b commit f0fcef2

25 files changed

+1002
-194
lines changed

localstack-core/localstack/services/events/event_rule_engine.py

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,24 @@ def _evaluate_condition(self, value, condition, field_exists: bool):
6666
return must_exist == field_exists
6767
elif anything_but := condition.get("anything-but"):
6868
if isinstance(anything_but, dict):
69-
if not_prefix := anything_but.get("prefix"):
70-
return not value.startswith(not_prefix)
71-
elif not_suffix := anything_but.get("suffix"):
72-
return not value.endswith(not_suffix)
73-
elif not_equal_ignore_case := anything_but.get("equals-ignore-case"):
74-
if isinstance(not_equal_ignore_case, str):
75-
return not_equal_ignore_case.lower() != value.lower()
76-
elif isinstance(not_equal_ignore_case, list):
77-
return all(value.lower() != v.lower() for v in not_equal_ignore_case)
69+
if not_condition := anything_but.get("prefix"):
70+
predicate = self._evaluate_prefix
71+
elif not_condition := anything_but.get("suffix"):
72+
predicate = self._evaluate_suffix
73+
elif not_condition := anything_but.get("equals-ignore-case"):
74+
predicate = self._evaluate_equal_ignore_case
75+
elif not_condition := anything_but.get("wildcard"):
76+
predicate = self._evaluate_wildcard
77+
else:
78+
# this should not happen as we validate the EventPattern before
79+
return False
80+
81+
if isinstance(not_condition, str):
82+
return not predicate(not_condition, value)
83+
elif isinstance(not_condition, list):
84+
return all(
85+
not predicate(sub_condition, value) for sub_condition in not_condition
86+
)
7887

7988
elif isinstance(anything_but, list):
8089
return value not in anything_but
@@ -87,19 +96,19 @@ def _evaluate_condition(self, value, condition, field_exists: bool):
8796
elif prefix := condition.get("prefix"):
8897
if isinstance(prefix, dict):
8998
if prefix_equal_ignore_case := prefix.get("equals-ignore-case"):
90-
return value.lower().startswith(prefix_equal_ignore_case.lower())
91-
92-
return value.startswith(prefix)
99+
return self._evaluate_prefix(prefix_equal_ignore_case.lower(), value.lower())
100+
else:
101+
return self._evaluate_prefix(prefix, value)
93102

94103
elif suffix := condition.get("suffix"):
95104
if isinstance(suffix, dict):
96105
if suffix_equal_ignore_case := suffix.get("equals-ignore-case"):
97-
return value.lower().endswith(suffix_equal_ignore_case.lower())
98-
99-
return value.endswith(suffix)
106+
return self._evaluate_suffix(suffix_equal_ignore_case.lower(), value.lower())
107+
else:
108+
return self._evaluate_suffix(suffix, value)
100109

101110
elif equal_ignore_case := condition.get("equals-ignore-case"):
102-
return equal_ignore_case.lower() == value.lower()
111+
return self._evaluate_equal_ignore_case(equal_ignore_case, value)
103112
elif numeric_condition := condition.get("numeric"):
104113
return self._evaluate_numeric_condition(numeric_condition, value)
105114

@@ -108,12 +117,28 @@ def _evaluate_condition(self, value, condition, field_exists: bool):
108117
return value in ips
109118

110119
elif wildcard := condition.get("wildcard"):
111-
return re.match(re.escape(wildcard).replace("\\*", ".+") + "$", value)
120+
return self._evaluate_wildcard(wildcard, value)
112121

113122
return False
114123

115124
@staticmethod
116-
def _evaluate_numeric_condition(conditions, value):
125+
def _evaluate_prefix(condition: str | list, value: str) -> bool:
126+
return value.startswith(condition)
127+
128+
@staticmethod
129+
def _evaluate_suffix(condition: str | list, value: str) -> bool:
130+
return value.endswith(condition)
131+
132+
@staticmethod
133+
def _evaluate_equal_ignore_case(condition: str, value: str) -> bool:
134+
return condition.lower() == value.lower()
135+
136+
@staticmethod
137+
def _evaluate_wildcard(condition: str, value: str) -> bool:
138+
return re.match(re.escape(condition).replace("\\*", ".+") + "$", value)
139+
140+
@staticmethod
141+
def _evaluate_numeric_condition(conditions, value) -> bool:
117142
try:
118143
# try if the value is numeric
119144
value = float(value)
@@ -362,7 +387,18 @@ def _validate_rule(self, rule: t.Any, from_: str | None = None) -> None:
362387
"prefix",
363388
"suffix",
364389
):
365-
if isinstance(value, dict):
390+
if from_ == "anything-but":
391+
if isinstance(value, dict):
392+
raise InvalidEventPatternException(
393+
f"{self.error_prefix}Value of {from_} must be an array or single string/number value."
394+
)
395+
396+
if not self._is_str_or_list_of_str(value):
397+
raise InvalidEventPatternException(
398+
f"{self.error_prefix}prefix/suffix match pattern must be a string"
399+
)
400+
401+
elif isinstance(value, dict):
366402
for inner_operator in value.keys():
367403
if inner_operator != "equals-ignore-case":
368404
raise InvalidEventPatternException(
@@ -377,9 +413,7 @@ def _validate_rule(self, rule: t.Any, from_: str | None = None) -> None:
377413

378414
elif operator == "equals-ignore-case":
379415
if from_ == "anything-but":
380-
if (not isinstance(value, (str, list))) or (
381-
isinstance(value, list) and not all(isinstance(v, str) for v in value)
382-
):
416+
if not self._is_str_or_list_of_str(value):
383417
raise InvalidEventPatternException(
384418
f"{self.error_prefix}Inside {from_}/{operator} list, number|start|null|boolean is not supported."
385419
)
@@ -400,7 +434,12 @@ def _validate_rule(self, rule: t.Any, from_: str | None = None) -> None:
400434
# or have a nested `prefix`, `suffix` or `equals-ignore-case` pattern
401435
elif isinstance(value, dict):
402436
for inner_operator in v E377 alue.keys():
403-
if inner_operator not in ("prefix", "equals-ignore-case", "suffix"):
437+
if inner_operator not in (
438+
"prefix",
439+
"equals-ignore-case",
440+
"suffix",
441+
"wildcard",
442+
):
404443
raise InvalidEventPatternException(
405444
f"{self.error_prefix}Unsupported anything-but pattern: {inner_operator}"
406445
)
@@ -426,7 +465,11 @@ def _validate_rule(self, rule: t.Any, from_: str | None = None) -> None:
426465
f"{self.error_prefix}Malformed CIDR, one '/' required"
427466
)
428467
elif operator == "wildcard":
429-
self._validate_wildcard(value)
468+
if from_ == "anything-but" and isinstance(value, list):
469+
for v in value:
470+
self._validate_wildcard(v)
471+
else:
472+
self._validate_wildcard(value)
430473

431474
else:
432475
raise InvalidEventPatternException(
@@ -512,3 +555,12 @@ def _validate_wildcard(self, value: t.Any):
512555
raise InvalidEventPatternException(
513556
f"{self.error_prefix}Rule is too complex - try using fewer wildcard characters or fewer repeating character sequences after a wildcard character"
514557
)
558+
559+
@staticmethod
560+
def _is_str_or_list_of_str(value: t.Any) -> bool:
561+
if not isinstance(value, (str, list)):
562+
return False
563+
if isinstance(value, list) and not all(isinstance(v, str) for v in value):
564+
return False
565+
566+
return True
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"FileName": "file.txt.bak"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"FileName": [ { "anything-but": { "prefix": { "equals-ignore-case": "file" }} } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"state": "post-init"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"state": [ { "anything-but": { "prefix": 123 } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"state": "post-init"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"state": [ { "anything-but": { "prefix": ["init", "test"] } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"state": "post-init"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"state": [ { "anything-but": { "prefix": ["init", "post"] } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"state": "post-init"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"state": [ { "anything-but": { "prefix": [123, "test"] } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"FileName": "file.txt.bak"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"FileName": [ { "anything-but": { "suffix": { "equals-ignore-case": ".png" }} } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"FileName": "file.txt.bak"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"FileName": [ { "anything-but": { "suffix": 123 } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"FileName": "file.txt.bak"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"FileName": [ { "anything-but": { "suffix": [".txt", ".jpg"] } } ]
17+
}
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Based on https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but
2+
{
3+
"Event": {
4+
"id": "1",
5+
"source": "test-source",
6+
"detail-type": "test-detail-type",
7+
"account": "123456789012",
8+
"region": "us-east-2",
9+
"time": "2022-07-13T13:48:01Z",
10+
"detail": {
11+
"FileName": "file.jpg"
12+
}
13+
},
14+
"EventPattern": {
15+
"detail": {
16+
"FileName": [ { "anything-but": { "suffix": [".txt", ".jpg"] } } ]
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)
0