From d93865ab0b2422417e260f915c6efb19d0cc7382 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 21 Apr 2025 12:54:19 -0700 Subject: [PATCH 1/3] Require at least 1 analytic story per detection. Significant cleanup of the savedstories_detections.j2 template to make other changes easier. The changes in this template still generate an identical savedsearches.conf file before the changes. --- contentctl/objects/detection_tags.py | 2 +- .../templates/savedsearches_detections.j2 | 28 +------------------ 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/contentctl/objects/detection_tags.py b/contentctl/objects/detection_tags.py index c723e5c8..f37514ee 100644 --- a/contentctl/objects/detection_tags.py +++ b/contentctl/objects/detection_tags.py @@ -43,7 +43,7 @@ class DetectionTags(BaseModel): # detection spec model_config = ConfigDict(validate_default=False, extra="forbid") - analytic_story: list[Story] = Field(...) + analytic_story: list[Story] = Field(min_length=1) asset_type: AssetType = Field(...) group: list[str] = [] diff --git a/contentctl/output/templates/savedsearches_detections.j2 b/contentctl/output/templates/savedsearches_detections.j2 index 322b3036..6cc55a5f 100644 --- a/contentctl/output/templates/savedsearches_detections.j2 +++ b/contentctl/output/templates/savedsearches_detections.j2 @@ -1,7 +1,6 @@ ### {{app.label}} DETECTIONS ### {% for detection in objects %} -{% if (detection.type == 'TTP' or detection.type == 'Anomaly' or detection.type == 'Hunting' or detection.type == 'Correlation') %} [{{ detection.get_conf_stanza_name(app) }}] action.escu = 0 action.escu.enabled = 1 @@ -9,23 +8,13 @@ description = {{ detection.status_aware_description | escapeNewlines() }} action.escu.mappings = {{ detection.mappings | tojson }} action.escu.data_models = {{ detection.datamodel | tojson }} action.escu.eli5 = {{ detection.status_aware_description | escapeNewlines() }} -{% if detection.how_to_implement %} action.escu.how_to_implement = {{ detection.how_to_implement | escapeNewlines() }} -{% else %} -action.escu.how_to_implement = none -{% endif %} -{% if detection.known_false_positives %} action.escu.known_false_positives = {{ detection.known_false_positives | escapeNewlines() }} -{% else %} -action.escu.known_false_positives = None -{% endif %} action.escu.creation_date = {{ detection.date }} action.escu.modification_date = {{ detection.date }} action.escu.confidence = high action.escu.search_type = detection -{% if detection.tags.product is defined %} action.escu.product = {{ detection.tags.product | tojson }} -{% endif %} {% if detection.tags.atomic_guid %} action.escu.atomic_red_team_guids = {{ detection.tags.getAtomicGuidStringArray() | tojson }} {% endif %} @@ -34,7 +23,6 @@ action.escu.providing_technologies = {{ detection.providing_technologies | tojso {% else %} action.escu.providing_technologies = null {% endif %} -{% if detection.tags.analytic_story %} action.escu.analytic_story = {{ objectListToNameList(detection.tags.analytic_story) | tojson }} {% if detection.deployment.alert_action.rba.enabled%} action.risk = 1 @@ -43,9 +31,6 @@ action.risk.param._risk = {{ detection.risk | tojson }} action.risk.param._risk_score = 0 action.risk.param.verbose = 0 {% endif %} -{% else %} -action.escu.analytic_story = [] -{% endif %} cron_schedule = {{ detection.deployment.scheduling.cron_schedule }} dispatch.earliest_time = {{ detection.deployment.scheduling.earliest_time }} dispatch.latest_time = {{ detection.deployment.scheduling.latest_time }} @@ -53,15 +38,10 @@ action.correlationsearch.enabled = 1 action.correlationsearch.label = {{ detection.get_action_dot_correlationsearch_dot_label(app) }} action.correlationsearch.annotations = {{ detection.annotations | tojson }} action.correlationsearch.metadata = {{ detection.metadata | tojson }} -{% if detection.deployment.scheduling.schedule_window is defined %} schedule_window = {{ detection.deployment.scheduling.schedule_window }} -{% endif %} -{% if detection.deployment is defined %} {% if detection.deployment.alert_action.notable %} action.notable = 1 -{% if detection.nes_fields %} action.notable.param.nes_fields = {{ detection.nes_fields }} -{% endif %} action.notable.param.rule_description = {{ detection.deployment.alert_action.notable.rule_description | custom_jinja2_enrichment_filter(detection) | escapeNewlines()}} action.notable.param.rule_title = {% if detection.type | lower == "correlation" %}RBA: {{ detection.deployment.alert_action.notable.rule_title | custom_jinja2_enrichment_filter(detection) }}{% else %}{{ detection.deployment.alert_action.notable.rule_title | custom_jinja2_enrichment_filter(detection) }}{% endif +%} action.notable.param.security_domain = {{ detection.tags.security_domain }} @@ -87,13 +67,8 @@ action.sendtophantom.param.phantom_server = {{ detection.deployment.alert_action action.sendtophantom.param.sensitivity = {{ detection.deployment.alert_action.phantom.sensitivity | custom_jinja2_enrichment_filter(detection) }} action.sendtophantom.param.severity = {{ detection.deployment.alert_action.phantom.severity | custom_jinja2_enrichment_filter(detection) }} {% endif %} -{% endif %} alert.digest_mode = 1 -{% if detection.enabled_by_default %} -disabled = false -{% else %} -disabled = true -{% endif %} +disabled = {{ (not detection.enabled_by_default) | lower }} enableSched = 1 allow_skew = 100% counttype = number of events @@ -108,7 +83,6 @@ alert.suppress.period = {{ detection.tags.throttling.period }} {% endif %} search = {{ detection.search | escapeNewlines() }} action.notable.param.drilldown_searches = {{ detection.drilldowns_in_JSON | tojson | escapeNewlines() }} -{% endif %} {% endfor %} ### END {{ app.label }} DETECTIONS ### From ac0d920b2f0f84b93b37fe91cf94ac0e46fff8d5 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 22 Apr 2025 11:17:49 -0700 Subject: [PATCH 2/3] Add appropriate updated fields to savedsearches.conf --- contentctl/output/templates/savedsearches_detections.j2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contentctl/output/templates/savedsearches_detections.j2 b/contentctl/output/templates/savedsearches_detections.j2 index 6cc55a5f..12ed2e84 100644 --- a/contentctl/output/templates/savedsearches_detections.j2 +++ b/contentctl/output/templates/savedsearches_detections.j2 @@ -35,12 +35,14 @@ cron_schedule = {{ detection.deployment.scheduling.cron_schedule }} dispatch.earliest_time = {{ detection.deployment.scheduling.earliest_time }} dispatch.latest_time = {{ detection.deployment.scheduling.latest_time }} action.correlationsearch.enabled = 1 +action.correlationsearch.detection_type = ebd action.correlationsearch.label = {{ detection.get_action_dot_correlationsearch_dot_label(app) }} action.correlationsearch.annotations = {{ detection.annotations | tojson }} action.correlationsearch.metadata = {{ detection.metadata | tojson }} schedule_window = {{ detection.deployment.scheduling.schedule_window }} {% if detection.deployment.alert_action.notable %} action.notable = 1 +action.notable.param._entities = [{"risk_object_field": "N/A", "risk_object_type": "N/A", "risk_score": "0"}] action.notable.param.nes_fields = {{ detection.nes_fields }} action.notable.param.rule_description = {{ detection.deployment.alert_action.notable.rule_description | custom_jinja2_enrichment_filter(detection) | escapeNewlines()}} action.notable.param.rule_title = {% if detection.type | lower == "correlation" %}RBA: {{ detection.deployment.alert_action.notable.rule_title | custom_jinja2_enrichment_filter(detection) }}{% else %}{{ detection.deployment.alert_action.notable.rule_title | custom_jinja2_enrichment_filter(detection) }}{% endif +%} From 9a475a51b1dd73e43dbfed608c34e48d4916f724 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 1 May 2025 10:24:34 -0700 Subject: [PATCH 3/3] don't set a min lenght for stories --- contentctl/objects/detection_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contentctl/objects/detection_tags.py b/contentctl/objects/detection_tags.py index f37514ee..c723e5c8 100644 --- a/contentctl/objects/detection_tags.py +++ b/contentctl/objects/detection_tags.py @@ -43,7 +43,7 @@ class DetectionTags(BaseModel): # detection spec model_config = ConfigDict(validate_default=False, extra="forbid") - analytic_story: list[Story] = Field(min_length=1) + analytic_story: list[Story] = Field(...) asset_type: AssetType = Field(...) group: list[str] = []