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