Skip to content

Commit f85ccbd

Browse files
authored
Merge pull request #213 from splunk/fix_name_length
Fix name length
2 parents f8cbe3f + da18ef7 commit f85ccbd

12 files changed

+98
-33
lines changed

contentctl/objects/abstract_security_content_objects/detection_abstract.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
if TYPE_CHECKING:
2121
from contentctl.input.director import DirectorOutputDto
2222
from contentctl.objects.baseline import Baseline
23-
23+
from contentctl.objects.config import CustomApp
24+
2425
from contentctl.objects.security_content_object import SecurityContentObject
2526
from contentctl.objects.enums import AnalyticsType
2627
from contentctl.objects.enums import DataModel
@@ -36,7 +37,6 @@
3637
from contentctl.objects.data_source import DataSource
3738
from contentctl.objects.base_test_result import TestResultStatus
3839

39-
# from contentctl.objects.playbook import Playbook
4040
from contentctl.objects.enums import ProvidingTechnology
4141
from contentctl.enrichments.cve_enrichment import CveEnrichmentObj
4242
import datetime
@@ -51,8 +51,8 @@
5151
# TODO (#266): disable the use_enum_values configuration
5252
class Detection_Abstract(SecurityContentObject):
5353
model_config = ConfigDict(use_enum_values=True)
54-
55-
# contentType: SecurityContentType = SecurityContentType.detections
54+
name:str = Field(...,max_length=67)
55+
#contentType: SecurityContentType = SecurityContentType.detections
5656
type: AnalyticsType = Field(...)
5757
status: DetectionStatus = Field(...)
5858
data_source: list[str] = []
@@ -70,10 +70,31 @@ class Detection_Abstract(SecurityContentObject):
7070
# https://github.com/pydantic/pydantic/issues/9101#issuecomment-2019032541
7171
tests: List[Annotated[Union[UnitTest, IntegrationTest, ManualTest], Field(union_mode='left_to_right')]] = []
7272
# A list of groups of tests, relying on the same data
73-
test_groups: Union[list[TestGroup], None] = Field(None, validate_default=True)
73+
test_groups: list[TestGroup] = []
7474

7575
data_source_objects: list[DataSource] = []
7676

77+
def get_action_dot_correlationsearch_dot_label(self, app:CustomApp, max_stanza_length:int=99)->str:
78+
label = self.get_conf_stanza_name(app)
79+
label_after_saving_in_product = f"{self.tags.security_domain.value} - {label} - Rule"
80+
81+
if len(label_after_saving_in_product) > max_stanza_length:
82+
raise ValueError(f"label may only be {max_stanza_length} characters to allow updating in-product, "
83+
f"but stanza was actually {len(label_after_saving_in_product)} characters: '{label_after_saving_in_product}' ")
84+
85+
return label
86+
87+
def get_conf_stanza_name(self, app:CustomApp, max_stanza_length:int=81)->str:
88+
stanza_name = f"{app.label} - {self.name} - Rule"
89+
if len(stanza_name) > max_stanza_length:
90+
raise ValueError(f"conf stanza may only be {max_stanza_length} characters, "
91+
f"but stanza was actually {len(stanza_name)} characters: '{stanza_name}' ")
92+
#print(f"Stanza Length[{len(stanza_name)}]")
93+
return stanza_name
94+
95+
96+
97+
7798
@field_validator("search", mode="before")
7899
@classmethod
79100
def validate_presence_of_filter_macro(cls, value:str, info:ValidationInfo)->str:
@@ -515,7 +536,7 @@ def model_post_init(self, __context: Any) -> None:
515536
self.data_source_objects = matched_data_sources
516537

517538
for story in self.tags.analytic_story:
518-
story.detections.append(self)
539+
story.detections.append(self)
519540

520541
self.cve_enrichment_func(__context)
521542

contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131

3232
# TODO (#266): disable the use_enum_values configuration
3333
class SecurityContentObject_Abstract(BaseModel, abc.ABC):
34-
model_config = ConfigDict(use_enum_values=True, validate_default=True)
35-
36-
name: str = Field(...)
37-
author: str = Field("Content Author", max_length=255)
38-
date: datetime.date = Field(datetime.date.today())
39-
version: NonNegativeInt = 1
40-
id: uuid.UUID = Field(default_factory=uuid.uuid4) # we set a default here until all content has a uuid
41-
description: str = Field("Enter Description Here", max_length=10000)
34+
model_config = ConfigDict(use_enum_values=True,validate_default=True)
35+
36+
name: str = Field(...,max_length=99)
37+
author: str = Field(...,max_length=255)
38+
date: datetime.date = Field(...)
39+
version: NonNegativeInt = Field(...)
40+
id: uuid.UUID = Field(...) #we set a default here until all content has a uuid
41+
description: str = Field(...,max_length=10000)
4242
file_path: Optional[FilePath] = None
4343
references: Optional[List[HttpUrl]] = None
4444

