contentctl 4.4.7__py3-none-any.whl → 5.0.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contentctl/actions/build.py +39 -27
- contentctl/actions/detection_testing/DetectionTestingManager.py +0 -1
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +32 -26
- contentctl/actions/detection_testing/progress_bar.py +6 -6
- contentctl/actions/detection_testing/views/DetectionTestingView.py +4 -4
- contentctl/actions/new_content.py +98 -81
- contentctl/actions/test.py +4 -5
- contentctl/actions/validate.py +2 -1
- contentctl/contentctl.py +114 -79
- contentctl/helper/utils.py +0 -14
- contentctl/input/director.py +5 -5
- contentctl/input/new_content_questions.py +2 -2
- contentctl/input/yml_reader.py +11 -6
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +228 -120
- contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +5 -7
- contentctl/objects/alert_action.py +2 -1
- contentctl/objects/atomic.py +1 -0
- contentctl/objects/base_test.py +4 -3
- contentctl/objects/base_test_result.py +3 -3
- contentctl/objects/baseline.py +26 -6
- contentctl/objects/baseline_tags.py +2 -3
- contentctl/objects/config.py +26 -45
- contentctl/objects/constants.py +4 -1
- contentctl/objects/correlation_search.py +89 -95
- contentctl/objects/data_source.py +5 -6
- contentctl/objects/deployment.py +2 -10
- contentctl/objects/deployment_email.py +2 -1
- contentctl/objects/deployment_notable.py +2 -1
- contentctl/objects/deployment_phantom.py +2 -1
- contentctl/objects/deployment_rba.py +2 -1
- contentctl/objects/deployment_scheduling.py +2 -1
- contentctl/objects/deployment_slack.py +2 -1
- contentctl/objects/detection_tags.py +7 -42
- contentctl/objects/drilldown.py +1 -0
- contentctl/objects/enums.py +21 -58
- contentctl/objects/investigation.py +6 -5
- contentctl/objects/investigation_tags.py +2 -3
- contentctl/objects/lookup.py +145 -63
- contentctl/objects/macro.py +2 -3
- contentctl/objects/mitre_attack_enrichment.py +2 -2
- contentctl/objects/observable.py +3 -1
- contentctl/objects/playbook_tags.py +5 -1
- contentctl/objects/rba.py +90 -0
- contentctl/objects/risk_event.py +87 -144
- contentctl/objects/story_tags.py +1 -2
- contentctl/objects/test_attack_data.py +2 -1
- contentctl/objects/unit_test_baseline.py +2 -1
- contentctl/output/api_json_output.py +233 -220
- contentctl/output/conf_output.py +51 -44
- contentctl/output/conf_writer.py +201 -125
- contentctl/output/data_source_writer.py +0 -1
- contentctl/output/json_writer.py +2 -4
- contentctl/output/svg_output.py +1 -1
- contentctl/output/templates/analyticstories_detections.j2 +1 -1
- contentctl/output/templates/collections.j2 +1 -1
- contentctl/output/templates/doc_detections.j2 +0 -5
- contentctl/output/templates/savedsearches_detections.j2 +8 -3
- contentctl/output/templates/transforms.j2 +4 -4
- contentctl/output/yml_writer.py +15 -0
- contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +16 -34
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/METADATA +5 -4
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/RECORD +65 -68
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/WHEEL +1 -1
- contentctl/objects/event_source.py +0 -11
- contentctl/output/detection_writer.py +0 -28
- contentctl/output/new_content_yml_output.py +0 -56
- contentctl/output/yml_output.py +0 -66
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/LICENSE.md +0 -0
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Union, Any
|
|
2
|
-
from enum import
|
|
2
|
+
from enum import StrEnum
|
|
3
3
|
|
|
4
4
|
from pydantic import ConfigDict, BaseModel
|
|
5
5
|
from splunklib.data import Record # type: ignore
|
|
@@ -10,7 +10,7 @@ from contentctl.helper.utils import Utils
|
|
|
10
10
|
# TODO (#267): Align test reporting more closely w/ status enums (as it relates to "untested")
|
|
11
11
|
# TODO (PEX-432): add status "UNSET" so that we can make sure the result is always of this enum
|
|
12
12
|
# type; remove mypy ignores associated w/ these typing issues once we do
|
|
13
|
-
class TestResultStatus(
|
|
13
|
+
class TestResultStatus(StrEnum):
|
|
14
14
|
"""Enum for test status (e.g. pass/fail)"""
|
|
15
15
|
# Test failed (detection did NOT fire appropriately)
|
|
16
16
|
FAIL = "fail"
|
|
@@ -113,7 +113,7 @@ class BaseTestResult(BaseModel):
|
|
|
113
113
|
# Exceptions and enums cannot be serialized, so convert to str
|
|
114
114
|
if isinstance(getattr(self, field), Exception):
|
|
115
115
|
summary_dict[field] = str(getattr(self, field))
|
|
116
|
-
elif isinstance(getattr(self, field),
|
|
116
|
+
elif isinstance(getattr(self, field), StrEnum):
|
|
117
117
|
summary_dict[field] = str(getattr(self, field))
|
|
118
118
|
else:
|
|
119
119
|
summary_dict[field] = getattr(self, field)
|
contentctl/objects/baseline.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
from typing import Annotated,
|
|
4
|
-
|
|
3
|
+
from typing import Annotated, List,Any, TYPE_CHECKING
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from contentctl.input.director import DirectorOutputDto
|
|
6
|
+
|
|
7
|
+
from pydantic import field_validator, ValidationInfo, Field, model_serializer, computed_field
|
|
5
8
|
from contentctl.objects.deployment import Deployment
|
|
6
9
|
from contentctl.objects.security_content_object import SecurityContentObject
|
|
7
10
|
from contentctl.objects.enums import DataModel
|
|
@@ -9,21 +12,34 @@ from contentctl.objects.baseline_tags import BaselineTags
|
|
|
9
12
|
|
|
10
13
|
from contentctl.objects.config import CustomApp
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
from contentctl.objects.lookup import Lookup
|
|
13
16
|
from contentctl.objects.constants import CONTENTCTL_MAX_SEARCH_NAME_LENGTH,CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE
|
|
14
17
|
|
|
15
18
|
class Baseline(SecurityContentObject):
|
|
16
19
|
name:str = Field(...,max_length=CONTENTCTL_MAX_SEARCH_NAME_LENGTH)
|
|
17
20
|
type: Annotated[str,Field(pattern="^Baseline$")] = Field(...)
|
|
18
|
-
datamodel: Optional[List[DataModel]] = None
|
|
19
21
|
search: str = Field(..., min_length=4)
|
|
20
22
|
how_to_implement: str = Field(..., min_length=4)
|
|
21
23
|
known_false_positives: str = Field(..., min_length=4)
|
|
22
24
|
tags: BaselineTags = Field(...)
|
|
23
|
-
|
|
25
|
+
lookups: list[Lookup] = Field([], validate_default=True)
|
|
24
26
|
# enrichment
|
|
25
27
|
deployment: Deployment = Field({})
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@field_validator('lookups', mode="before")
|
|
31
|
+
@classmethod
|
|
32
|
+
def getBaselineLookups(cls, v:list[str], info:ValidationInfo) -> list[Lookup]:
|
|
33
|
+
'''
|
|
34
|
+
This function has been copied and renamed from the Detection_Abstract class
|
|
35
|
+
'''
|
|
36
|
+
director:DirectorOutputDto = info.context.get("output_dto",None)
|
|
37
|
+
search: str | None = info.data.get("search",None)
|
|
38
|
+
if search is None:
|
|
39
|
+
raise ValueError("Search was None - is this file missing the search field?")
|
|
40
|
+
|
|
41
|
+
lookups = Lookup.get_lookups(search, director)
|
|
42
|
+
return lookups
|
|
27
43
|
|
|
28
44
|
def get_conf_stanza_name(self, app:CustomApp)->str:
|
|
29
45
|
stanza_name = CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE.format(app_label=app.label, detection_name=self.name)
|
|
@@ -34,6 +50,10 @@ class Baseline(SecurityContentObject):
|
|
|
34
50
|
def getDeployment(cls, v:Any, info:ValidationInfo)->Deployment:
|
|
35
51
|
return Deployment.getDeployment(v,info)
|
|
36
52
|
|
|
53
|
+
@computed_field
|
|
54
|
+
@property
|
|
55
|
+
def datamodel(self) -> List[DataModel]:
|
|
56
|
+
return [dm for dm in DataModel if dm in self.search]
|
|
37
57
|
|
|
38
58
|
@model_serializer
|
|
39
59
|
def serialize_model(self):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from pydantic import BaseModel, Field, field_validator, ValidationInfo, model_serializer
|
|
2
|
+
from pydantic import BaseModel, Field, field_validator, ValidationInfo, model_serializer, ConfigDict
|
|
3
3
|
from typing import List, Any, Union
|
|
4
4
|
|
|
5
5
|
from contentctl.objects.story import Story
|
|
@@ -12,12 +12,12 @@ from contentctl.objects.enums import SecurityDomain
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class BaselineTags(BaseModel):
|
|
15
|
+
model_config = ConfigDict(extra="forbid")
|
|
15
16
|
analytic_story: list[Story] = Field(...)
|
|
16
17
|
#deployment: Deployment = Field('SET_IN_GET_DEPLOYMENT_FUNCTION')
|
|
17
18
|
# TODO (#223): can we remove str from the possible types here?
|
|
18
19
|
detections: List[Union[Detection,str]] = Field(...)
|
|
19
20
|
product: List[SecurityContentProductName] = Field(...,min_length=1)
|
|
20
|
-
required_fields: List[str] = Field(...,min_length=1)
|
|
21
21
|
security_domain: SecurityDomain = Field(...)
|
|
22
22
|
|
|
23
23
|
|
|
@@ -33,7 +33,6 @@ class BaselineTags(BaseModel):
|
|
|
33
33
|
"analytic_story": [story.name for story in self.analytic_story],
|
|
34
34
|
"detections": [detection.name for detection in self.detections if isinstance(detection,Detection)],
|
|
35
35
|
"product": self.product,
|
|
36
|
-
"required_fields":self.required_fields,
|
|
37
36
|
"security_domain":self.security_domain,
|
|
38
37
|
"deployments": None
|
|
39
38
|
}
|
contentctl/objects/config.py
CHANGED
|
@@ -33,9 +33,9 @@ COMMON_INFORMATION_MODEL_UID = 1621
|
|
|
33
33
|
SPLUNKBASE_URL = "https://splunkbase.splunk.com/app/{uid}/release/{version}/download"
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
37
36
|
class App_Base(BaseModel,ABC):
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True, extra='forbid')
|
|
39
39
|
uid: Optional[int] = Field(default=None)
|
|
40
40
|
title: str = Field(description="Human-readable name used by the app. This can have special characters.")
|
|
41
41
|
appid: Optional[APPID_TYPE]= Field(default=None,description="Internal name used by your app. "
|
|
@@ -59,9 +59,8 @@ class App_Base(BaseModel,ABC):
|
|
|
59
59
|
config.getLocalAppDir().mkdir(parents=True)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
63
62
|
class TestApp(App_Base):
|
|
64
|
-
model_config = ConfigDict(
|
|
63
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
65
64
|
hardcoded_path: Optional[Union[FilePath,HttpUrl]] = Field(default=None, description="This may be a relative or absolute link to a file OR an HTTP URL linking to your app.")
|
|
66
65
|
|
|
67
66
|
|
|
@@ -99,9 +98,8 @@ class TestApp(App_Base):
|
|
|
99
98
|
return str(destination)
|
|
100
99
|
|
|
101
100
|
|
|
102
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
103
101
|
class CustomApp(App_Base):
|
|
104
|
-
model_config = ConfigDict(
|
|
102
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
105
103
|
# Fields required for app.conf based on
|
|
106
104
|
# https://docs.splunk.com/Documentation/Splunk/9.0.4/Admin/Appconf
|
|
107
105
|
uid: int = Field(ge=2, lt=100000, default_factory=lambda:random.randint(20000,100000))
|
|
@@ -159,9 +157,8 @@ class CustomApp(App_Base):
|
|
|
159
157
|
verbose_print=True)
|
|
160
158
|
return str(destination)
|
|
161
159
|
|
|
162
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
163
160
|
class Config_Base(BaseModel):
|
|
164
|
-
model_config = ConfigDict(
|
|
161
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
165
162
|
|
|
166
163
|
path: DirectoryPath = Field(default=DirectoryPath("."), description="The root of your app.")
|
|
167
164
|
app:CustomApp = Field(default_factory=CustomApp)
|
|
@@ -175,7 +172,7 @@ class Config_Base(BaseModel):
|
|
|
175
172
|
return str(path)
|
|
176
173
|
|
|
177
174
|
class init(Config_Base):
|
|
178
|
-
model_config = ConfigDict(
|
|
175
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
179
176
|
bare: bool = Field(default=False, description="contentctl normally provides some some example content "
|
|
180
177
|
"(macros, stories, data_sources, and/or analytic stories). This option disables "
|
|
181
178
|
"initialization with that additional contnet. Note that even if --bare is used, it "
|
|
@@ -184,9 +181,8 @@ class init(Config_Base):
|
|
|
184
181
|
"the deployment/ directory (since it is not yet easily customizable).")
|
|
185
182
|
|
|
186
183
|
|
|
187
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
188
184
|
class validate(Config_Base):
|
|
189
|
-
model_config = ConfigDict(
|
|
185
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
190
186
|
enrichments: bool = Field(default=False, description="Enable MITRE, APP, and CVE Enrichments. "\
|
|
191
187
|
"This is useful when outputting a release build "\
|
|
192
188
|
"and validating these values, but should otherwise "\
|
|
@@ -241,9 +237,8 @@ class report(validate):
|
|
|
241
237
|
return self.path/"reporting/"
|
|
242
238
|
|
|
243
239
|
|
|
244
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
245
240
|
class build(validate):
|
|
246
|
-
model_config = ConfigDict(
|
|
241
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
247
242
|
build_path: DirectoryPath = Field(default=DirectoryPath("dist/"), title="Target path for all build outputs")
|
|
248
243
|
|
|
249
244
|
@field_serializer('build_path',when_used='always')
|
|
@@ -395,17 +390,15 @@ class new(Config_Base):
|
|
|
395
390
|
type: NewContentType = Field(default=NewContentType.detection, description="Specify the type of content you would like to create.")
|
|
396
391
|
|
|
397
392
|
|
|
398
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
399
393
|
class deploy_acs(inspect):
|
|
400
|
-
model_config = ConfigDict(
|
|
394
|
+
model_config = ConfigDict(validate_default=False, arbitrary_types_allowed=True)
|
|
401
395
|
#ignore linter error
|
|
402
396
|
splunk_cloud_jwt_token: str = Field(exclude=True, description="Splunk JWT used for performing ACS operations on a Splunk Cloud Instance")
|
|
403
397
|
splunk_cloud_stack: str = Field(description="The name of your Splunk Cloud Stack")
|
|
404
398
|
|
|
405
399
|
|
|
406
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
407
400
|
class Infrastructure(BaseModel):
|
|
408
|
-
model_config = ConfigDict(
|
|
401
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
409
402
|
splunk_app_username:str = Field(default="admin", description="Username for logging in to your Splunk Server")
|
|
410
403
|
splunk_app_password:str = Field(exclude=True, default="password", description="Password for logging in to your Splunk Server.")
|
|
411
404
|
instance_address:str = Field(..., description="Address of your splunk server.")
|
|
@@ -415,15 +408,13 @@ class Infrastructure(BaseModel):
|
|
|
415
408
|
instance_name: str = Field(...)
|
|
416
409
|
|
|
417
410
|
|
|
418
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
419
411
|
class Container(Infrastructure):
|
|
420
|
-
model_config = ConfigDict(
|
|
412
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
421
413
|
instance_address:str = Field(default="localhost", description="Address of your splunk server.")
|
|
422
414
|
|
|
423
415
|
|
|
424
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
425
416
|
class ContainerSettings(BaseModel):
|
|
426
|
-
model_config = ConfigDict(
|
|
417
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
427
418
|
leave_running: bool = Field(default=True, description="Leave container running after it is first "
|
|
428
419
|
"set up to speed up subsequent test runs.")
|
|
429
420
|
num_containers: PositiveInt = Field(default=1, description="Number of containers to start in parallel. "
|
|
@@ -444,18 +435,19 @@ class ContainerSettings(BaseModel):
|
|
|
444
435
|
|
|
445
436
|
class All(BaseModel):
|
|
446
437
|
#Doesn't need any extra logic
|
|
438
|
+
mode_name:str = "All"
|
|
447
439
|
pass
|
|
448
440
|
|
|
449
441
|
|
|
450
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
451
442
|
class Changes(BaseModel):
|
|
452
|
-
model_config = ConfigDict(
|
|
443
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
444
|
+
mode_name: str = "Changes"
|
|
453
445
|
target_branch:str = Field(...,description="The target branch to diff against. Note that this includes uncommitted changes in the working directory as well.")
|
|
454
446
|
|
|
455
447
|
|
|
456
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
457
448
|
class Selected(BaseModel):
|
|
458
|
-
model_config = ConfigDict(
|
|
449
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
450
|
+
mode_name:str = "Selected"
|
|
459
451
|
files:List[FilePath] = Field(...,description="List of detection files to test, separated by spaces.")
|
|
460
452
|
|
|
461
453
|
@field_serializer('files',when_used='always')
|
|
@@ -684,12 +676,12 @@ DEFAULT_APPS:List[TestApp] = [
|
|
|
684
676
|
class test_common(build):
|
|
685
677
|
mode:Union[Changes, Selected, All] = Field(All(), union_mode='left_to_right')
|
|
686
678
|
post_test_behavior: PostTestBehavior = Field(default=PostTestBehavior.pause_on_failure, description="Controls what to do when a test completes.\n\n"
|
|
687
|
-
f"'{PostTestBehavior.always_pause
|
|
679
|
+
f"'{PostTestBehavior.always_pause}' - the state of "
|
|
688
680
|
"the test will always pause after a test, allowing the user to log into the "
|
|
689
681
|
"server and experiment with the search and data before it is removed.\n\n"
|
|
690
|
-
f"'{PostTestBehavior.pause_on_failure
|
|
682
|
+
f"'{PostTestBehavior.pause_on_failure}' - pause execution ONLY when a test fails. The user may press ENTER in the terminal "
|
|
691
683
|
"running the test to move on to the next test.\n\n"
|
|
692
|
-
f"'{PostTestBehavior.never_pause
|
|
684
|
+
f"'{PostTestBehavior.never_pause}' - never stop testing, even if a test fails.\n\n"
|
|
693
685
|
"***SPECIAL NOTE FOR CI/CD*** 'never_pause' MUST be used for a test to "
|
|
694
686
|
"run in an unattended manner or in a CI/CD system - otherwise a single failed test "
|
|
695
687
|
"will result in the testing never finishing as the tool waits for input.")
|
|
@@ -706,7 +698,7 @@ class test_common(build):
|
|
|
706
698
|
" interactive command line workflow that can display progress bars and status information frequently. "
|
|
707
699
|
"Unfortunately it is incompatible with, or may cause poorly formatted logs, in many CI/CD systems or other unattended environments. "
|
|
708
700
|
"If you are running contentctl in CI/CD, then please set this argument to True. Note that if you are running in a CI/CD context, "
|
|
709
|
-
f"you also MUST set post_test_behavior to {PostTestBehavior.never_pause
|
|
701
|
+
f"you also MUST set post_test_behavior to {PostTestBehavior.never_pause}. Otherwiser, a failed detection will cause"
|
|
710
702
|
"the CI/CD running to pause indefinitely.")
|
|
711
703
|
|
|
712
704
|
apps: List[TestApp] = Field(default=DEFAULT_APPS, exclude=False, description="List of apps to install in test environment")
|
|
@@ -715,7 +707,7 @@ class test_common(build):
|
|
|
715
707
|
def dumpCICDPlanAndQuit(self, githash: str, detections:List[Detection]):
|
|
716
708
|
output_file = self.path / "test_plan.yml"
|
|
717
709
|
self.mode = Selected(files=sorted([detection.file_path for detection in detections], key=lambda path: str(path)))
|
|
718
|
-
self.post_test_behavior = PostTestBehavior.never_pause
|
|
710
|
+
self.post_test_behavior = PostTestBehavior.never_pause
|
|
719
711
|
#required so that CI/CD does not get too much output or hang
|
|
720
712
|
self.disable_tqdm = True
|
|
721
713
|
|
|
@@ -782,12 +774,12 @@ class test_common(build):
|
|
|
782
774
|
def suppressTQDM(self)->Self:
|
|
783
775
|
if self.disable_tqdm:
|
|
784
776
|
tqdm.tqdm.__init__ = partialmethod(tqdm.tqdm.__init__, disable=True)
|
|
785
|
-
if self.post_test_behavior != PostTestBehavior.never_pause
|
|
777
|
+
if self.post_test_behavior != PostTestBehavior.never_pause:
|
|
786
778
|
raise ValueError(f"You have disabled tqdm, presumably because you are "
|
|
787
779
|
f"running in CI/CD or another unattended context.\n"
|
|
788
780
|
f"However, post_test_behavior is set to [{self.post_test_behavior}].\n"
|
|
789
781
|
f"If that is the case, then you MUST set post_test_behavior "
|
|
790
|
-
f"to [{PostTestBehavior.never_pause
|
|
782
|
+
f"to [{PostTestBehavior.never_pause}].\n"
|
|
791
783
|
"Otherwise, if a detection fails in CI/CD, your CI/CD runner will hang forever.")
|
|
792
784
|
return self
|
|
793
785
|
|
|
@@ -817,18 +809,8 @@ class test_common(build):
|
|
|
817
809
|
return self
|
|
818
810
|
|
|
819
811
|
|
|
820
|
-
def getModeName(self)->str:
|
|
821
|
-
if isinstance(self.mode, All):
|
|
822
|
-
return DetectionTestingMode.all.value
|
|
823
|
-
elif isinstance(self.mode, Changes):
|
|
824
|
-
return DetectionTestingMode.changes.value
|
|
825
|
-
else:
|
|
826
|
-
return DetectionTestingMode.selected.value
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
830
812
|
class test(test_common):
|
|
831
|
-
model_config = ConfigDict(
|
|
813
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
832
814
|
container_settings:ContainerSettings = ContainerSettings()
|
|
833
815
|
test_instances: List[Container] = Field([], exclude = True, validate_default=True)
|
|
834
816
|
splunk_api_username: Optional[str] = Field(default=None, exclude = True,description="Splunk API username used for running appinspect or installating apps from Splunkbase")
|
|
@@ -893,9 +875,8 @@ class test(test_common):
|
|
|
893
875
|
TEST_ARGS_ENV = "CONTENTCTL_TEST_INFRASTRUCTURES"
|
|
894
876
|
|
|
895
877
|
|
|
896
|
-
# TODO (#266): disable the use_enum_values configuration
|
|
897
878
|
class test_servers(test_common):
|
|
898
|
-
model_config = ConfigDict(
|
|
879
|
+
model_config = ConfigDict(validate_default=True, arbitrary_types_allowed=True)
|
|
899
880
|
test_instances:List[Infrastructure] = Field([],description="Test against one or more preconfigured servers.", validate_default=True)
|
|
900
881
|
server_info:Optional[str] = Field(None, validate_default=True, description='String of pre-configured servers to use for testing. The list MUST be in the format:\n'
|
|
901
882
|
'address,username,web_ui_port,hec_port,api_port;address_2,username_2,web_ui_port_2,hec_port_2,api_port_2'
|
contentctl/objects/constants.py
CHANGED
|
@@ -79,6 +79,7 @@ SES_KILL_CHAIN_MAPPINGS = {
|
|
|
79
79
|
"Actions on Objectives": 7
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
# TODO (cmcginley): @ljstella should this be removed? also referenced in new_content.py
|
|
82
83
|
SES_OBSERVABLE_ROLE_MAPPING = {
|
|
83
84
|
"Other": -1,
|
|
84
85
|
"Unknown": 0,
|
|
@@ -93,6 +94,7 @@ SES_OBSERVABLE_ROLE_MAPPING = {
|
|
|
93
94
|
"Observer": 9
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
# TODO (cmcginley): @ljstella should this be removed? also referenced in new_content.py
|
|
96
98
|
SES_OBSERVABLE_TYPE_MAPPING = {
|
|
97
99
|
"Unknown": 0,
|
|
98
100
|
"Hostname": 1,
|
|
@@ -135,6 +137,7 @@ SES_ATTACK_TACTICS_ID_MAPPING = {
|
|
|
135
137
|
"Impact": "TA0040"
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
# TODO (cmcginley): is this just for the transition testing?
|
|
138
141
|
RBA_OBSERVABLE_ROLE_MAPPING = {
|
|
139
142
|
"Attacker": 0,
|
|
140
143
|
"Victim": 1
|
|
@@ -149,7 +152,7 @@ DOWNLOADS_DIRECTORY = "downloads"
|
|
|
149
152
|
# errors, if its name is longer than 99 characters.
|
|
150
153
|
# When an saved search is cloned in Enterprise Security User Interface,
|
|
151
154
|
# it is wrapped in the following:
|
|
152
|
-
# {Detection.tags.security_domain
|
|
155
|
+
# {Detection.tags.security_domain} - {SEARCH_STANZA_NAME} - Rule
|
|
153
156
|
# Similarly, when we generate the search stanza name in contentctl, it
|
|
154
157
|
# is app.label - detection.name - Rule
|
|
155
158
|
# However, in product the search name is:
|