1
1
import re
2
- from typing import Union , Optional
3
2
4
3
from pydantic import BaseModel , Field , PrivateAttr , field_validator , computed_field
5
4
6
5
from contentctl .objects .errors import ValidationFailed
7
6
from contentctl .objects .detection import Detection
8
7
from contentctl .objects .observable import Observable
9
8
9
+ # TODO (#259): Map our observable types to more than user/system
10
10
# TODO (#247): centralize this mapping w/ usage of SES_OBSERVABLE_TYPE_MAPPING (see
11
11
# observable.py) and the ad hoc mapping made in detection_abstract.py (see the risk property func)
12
12
TYPE_MAP : dict [str , list [str ]] = {
@@ -55,7 +55,7 @@ class RiskEvent(BaseModel):
55
55
search_name : str
56
56
57
57
# The subject of the risk event (e.g. a username, process name, system name, account ID, etc.)
58
- risk_object : Union [ int , str ]
58
+ risk_object : int | str
59
59
60
60
# The type of the risk object (e.g. user, system, or other)
61
61
risk_object_type : str
@@ -83,7 +83,7 @@ class RiskEvent(BaseModel):
83
83
contributing_events_search : str
84
84
85
85
# Private attribute caching the observable this RiskEvent is mapped to
86
- _matched_observable : Optional [ Observable ] = PrivateAttr (default = None )
86
+ _matched_observable : Observable | None = PrivateAttr (default = None )
87
87
88
88
class Config :
89
89
# Allowing fields that aren't explicitly defined to be passed since some of the risk event's
@@ -92,7 +92,7 @@ class Config:
92
92
93
93
@field_validator ("annotations_mitre_attack" , "analyticstories" , mode = "before" )
94
94
@classmethod
95
- def _convert_str_value_to_singleton (cls , v : Union [ str , list [str ] ]) -> list [str ]:
95
+ def _convert_str_value_to_singleton (cls , v : str | list [str ]) -> list [str ]:
96
96
"""
97
97
Given a value, determine if its a list or a single str value; if a single value, return as a
98
98
singleton. Do nothing if anything else.
@@ -272,17 +272,13 @@ def ignore_observable(observable: Observable) -> bool:
272
272
:param observable: the Observable object we are checking the roles of
273
273
:returns: a bool indicating whether this observable should be ignored or not
274
274
"""
275
- # TODO (cmcginley): could there be a case where an observable has both an Attacker and
276
- # Victim (or equivalent) role? If so, how should we handle ignoring it?
277
275
ignore = False
278
276
for role in observable .role :
279
277
if role in IGNORE_ROLES :
280
278
ignore = True
281
279
break
282
280
return ignore
283
281
284
- # TODO: pull field to match against from `contributing_events_search` -> the field they are
285
- # keying off of is the field related to the observable
286
282
def get_matched_observable (self , observables : list [Observable ]) -> Observable :
287
283
"""
288
284
Given a list of observables, return the one this risk event matches
@@ -295,7 +291,7 @@ def get_matched_observable(self, observables: list[Observable]) -> Observable:
295
291
if self ._matched_observable is not None :
296
292
return self ._matched_observable
297
293
298
- matched_observable : Optional [ Observable ] = None
294
+ matched_observable : Observable | None = None
299
295
300
296
# Iterate over the obervables and check for a match
301
297
for observable in observables :
0 commit comments