contentctl/objects/baseline.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from contentctl.objects.security_content_object import SecurityContentObject
77
from contentctl.objects.enums import DataModel
88
from contentctl.objects.baseline_tags import BaselineTags
9+
from contentctl.objects.config import CustomApp
910
#from contentctl.objects.deployment import Deployment
1011

1112
# from typing import TYPE_CHECKING
@@ -14,6 +15,7 @@
1415

1516

1617
class Baseline(SecurityContentObject):
18+
name:str = Field(...,max_length=67)
1719
type: Annotated[str,Field(pattern="^Baseline$")] = Field(...)
1820
datamodel: Optional[List[DataModel]] = None
1921
search: str = Field(..., min_length=4)
@@ -23,6 +25,15 @@ class Baseline(SecurityContentObject):
2325

2426
# enrichment
2527
deployment: Deployment = Field({})
28+
29+
def get_conf_stanza_name(self, app:CustomApp, max_stanza_length:int=81)->str:
30+
stanza_name = f"{app.label} - {self.name}"
31+
if len(stanza_name) > max_stanza_length:
32+
raise ValueError(f"conf stanza may only be {max_stanza_length} characters, "
33+
f"but stanza was actually {len(stanza_name)} characters: '{stanza_name}' ")
34+
#print(f"Stanza Length[{len(stanza_name)}]")
35+
return stanza_name
36+
2637

2738
@field_validator("deployment", mode="before")
2839
def getDeployment(cls, v:Any, info:ValidationInfo)->Deployment:

contentctl/objects/deployment.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from __future__ import annotations
2-
from pydantic import Field, computed_field, model_validator,ValidationInfo, model_serializer
3-
from typing import Optional,Any
4-
2+
from pydantic import Field, computed_field,ValidationInfo, model_serializer, NonNegativeInt
3+
from typing import Any
4+
import uuid
5+
import datetime
56
from contentctl.objects.security_content_object import SecurityContentObject
67
from contentctl.objects.deployment_scheduling import DeploymentScheduling
78
from contentctl.objects.alert_action import AlertAction
@@ -15,17 +16,22 @@ class Deployment(SecurityContentObject):
1516
#author: str = None
1617
#description: str = None
1718
#contentType: SecurityContentType = SecurityContentType.deployments
19+
20+
1821
scheduling: DeploymentScheduling = Field(...)
1922
alert_action: AlertAction = AlertAction()
2023
type: DeploymentType = Field(...)
24+
author: str = Field(...,max_length=255)
25+
version: NonNegativeInt = 1
2126

2227
#Type was the only tag exposed and should likely be removed/refactored.
2328
#For transitional reasons, provide this as a computed_field in prep for removal
2429
@computed_field
2530
@property
2631
def tags(self)->dict[str,DeploymentType]:
2732
return {"type": self.type}
28-
33+
34+
2935
@staticmethod
3036
def getDeployment(v:dict[str,Any], info:ValidationInfo)->Deployment:
3137
if v != {}:
@@ -36,8 +42,17 @@ def getDeployment(v:dict[str,Any], info:ValidationInfo)->Deployment:
3642
detection_name = info.data.get("name", None)
3743
if detection_name is None:
3844
raise ValueError("Could not create inline deployment - Baseline or Detection lacking 'name' field,")
45+
46+
# Add a number of static values
47+
v.update({
48+
'name': f"{detection_name} - Inline Deployment",
49+
'id':uuid.uuid4(),
50+
'date': datetime.date.today(),
51+
'description': "Inline deployment created at runtime.",
52+
'author': "contentctl tool"
53+
})
54+
3955

40-
v['name'] = f"{detection_name} - Inline Deployment"
4156
# This constructs a temporary in-memory deployment,
4257
# allowing the deployment to be easily defined in the
4358
# detection on a per detection basis.

contentctl/objects/investigation.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
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-
8+
from contentctl.objects.config import CustomApp
99

