contentctl 3.6.0__py3-none-any.whl → 4.0.2__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 +89 -0
- contentctl/actions/detection_testing/DetectionTestingManager.py +48 -49
- contentctl/actions/detection_testing/GitService.py +148 -230
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +14 -24
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +43 -17
- contentctl/actions/detection_testing/views/DetectionTestingView.py +3 -2
- contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -8
- contentctl/actions/doc_gen.py +1 -1
- contentctl/actions/initialize.py +28 -65
- contentctl/actions/inspect.py +260 -0
- contentctl/actions/new_content.py +106 -13
- contentctl/actions/release_notes.py +168 -144
- contentctl/actions/reporting.py +24 -13
- contentctl/actions/test.py +39 -20
- contentctl/actions/validate.py +25 -48
- contentctl/contentctl.py +196 -754
- contentctl/enrichments/attack_enrichment.py +69 -19
- contentctl/enrichments/cve_enrichment.py +28 -13
- contentctl/helper/link_validator.py +24 -26
- contentctl/helper/utils.py +7 -3
- contentctl/input/director.py +139 -201
- contentctl/input/new_content_questions.py +63 -61
- contentctl/input/sigma_converter.py +1 -2
- contentctl/input/ssa_detection_builder.py +16 -7
- contentctl/input/yml_reader.py +4 -3
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +487 -154
- contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +155 -51
- contentctl/objects/alert_action.py +40 -0
- contentctl/objects/atomic.py +212 -0
- contentctl/objects/baseline.py +44 -43
- contentctl/objects/baseline_tags.py +69 -20
- contentctl/objects/config.py +857 -125
- contentctl/objects/constants.py +0 -1
- contentctl/objects/correlation_search.py +1 -1
- contentctl/objects/data_source.py +2 -4
- contentctl/objects/deployment.py +61 -21
- contentctl/objects/deployment_email.py +2 -2
- contentctl/objects/deployment_notable.py +4 -4
- contentctl/objects/deployment_phantom.py +2 -2
- contentctl/objects/deployment_rba.py +3 -4
- contentctl/objects/deployment_scheduling.py +2 -3
- contentctl/objects/deployment_slack.py +2 -2
- contentctl/objects/detection.py +1 -5
- contentctl/objects/detection_tags.py +210 -119
- contentctl/objects/enums.py +312 -24
- contentctl/objects/integration_test.py +1 -1
- contentctl/objects/integration_test_result.py +0 -2
- contentctl/objects/investigation.py +62 -53
- contentctl/objects/investigation_tags.py +30 -6
- contentctl/objects/lookup.py +80 -31
- contentctl/objects/macro.py +29 -45
- contentctl/objects/mitre_attack_enrichment.py +29 -5
- contentctl/objects/observable.py +3 -7
- contentctl/objects/playbook.py +60 -30
- contentctl/objects/playbook_tags.py +45 -8
- contentctl/objects/security_content_object.py +1 -5
- contentctl/objects/ssa_detection.py +8 -4
- contentctl/objects/ssa_detection_tags.py +19 -26
- contentctl/objects/story.py +142 -44
- contentctl/objects/story_tags.py +46 -33
- contentctl/objects/unit_test.py +7 -2
- contentctl/objects/unit_test_attack_data.py +10 -19
- contentctl/objects/unit_test_baseline.py +1 -1
- contentctl/objects/unit_test_old.py +4 -3
- contentctl/objects/unit_test_result.py +5 -3
- contentctl/objects/unit_test_ssa.py +31 -0
- contentctl/output/api_json_output.py +202 -130
- contentctl/output/attack_nav_output.py +20 -9
- contentctl/output/attack_nav_writer.py +3 -3
- contentctl/output/ba_yml_output.py +3 -3
- contentctl/output/conf_output.py +125 -391
- contentctl/output/conf_writer.py +169 -31
- contentctl/output/jinja_writer.py +2 -2
- contentctl/output/json_writer.py +17 -5
- contentctl/output/new_content_yml_output.py +8 -7
- contentctl/output/svg_output.py +17 -27
- contentctl/output/templates/analyticstories_detections.j2 +8 -4
- contentctl/output/templates/analyticstories_investigations.j2 +1 -1
- contentctl/output/templates/analyticstories_stories.j2 +6 -6
- contentctl/output/templates/app.conf.j2 +2 -2
- contentctl/output/templates/app.manifest.j2 +2 -2
- contentctl/output/templates/detection_coverage.j2 +6 -8
- contentctl/output/templates/doc_detection_page.j2 +2 -2
- contentctl/output/templates/doc_detections.j2 +2 -2
- contentctl/output/templates/doc_stories.j2 +1 -1
- contentctl/output/templates/es_investigations_investigations.j2 +1 -1
- contentctl/output/templates/es_investigations_stories.j2 +1 -1
- contentctl/output/templates/header.j2 +2 -1
- contentctl/output/templates/macros.j2 +6 -10
- contentctl/output/templates/savedsearches_baselines.j2 +5 -5
- contentctl/output/templates/savedsearches_detections.j2 +36 -33
- contentctl/output/templates/savedsearches_investigations.j2 +4 -4
- contentctl/output/templates/transforms.j2 +4 -4
- contentctl/output/yml_writer.py +2 -2
- contentctl/templates/app_template/README.md +7 -0
- contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/nav/default.xml +1 -0
- contentctl/templates/app_template/lookups/mitre_enrichment.csv +638 -0
- contentctl/templates/deployments/{00_default_anomaly.yml → escu_default_configuration_anomaly.yml} +1 -2
- contentctl/templates/deployments/{00_default_baseline.yml → escu_default_configuration_baseline.yml} +1 -2
- contentctl/templates/deployments/{00_default_correlation.yml → escu_default_configuration_correlation.yml} +2 -2
- contentctl/templates/deployments/{00_default_hunting.yml → escu_default_configuration_hunting.yml} +2 -2
- contentctl/templates/deployments/{00_default_ttp.yml → escu_default_configuration_ttp.yml} +1 -2
- contentctl/templates/detections/anomalous_usage_of_7zip.yml +0 -1
- contentctl/templates/stories/cobalt_strike.yml +0 -1
- {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/METADATA +36 -15
- contentctl-4.0.2.dist-info/RECORD +168 -0
- contentctl/actions/detection_testing/DataManipulation.py +0 -149
- contentctl/actions/generate.py +0 -91
- contentctl/helper/config_handler.py +0 -75
- contentctl/input/baseline_builder.py +0 -66
- contentctl/input/basic_builder.py +0 -58
- contentctl/input/detection_builder.py +0 -370
- contentctl/input/investigation_builder.py +0 -42
- contentctl/input/new_content_generator.py +0 -95
- contentctl/input/playbook_builder.py +0 -68
- contentctl/input/story_builder.py +0 -106
- contentctl/objects/app.py +0 -214
- contentctl/objects/repo_config.py +0 -163
- contentctl/objects/test_config.py +0 -630
- contentctl/output/templates/macros_detections.j2 +0 -7
- contentctl/output/templates/splunk_app/README.md +0 -7
- contentctl-3.6.0.dist-info/RECORD +0 -176
- /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_story_detail.txt +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_summary.txt +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_usage_dashboard.txt +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/analytic_stories.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/app.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/commands.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/content-version.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/escu_summary.xml +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/feedback.xml +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/distsearch.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/usage_searches.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/default/use_case_library.conf +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/metadata/default.meta +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon.png +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt.png +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt_2x.png +0 -0
- /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon_2x.png +0 -0
- {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/LICENSE.md +0 -0
- {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/WHEEL +0 -0
- {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/entry_points.txt +0 -0
contentctl/actions/test.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from typing import List
|
|
2
3
|
|
|
3
|
-
from contentctl.objects.
|
|
4
|
-
from contentctl.objects.enums import DetectionTestingMode
|
|
4
|
+
from contentctl.objects.config import test_common
|
|
5
|
+
from contentctl.objects.enums import DetectionTestingMode, DetectionStatus, AnalyticsType
|
|
6
|
+
from contentctl.objects.detection import Detection
|
|
5
7
|
|
|
6
8
|
from contentctl.input.director import DirectorOutputDto
|
|
7
9
|
|
|
8
|
-
from contentctl.actions.detection_testing.GitService import (
|
|
9
|
-
GitService,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
10
|
from contentctl.actions.detection_testing.DetectionTestingManager import (
|
|
13
11
|
DetectionTestingManager,
|
|
14
12
|
DetectionTestingManagerInputDto,
|
|
@@ -32,24 +30,45 @@ from contentctl.actions.detection_testing.views.DetectionTestingViewFile import
|
|
|
32
30
|
DetectionTestingViewFile,
|
|
33
31
|
)
|
|
34
32
|
|
|
35
|
-
from
|
|
36
|
-
|
|
33
|
+
from contentctl.objects.integration_test import IntegrationTest
|
|
34
|
+
|
|
35
|
+
import pathlib
|
|
37
36
|
|
|
38
37
|
MAXIMUM_CONFIGURATION_TIME_SECONDS = 600
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
@dataclass(frozen=True)
|
|
42
41
|
class TestInputDto:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
config: TestConfig
|
|
42
|
+
detections: List[Detection]
|
|
43
|
+
config: test_common
|
|
46
44
|
|
|
47
45
|
|
|
48
|
-
class TestOutputDto:
|
|
49
|
-
results: list
|
|
50
|
-
|
|
51
|
-
|
|
52
46
|
class Test:
|
|
47
|
+
|
|
48
|
+
def filter_detections(self, input_dto: TestInputDto)->TestInputDto:
|
|
49
|
+
|
|
50
|
+
if not input_dto.config.enable_integration_testing:
|
|
51
|
+
#Skip all integraiton tests if integration testing is not enabled:
|
|
52
|
+
for detection in input_dto.detections:
|
|
53
|
+
for test in detection.tests:
|
|
54
|
+
if isinstance(test, IntegrationTest):
|
|
55
|
+
test.skip("TEST SKIPPED: Skipping all integration tests")
|
|
56
|
+
|
|
57
|
+
list_after_filtering:List[Detection] = []
|
|
58
|
+
#extra filtering which may be removed/modified in the future
|
|
59
|
+
for detection in input_dto.detections:
|
|
60
|
+
if (detection.status != DetectionStatus.production.value):
|
|
61
|
+
#print(f"{detection.name} - Not testing because [STATUS: {detection.status}]")
|
|
62
|
+
pass
|
|
63
|
+
elif detection.type == AnalyticsType.Correlation:
|
|
64
|
+
#print(f"{detection.name} - Not testing because [ TYPE: {detection.type}]")
|
|
65
|
+
pass
|
|
66
|
+
else:
|
|
67
|
+
list_after_filtering.append(detection)
|
|
68
|
+
|
|
69
|
+
return TestInputDto(list_after_filtering, input_dto.config)
|
|
70
|
+
|
|
71
|
+
|
|
53
72
|
def execute(self, input_dto: TestInputDto) -> bool:
|
|
54
73
|
|
|
55
74
|
|
|
@@ -62,19 +81,19 @@ class Test:
|
|
|
62
81
|
|
|
63
82
|
manager_input_dto = DetectionTestingManagerInputDto(
|
|
64
83
|
config=input_dto.config,
|
|
65
|
-
|
|
84
|
+
detections=input_dto.detections,
|
|
66
85
|
views=[web, cli, file],
|
|
67
86
|
)
|
|
68
87
|
manager = DetectionTestingManager(
|
|
69
88
|
input_dto=manager_input_dto, output_dto=output_dto
|
|
70
89
|
)
|
|
71
90
|
|
|
72
|
-
if len(input_dto.
|
|
73
|
-
print(f"With Detection Testing Mode '{input_dto.config.
|
|
91
|
+
if len(input_dto.detections) == 0:
|
|
92
|
+
print(f"With Detection Testing Mode '{input_dto.config.getModeName()}', there were [0] detections found to test.\nAs such, we will quit immediately.")
|
|
74
93
|
else:
|
|
75
|
-
print(f"MODE: [{input_dto.config.
|
|
94
|
+
print(f"MODE: [{input_dto.config.getModeName()}] - Test [{len(input_dto.detections)}] detections")
|
|
76
95
|
if input_dto.config.mode in [DetectionTestingMode.changes, DetectionTestingMode.selected]:
|
|
77
|
-
files_string = '\n- '.join([
|
|
96
|
+
files_string = '\n- '.join([str(pathlib.Path(detection.file_path).relative_to(input_dto.config.path)) for detection in input_dto.detections])
|
|
78
97
|
print(f"Detections:\n- {files_string}")
|
|
79
98
|
|
|
80
99
|
manager.setup()
|
contentctl/actions/validate.py
CHANGED
|
@@ -6,42 +6,32 @@ from pydantic import ValidationError
|
|
|
6
6
|
from typing import Union
|
|
7
7
|
|
|
8
8
|
from contentctl.objects.enums import SecurityContentProduct
|
|
9
|
+
from contentctl.objects.abstract_security_content_objects.security_content_object_abstract import SecurityContentObject_Abstract
|
|
9
10
|
from contentctl.input.director import (
|
|
10
11
|
Director,
|
|
11
|
-
|
|
12
|
-
DirectorOutputDto,
|
|
12
|
+
DirectorOutputDto
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
from contentctl.objects.config import validate
|
|
16
|
+
from contentctl.enrichments.attack_enrichment import AttackEnrichment
|
|
17
|
+
from contentctl.enrichments.cve_enrichment import CveEnrichment
|
|
18
|
+
from contentctl.objects.atomic import AtomicTest
|
|
20
19
|
|
|
21
20
|
class Validate:
|
|
22
|
-
def execute(self, input_dto:
|
|
23
|
-
|
|
21
|
+
def execute(self, input_dto: validate) -> DirectorOutputDto:
|
|
22
|
+
|
|
23
|
+
director_output_dto = DirectorOutputDto(AtomicTest.getAtomicTestsFromArtRepo(repo_path=input_dto.getAtomicRedTeamRepoPath(),
|
|
24
|
+
enabled=input_dto.enrichments),
|
|
25
|
+
AttackEnrichment.getAttackEnrichment(input_dto),
|
|
26
|
+
[],[],[],[],[],[],[],[],[])
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
director = Director(director_output_dto)
|
|
25
|
-
director.execute(input_dto
|
|
26
|
-
|
|
27
|
-
# uuid validation all objects
|
|
28
|
-
try:
|
|
29
|
-
security_content_objects = (
|
|
30
|
-
director_output_dto.detections
|
|
31
|
-
+ director_output_dto.stories
|
|
32
|
-
+ director_output_dto.baselines
|
|
33
|
-
+ director_output_dto.investigations
|
|
34
|
-
+ director_output_dto.playbooks
|
|
35
|
-
)
|
|
36
|
-
self.validate_duplicate_uuids(security_content_objects)
|
|
37
|
-
|
|
38
|
-
except ValueError as e:
|
|
39
|
-
print(e)
|
|
40
|
-
sys.exit(1)
|
|
30
|
+
director.execute(input_dto)
|
|
41
31
|
|
|
42
|
-
return
|
|
32
|
+
return director_output_dto
|
|
43
33
|
|
|
44
|
-
def validate_duplicate_uuids(self, security_content_objects):
|
|
34
|
+
def validate_duplicate_uuids(self, security_content_objects:list[SecurityContentObject_Abstract]):
|
|
45
35
|
all_uuids = set()
|
|
46
36
|
duplicate_uuids = set()
|
|
47
37
|
for elem in security_content_objects:
|
|
@@ -54,28 +44,15 @@ class Validate:
|
|
|
54
44
|
|
|
55
45
|
if len(duplicate_uuids) == 0:
|
|
56
46
|
return
|
|
57
|
-
|
|
47
|
+
|
|
58
48
|
# At least once duplicate uuid has been found. Enumerate all
|
|
59
49
|
# the pieces of content that use duplicate uuids
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
for
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
duplicate_messages = []
|
|
51
|
+
for uuid in duplicate_uuids:
|
|
52
|
+
duplicate_uuid_content = [str(content.file_path) for content in security_content_objects if content.id in duplicate_uuids]
|
|
53
|
+
duplicate_messages.append(f"Duplicate UUID [{uuid}] in {duplicate_uuid_content}")
|
|
54
|
+
|
|
66
55
|
raise ValueError(
|
|
67
|
-
"ERROR: Duplicate ID found in objects:\n"
|
|
68
|
-
+ "\n".join(
|
|
56
|
+
"ERROR: Duplicate ID(s) found in objects:\n"
|
|
57
|
+
+ "\n - ".join(duplicate_messages)
|
|
69
58
|
)
|
|
70
|
-
|
|
71
|
-
# def validate_detection_exist_for_test(self, tests: list, detections: list):
|
|
72
|
-
# for test in tests:
|
|
73
|
-
# found_detection = False
|
|
74
|
-
# for detection in detections:
|
|
75
|
-
# if test.tests[0].file in detection.file_path:
|
|
76
|
-
# found_detection = True
|
|
77
|
-
|
|
78
|
-
# if not found_detection:
|
|
79
|
-
# raise ValueError(
|
|
80
|
-
# "ERROR: detection doesn't exist for test file: " + test.name
|
|
81
|
-
# )
|