diff --git a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py index ea755d8c..b981589e 100644 --- a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +++ b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py @@ -89,7 +89,7 @@ class DetectionTestingManagerOutputDto: start_time: Union[datetime.datetime, None] = None replay_index: str = "contentctl_testing_index" replay_host: str = "CONTENTCTL_HOST" - timeout_seconds: int = 60 + timeout_seconds: int = 120 terminate: bool = False diff --git a/contentctl/objects/abstract_security_content_objects/detection_abstract.py b/contentctl/objects/abstract_security_content_objects/detection_abstract.py index 3c5def00..e4ac4a68 100644 --- a/contentctl/objects/abstract_security_content_objects/detection_abstract.py +++ b/contentctl/objects/abstract_security_content_objects/detection_abstract.py @@ -1055,3 +1055,30 @@ def get_summary( # Return the summary return summary_dict + + @model_validator(mode="after") + def validate_data_source_output_fields(self): + # Skip validation for Hunting and Correlation types, or non-production detections + if self.status != DetectionStatus.production or self.type in { + AnalyticsType.Hunting, + AnalyticsType.Correlation, + }: + return self + + # Validate that all required output fields are present in the search + for data_source in self.data_source_objects: + if not data_source.output_fields: + continue + + missing_fields = [ + field for field in data_source.output_fields if field not in self.search + ] + + if missing_fields: + raise ValueError( + f"Data source '{data_source.name}' has output fields " + f"{missing_fields} that are not present in the search " + f"for detection '{self.name}'" + ) + + return self