1010
# TODO (#266): disable the use_enum_values configuration
1111
class Investigation(SecurityContentObject):
1212
model_config = ConfigDict(use_enum_values=True,validate_default=False)
1313
type: str = Field(...,pattern="^Investigation$")
1414
datamodel: list[DataModel] = Field(...)
15-
15+
name:str = Field(...,max_length=67)
1616
search: str = Field(...)
1717
how_to_implement: str = Field(...)
1818
known_false_positives: str = Field(...)
@@ -69,5 +69,13 @@ def model_post_init(self, ctx:dict[str,Any]):
6969
for story in self.tags.analytic_story:
7070
story.investigations.append(self)
7171

72+
def get_conf_stanza_name(self, app:CustomApp, max_stanza_length:int=81)->str:
73+
stanza_name = f"{app.label} - {self.name} - Response Task"
74+
if len(stanza_name) > max_stanza_length:
75+
raise ValueError(f"conf stanza may only be {max_stanza_length} characters, "
76+
f"but stanza was actually {len(stanza_name)} characters: '{stanza_name}' ")
77+
#print(f"Stanza Length[{len(stanza_name)}]")
78+
return stanza_name
79+
7280

7381

contentctl/objects/lookup.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from __future__ import annotations
2-
from pydantic import field_validator, ValidationInfo, model_validator, FilePath, model_serializer
2+
from pydantic import field_validator, ValidationInfo, model_validator, FilePath, model_serializer, Field, NonNegativeInt
33
from typing import TYPE_CHECKING, Optional, Any, Union
44
import re
55
import csv
6+
import uuid
7+
import datetime
68
if TYPE_CHECKING:
79
from contentctl.input.director import DirectorOutputDto
810
from contentctl.objects.config import validate
@@ -32,6 +34,11 @@ class Lookup(SecurityContentObject):
3234
match_type: Optional[str] = None
3335
min_matches: Optional[int] = None
3436
case_sensitive_match: Optional[bool] = None
37+
# TODO: Add id field to all lookup ymls
38+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
39+
date: datetime.date = Field(datetime.date.today())
40+
author: str = Field("NO AUTHOR DEFINED",max_length=255)
41+
version: NonNegativeInt = 1
3542

3643

3744
@model_serializer

contentctl/objects/macro.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
from __future__ import annotations
44
from typing import TYPE_CHECKING, List
55
import re
6-
from pydantic import Field, model_serializer
6+
from pydantic import Field, model_serializer, NonNegativeInt
7+
import uuid
8+
import datetime
79
if TYPE_CHECKING:
810
from contentctl.input.director import DirectorOutputDto
911
from contentctl.objects.security_content_object import SecurityContentObject
@@ -22,7 +24,11 @@
2224
class Macro(SecurityContentObject):
2325
definition: str = Field(..., min_length=1)
2426
arguments: List[str] = Field([])
25-
27+
# TODO: Add id field to all macro ymls
28+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
29+
date: datetime.date = Field(datetime.date.today())
30+
author: str = Field("NO AUTHOR DEFINED",max_length=255)
31+
version: NonNegativeInt = 1
2632

2733

2834

contentctl/output/templates/analyticstories_detections.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
{% for detection in objects %}
55
{% if (detection.type == 'TTP' or detection.type == 'Anomaly' or detection.type == 'Hunting' or detection.type == 'Correlation') %}
6-
[savedsearch://{{app.label}} - {{ detection.name }} - Rule]
6+
[savedsearch://{{ detection.get_conf_stanza_name(app) }}]
77
type = detection
88
asset_type = {{ detection.tags.asset_type.value }}
99
confidence = medium

contentctl/output/templates/analyticstories_investigations.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
{% for detection in objects %}
55
{% if (detection.type == 'Investigation') %}
6-
[savedsearch://{{app.label}} - {{ detection.name }} - Response Task]
6+
[savedsearch://{{ detection.get_conf_stanza_name(app) }}]
77
type = investigation
88
explanation = none
99
{% if detection.how_to_implement is defined %}

contentctl/output/templates/savedsearches_baselines.j2

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44

55
{% for detection in objects %}
66
{% if (detection.type == 'Baseline') %}
7-
[{{app.label}} - {{ detection.name }}]
7+
[{{ detection.get_conf_stanza_name(app) }}]
88
action.escu = 0
99
action.escu.enabled = 1
1010
action.escu.search_type = support
11-
action.escu.full_search_name = {{app.label}} - {{ detection.name }}
1211
description = {{ detection.description | escapeNewlines() }}
1312
action.escu.creation_date = {{ detection.date }}
1413
action.escu.modification_date = {{ detection.date }}

0 commit comments

Comments
 (0)