Threat objects
Supersedes #204
Breaks out risk objects vs threat objects for the purpose of "what needs to be in every result" vs "what needs to be have a value at least once". Instead of trying to keep track of what's missing, its much easier to keep track of what is definitely not null, adding it to a set as we go, and comparing it to the full set at the end.
@pyth0n1c feel free to test with all sorts of broken YAMLs, I think I have accounted for all of the weird edge cases of potential test failures, but its also possible I missed something between banging my head on the keyboard and wishing for magical datatypes that don't exist.
@cmcginley-splunk likewise, feel free to test. I tried to keep up with the type annotations and formatting elsewhere in this, but its quite likely I've ruined some of the stuff you've added.
Forgot to note, you can use https://github.com/splunk/security_content/pull/2995 as an example PR that currently fails against main but passes with this- the detection detections/cloud/o365_zap_activity_detection.yml in particular exhibits the specific behavior I set out to check against- threat objects that are not present in all risk events, but are present in at least one. I tested we can still fail when something isn't present at all by adding a values(filename) as filename into the search and adjusting the observable file_name to filename- that field isn't in the underlying data so always ends up null.