Skip to content

Commit a92a9ed

Browse files
committed
Updates to get the names
of Detections, Baselines, and Investigations/Response Tasks Properly written to the conf files.
1 parent 59ea4f0 commit a92a9ed

File tree

9 files changed

+52
-36
lines changed

9 files changed

+52
-36
lines changed

contentctl/objects/abstract_security_content_objects/detection_abstract.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
from contentctl.objects.constants import (
4444
ES_MAX_STANZA_LENGTH,
4545
ES_SEARCH_STANZA_NAME_FORMAT_AFTER_CLONING_IN_PRODUCT_TEMPLATE,
46-
CONTENTCTL_MAX_SEARCH_NAME_LENGTH
46+
CONTENTCTL_MAX_SEARCH_NAME_LENGTH,
47+
CONTENTCTL_DETECTION_STANZA_NAME_FORMAT_TEMPLATE
4748
)
4849

4950
MISSING_SOURCES: set[str] = set()
@@ -80,6 +81,12 @@ class Detection_Abstract(SecurityContentObject):
8081

8182
data_source_objects: list[DataSource] = []
8283

84+
def get_conf_stanza_name(self, app:CustomApp)->str:
85+
stanza_name = CONTENTCTL_DETECTION_STANZA_NAME_FORMAT_TEMPLATE.format(app_label=app.label, detection_name=self.name)
86+
self.check_conf_stanza_max_length(stanza_name)
87+
return stanza_name
88+
89+
8390
def get_action_dot_correlationsearch_dot_label(self, app:CustomApp, max_stanza_length:int=ES_MAX_STANZA_LENGTH)->str:
8491
stanza_name = self.get_conf_stanza_name(app)
8592
stanza_name_after_saving_in_es = ES_SEARCH_STANZA_NAME_FORMAT_AFTER_CLONING_IN_PRODUCT_TEMPLATE.format(

contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from contentctl.objects.config import CustomApp
99

1010
from contentctl.objects.enums import AnalyticsType
11-
from contentctl.objects.constants import CONTENTCTL_MAX_STANZA_LENGTH, CONTENTCTL_STANZA_NAME_FORMAT_TEMPLATE
11+
from contentctl.objects.constants import CONTENTCTL_MAX_STANZA_LENGTH
1212
import abc
1313
import uuid
1414
import datetime
@@ -59,12 +59,11 @@ def serialize_model(self):
5959
"references": [str(url) for url in self.references or []]
6060
}
6161

62-
def get_conf_stanza_name(self, app:CustomApp, max_stanza_length:int=CONTENTCTL_MAX_STANZA_LENGTH)->str:
63-
stanza_name = CONTENTCTL_STANZA_NAME_FORMAT_TEMPLATE.format(app_label=app.label, detection_name=self.name)
62+
63+
def check_conf_stanza_max_length(self, stanza_name:str, max_stanza_length:int=CONTENTCTL_MAX_STANZA_LENGTH) -> None:
6464
if len(stanza_name) > max_stanza_length:
6565
raise ValueError(f"conf stanza may only be {max_stanza_length} characters, "
6666
f"but stanza was actually {len(stanza_name)} characters: '{stanza_name}' ")
67-
return stanza_name
6867

6968
@staticmethod
7069
def objectListToNameList(objects: list[SecurityContentObject]) -> list[str]:

contentctl/objects/baseline.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
from contentctl.objects.enums import DataModel
88
from contentctl.objects.baseline_tags import BaselineTags
99

10-
from contentctl.objects.constants import CONTENTCTL_MAX_SEARCH_NAME_LENGTH
10+
from contentctl.objects.config import CustomApp
11+
12+
13+
from contentctl.objects.constants import CONTENTCTL_MAX_SEARCH_NAME_LENGTH,CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE
1114

1215
class Baseline(SecurityContentObject):
1316
name:str = Field(...,max_length=CONTENTCTL_MAX_SEARCH_NAME_LENGTH)
@@ -22,6 +25,11 @@ class Baseline(SecurityContentObject):
2225
deployment: Deployment = Field({})
2326

