7
7
from contentctl .objects .rba import risk_object as RiskObject
8
8
9
9
10
- # TODO (cmcginley): the names below now collide a bit with Lou's new class names
11
10
class RiskEvent (BaseModel ):
12
11
"""Model for risk event in ES"""
13
12
14
13
# The search name (e.g. "ESCU - Windows Modify Registry EnableLinkedConnections - Rule")
15
14
search_name : str
16
15
17
16
# The subject of the risk event (e.g. a username, process name, system name, account ID, etc.)
18
- risk_object : int | str
17
+ # (not to be confused w/ the risk object from the detection)
18
+ es_risk_object : int | str
19
19
20
- # The type of the risk object (e.g. user, system, or other)
21
- risk_object_type : str
20
+ # The type of the risk object from ES (e.g. user, system, or other) (not to be confused w/
21
+ # the risk object from the detection)
22
+ es_risk_object_type : str
22
23
23
24
# The level of risk associated w/ the risk event
24
25
risk_score : int
@@ -140,9 +141,6 @@ def validate_analyticstories(self, detection: Detection) -> None:
140
141
f" in detection ({ [x .name for x in detection .tags .analytic_story ]} )."
141
142
)
142
143
143
- # TODO (cmcginley): all of this type checking is a good use case (potentially) for subtyping
144
- # detections by detection type, instead of having types as enums; could have an EBD subtype
145
- # for any detections that should produce risk so that rba is never None
146
144
def validate_risk_message (self , detection : Detection ) -> None :
147
145
"""
148
146
Given the associated detection, validate the risk event's message
@@ -160,7 +158,7 @@ def validate_risk_message(self, detection: Detection) -> None:
160
158
field_replacement_pattern = re .compile (r"\$\S+\$" )
161
159
tokens = field_replacement_pattern .findall (detection .rba .message )
162
160
163
- # TODO (cmcginley ): could expand this to get the field values from the raw events and check
161
+ # TODO (#346 ): could expand this to get the field values from the raw events and check
164
162
# to see that allexpected strings ARE in the risk message (as opposed to checking only
165
163
# that unexpected strings aren't)
166
164
# Check for the presence of each token in the message from the risk event
@@ -210,12 +208,12 @@ def validate_risk_against_risk_objects(self, risk_objects: set[RiskObject]) -> N
210
208
211
209
# The risk object type from the risk event should match our mapping of internal risk object
212
210
# types
213
- if self .risk_object_type != matched_risk_object .type .value :
211
+ if self .es_risk_object_type != matched_risk_object .type .value :
214
212
raise ValidationFailed (
215
- f"The risk object type from the risk event ({ self .risk_object_type } ) does not match"
213
+ f"The risk object type from the risk event ({ self .es_risk_object_type } ) does not match"
216
214
" the expected type based on the matched risk object "
217
- f"({ matched_risk_object .type .value } ): risk event=(object={ self .risk_object } , "
218
- f"type={ self .risk_object_type } , source_field_name={ self .source_field_name } ), "
215
+ f"({ matched_risk_object .type .value } ): risk event=(object={ self .es_risk_object } , "
216
+ f"type={ self .es_risk_object_type } , source_field_name={ self .source_field_name } ), "
219
217
f"risk object=(name={ matched_risk_object .field } , "
220
218
f"type={ matched_risk_object .type .value } )"
221
219
)
@@ -252,30 +250,23 @@ def get_matched_risk_object(self, risk_objects: set[RiskObject]) -> RiskObject:
252
250
253
251
# Try to match the risk_object against a specific risk object
254
252
if self .source_field_name == risk_object .field :
255
- # TODO (cmcginley): this should be enforced as part of build validation
253
+ # TODO (#347): enforce that field names are not repeated across risk objects as
254
+ # part of build/validate
256
255
if matched_risk_object is not None :
257
256
raise ValueError (
258
257
"Unexpected conditon: we don't expect multiple risk objects to use the "
259
258
"same field name, so we should not be able match the risk event to "
260
259
"multiple risk objects."
261
260
)
262
261
263
- # TODO (cmcginley): risk objects and threat objects are now in separate sets,
264
- # so I think we can safely eliminate this check entirely?
265
- # # Report any risk events we find that shouldn't be there
266
- # if RiskEvent.ignore_observable(observable):
267
- # raise ValidationFailed(
268
- # "Risk event matched an observable with an invalid role: "
269
- # f"(name={observable.name}, type={observable.type}, role={observable.role})")
270
-
271
262
# NOTE: we explicitly do not break early as we want to check each risk object
272
263
matched_risk_object = risk_object
273
264
274
265
# Ensure we were able to match the risk event to a specific risk object
275
266
if matched_risk_object is None :
276
267
raise ValidationFailed (
277
- f"Unable to match risk event (object={ self .risk_object } , type="
278
- f"{ self .risk_object_type } , source_field_name={ self .source_field_name } ) to a "
268
+ f"Unable to match risk event (object={ self .es_risk_object } , type="
269
+ f"{ self .es_risk_object_type } , source_field_name={ self .source_field_name } ) to a "
279
270
"risk object in the detection; please check for errors in the risk object types for this "
280
271
"detection, as well as the risk event build process in contentctl (e.g. threat "
281
272
"objects aren't being converted to risk objects somehow)."
0 commit comments