@@ -1060,15 +1060,20 @@ def retry_search_until_timeout(
1060
1060
results = JSONResultsReader (job .results (output_mode = "json" ))
1061
1061
1062
1062
# Consolidate a set of the distinct observable field names
1063
- observable_fields_set = set ([o .name for o in detection .tags .observable ])
1063
+ observable_fields_set = set ([o .name for o in detection .tags .observable ]) # keeping this around for later
1064
+ risk_object_fields_set = set ([o .name for o in detection .tags .observable if "Victim" in o .role ]) # just the "Risk Objects"
1065
+ threat_object_fields_set = set ([o .name for o in detection .tags .observable if "Attacker" in o .role ]) # just the "threat objects"
1064
1066
1065
1067
# Ensure the search had at least one result
1066
1068
if int (job .content .get ("resultCount" , "0" )) > 0 :
1067
1069
# Initialize the test result
1068
1070
test .result = UnitTestResult ()
1069
1071
1070
1072
# Initialize the collection of fields that are empty that shouldn't be
1073
+ present_threat_objects : set [str ] = set ()
1071
1074
empty_fields : set [str ] = set ()
1075
+
1076
+
1072
1077
1073
1078
# Filter out any messages in the results
1074
1079
for result in results :
@@ -1077,30 +1082,50 @@ def retry_search_until_timeout(
1077
1082
1078
1083
# If not a message, it is a dict and we will process it
1079
1084
results_fields_set = set (result .keys ())
1085
+ # Guard against first events (relevant later)
1080
1086
1081
- # Identify any observable fields that are not available in the results
1082
- missing_fields = observable_fields_set - results_fields_set
1083
- if len (missing_fields ) > 0 :
1087
+ # Identify any risk object fields that are not available in the results
1088
+ missing_risk_objects = risk_object_fields_set - results_fields_set
1089
+ if len (missing_risk_objects ) > 0 :
1084
1090
# Report a failure in such cases
1085
- e = Exception (f"The observable field(s) { missing_fields } are missing in the detection results" )
1091
+ e = Exception (f"The observable field(s) { missing_risk_objects } are missing in the detection results" )
1086
1092
test .result .set_job_content (
1087
1093
job .content ,
1088
1094
self .infrastructure ,
1089
- TestResultStatus .ERROR ,
1095
+ TestResultStatus .FAIL ,
1090
1096
exception = e ,
1091
1097
duration = time .time () - search_start_time ,
1092
1098
)
1093
1099
1094
- return
1100
+ return
1095
1101
1096
- # If we find one or more fields that contain the string "null" then they were
1102
+ # If we find one or more risk object fields that contain the string "null" then they were
1097
1103
# not populated and we should throw an error. This can happen if there is a typo
1098
1104
# on a field. In this case, the field will appear but will not contain any values
1099
- current_empty_fields = set ()
1105
+ current_empty_fields : set [str ] = set ()
1106
+
1100
1107
for field in observable_fields_set :
1101
1108
if result .get (field , 'null' ) == 'null' :
1102
- current_empty_fields .add (field )
1103
-
1109
+ if field in risk_object_fields_set :
1110
+ e = Exception (f"The risk object field { field } is missing in at least one result." )
1111
+ test .result .set_job_content (
1112
+ job .content ,
1113
+ self .infrastructure ,
1114
+ TestResultStatus .FAIL ,
1115
+ exception = e ,
1116
+ duration = time .time () - search_start_time ,
1117
+ )
1118
+ return
1119
+ else :
1120
+ if field in threat_object_fields_set :
1121
+ current_empty_fields .add (field )
1122
+ else :
1123
+ if field in threat_object_fields_set :
1124
+ present_threat_objects .add (field )
1125
+ continue
1126
+
1127
+
1128
+
1104
1129
# If everything succeeded up until now, and no empty fields are found in the
1105
1130
# current result, then the search was a success
1106
1131
if len (current_empty_fields ) == 0 :
@@ -1114,21 +1139,32 @@ def retry_search_until_timeout(
1114
1139
1115
1140
else :
1116
1141
empty_fields = empty_fields .union (current_empty_fields )
1117
-
1118
- # Report a failure if there were empty fields in all results
1119
- e = Exception (
1120
- f"One or more required observable fields { empty_fields } contained 'null' values. Is the "
1121
- "data being parsed correctly or is there an error in the naming of a field?"
1142
+
1143
+
1144
+ missing_threat_objects = threat_object_fields_set - present_threat_objects
1145
+ # Report a failure if there were empty fields in a threat object in all results
1146
+ if len (missing_threat_objects ) > 0 :
1147
+ e = Exception (
1148
+ f"One or more required threat object fields { missing_threat_objects } contained 'null' values in all events. "
1149
+ "Is the data being parsed correctly or is there an error in the naming of a field?"
1122
1150
)
1123
- test .result .set_job_content (
1124
- job .content ,
1125
- self .infrastructure ,
1126
- TestResultStatus .ERROR ,
1127
- exception = e ,
1128
- duration = time .time () - search_start_time ,
1129
- )
1151
+ test .result .set_job_content (
1152
+ job .content ,
1153
+ self .infrastructure ,
1154
+ TestResultStatus .FAIL ,
1155
+ exception = e ,
1156
+ duration = time .time () - search_start_time ,
1157
+ )
1158
+ return
1159
+
1130
1160
1131
- return
1161
+ test .result .set_job_content (
1162
+ job .content ,
1163
+ self .infrastructure ,
1164
+ TestResultStatus .PASS ,
1165
+ duration = time .time () - search_start_time ,
1166
+ )
1167
+ return
1132
1168
1133
1169
else :
1134
1170
# Report a failure if there were no results at all
0 commit comments