2427

28+
def get_conf_stanza_name(self, app:CustomApp)->str:
29+
stanza_name = CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE.format(app_label=app.label, detection_name=self.name)
30+
self.check_conf_stanza_max_length(stanza_name)
31+
return stanza_name
32+
2533
@field_validator("deployment", mode="before")
2634
def getDeployment(cls, v:Any, info:ValidationInfo)->Deployment:
2735
return Deployment.getDeployment(v,info)

contentctl/objects/constants.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,11 @@
162162
# Endpoint - ESCU - Name of Search From YML File - Rule - Rule
163163
# The math below accounts for all these caveats
164164
ES_MAX_STANZA_LENGTH = 99
165-
CONTENTCTL_STANZA_NAME_FORMAT_TEMPLATE = "{app_label} - {detection_name} - Rule"
165+
CONTENTCTL_DETECTION_STANZA_NAME_FORMAT_TEMPLATE = "{app_label} - {detection_name} - Rule"
166+
CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE = "{app_label} - {detection_name}"
167+
CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE = "{app_label} - {detection_name} - Response Task"
168+
166169
ES_SEARCH_STANZA_NAME_FORMAT_AFTER_CLONING_IN_PRODUCT_TEMPLATE = "{security_domain_value} - {search_name} - Rule"
167170
SECURITY_DOMAIN_MAX_LENGTH = max([len(SecurityDomain[value]) for value in SecurityDomain._member_map_])
168171
CONTENTCTL_MAX_STANZA_LENGTH = ES_MAX_STANZA_LENGTH - len(ES_SEARCH_STANZA_NAME_FORMAT_AFTER_CLONING_IN_PRODUCT_TEMPLATE.format(security_domain_value="X"*SECURITY_DOMAIN_MAX_LENGTH,search_name=""))
169-
CONTENTCTL_MAX_SEARCH_NAME_LENGTH = CONTENTCTL_MAX_STANZA_LENGTH - len(CONTENTCTL_STANZA_NAME_FORMAT_TEMPLATE.format(app_label="ESCU", detection_name=""))
172+
CONTENTCTL_MAX_SEARCH_NAME_LENGTH = CONTENTCTL_MAX_STANZA_LENGTH - len(CONTENTCTL_DETECTION_STANZA_NAME_FORMAT_TEMPLATE.format(app_label="ESCU", detection_name=""))

contentctl/objects/investigation.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
from contentctl.objects.security_content_object import SecurityContentObject
66
from contentctl.objects.enums import DataModel
77
from contentctl.objects.investigation_tags import InvestigationTags
8-
9-
from contentctl.objects.constants import CONTENTCTL_MAX_SEARCH_NAME_LENGTH
8+
from contentctl.objects.constants import (
9+
CONTENTCTL_MAX_SEARCH_NAME_LENGTH,
10+
CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE,
11+
CONTENTCTL_MAX_STANZA_LENGTH
12+
)
13+
from contentctl.objects.config import CustomApp
1014

1115
# TODO (#266): disable the use_enum_values configuration
1216
class Investigation(SecurityContentObject):
@@ -39,6 +43,16 @@ def inputs(self)->List[str]:
3943
def lowercase_name(self)->str:
4044
return self.name.replace(' ', '_').replace('-','_').replace('.','_').replace('/','_').lower().replace(' ', '_').replace('-','_').replace('.','_').replace('/','_').lower()
4145

46+
47+
# This is a slightly modified version of the get_conf_stanza_name function from
48+
# SecurityContentObject_Abstract
49+
def get_response_task_name(self, app:CustomApp, max_stanza_length:int=CONTENTCTL_MAX_STANZA_LENGTH)->str:
50+
stanza_name = CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE.format(app_label=app.label, detection_name=self.name)
51+
if len(stanza_name) > max_stanza_length:
52+
raise ValueError(f"conf stanza may only be {max_stanza_length} characters, "
53+
f"but stanza was actually {len(stanza_name)} characters: '{stanza_name}' ")
54+
return stanza_name
55+
4256

4357
@model_serializer
4458
def serialize_model(self):
@@ -69,6 +83,3 @@ def model_post_init(self, ctx:dict[str,Any]):
6983
# back to itself
7084
for story in self.tags.analytic_story:
7185
story.investigations.append(self)
72-
73-
74-

contentctl/objects/story.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,14 @@
88
from contentctl.objects.investigation import Investigation
99
from contentctl.objects.baseline import Baseline
1010
from contentctl.objects.data_source import DataSource
11+
from contentctl.objects.config import CustomApp
1112

1213
from contentctl.objects.security_content_object import SecurityContentObject
1314

14-
15-
16-
17-
18-
#from contentctl.objects.investigation import Investigation
19-
20-
21-
2215
class Story(SecurityContentObject):
2316
narrative: str = Field(...)
2417
tags: StoryTags = Field(...)
2518

26-
# enrichments
27-
#detection_names: List[str] = []
28-
#investigation_names: List[str] = []
29-
#baseline_names: List[str] = []
30-
3119
# These are updated when detection and investigation objects are created.
3220
# Specifically in the model_post_init functions
3321
detections:List[Detection] = []
@@ -46,9 +34,9 @@ def data_sources(self)-> list[DataSource]:
4634
return sorted(list(data_source_objects))
4735

4836

49-
def storyAndInvestigationNamesWithApp(self, app_name:str)->List[str]:
50-
return [f"{app_name} - {name} - Rule" for name in self.detection_names] + \
51-
[f"{app_name} - {name} - Response Task" for name in self.investigation_names]
37+
def storyAndInvestigationNamesWithApp(self, app:CustomApp)->List[str]:
38+
return [detection.get_conf_stanza_name(app) for detection in self.detections] + \
39+
[investigation.get_response_task_name(app) for investigation in self.investigations]
5240

5341
@model_serializer
5442
def serialize_model(self):

contentctl/output/templates/analyticstories_investigations.j2

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11

22
### RESPONSE TASKS ###
33

4-
{% for detection in objects %}
5-
{% if (detection.type == 'Investigation') %}
6-
[savedsearch://{{ detection.get_conf_stanza_name(app) }}]
4+
{% for investigation in objects %}
5+
{% if (investigation.type == 'Investigation') %}
6+
[savedsearch://{{ investigation.get_response_task_name(app) }}]
77
type = investigation
88
explanation = none
9-
{% if detection.how_to_implement is defined %}
10-
how_to_implement = {{ detection.how_to_implement | escapeNewlines() }}
9+
{% if investigation.how_to_implement is defined %}
10+
how_to_implement = {{ investigation.how_to_implement | escapeNewlines() }}
1111
{% else %}
1212
how_to_implement = none
1313
{% endif %}

contentctl/output/templates/analyticstories_stories.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ version = {{ story.version }}
1010
references = {{ story.getReferencesListForJson() | tojson }}
1111
maintainers = [{"company": "{{ story.author_company }}", "email": "{{ story.author_email }}", "name": "{{ story.author_name }}"}]
1212
spec_version = 3
13-
searches = {{ story.storyAndInvestigationNamesWithApp(app.label) | tojson }}
13+
searches = {{ story.storyAndInvestigationNamesWithApp(app) | tojson }}
1414
description = {{ story.description | escapeNewlines() }}
1515
{% if story.narrative is defined %}
1616
narrative = {{ story.narrative | escapeNewlines() }}

contentctl/output/templates/savedsearches_investigations.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{% for detection in objects %}
66
{% if (detection.type == 'Investigation') %}
77
{% if detection.search is defined %}
8-
[{{ detection.get_conf_stanza_name(app) }}]
8+
[{{ detection.get_response_task_name(app) }}]
99
action.escu = 0
1010
action.escu.enabled = 1
1111
action.escu.search_type = investigative

0 commit comments

Comments
 (0)