contentctl 4.4.7__py3-none-any.whl → 5.0.0__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/__init__.py +1 -1
- contentctl/actions/build.py +102 -57
- contentctl/actions/deploy_acs.py +29 -24
- contentctl/actions/detection_testing/DetectionTestingManager.py +66 -42
- contentctl/actions/detection_testing/GitService.py +134 -76
- contentctl/actions/detection_testing/generate_detection_coverage_badge.py +48 -30
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +192 -147
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +45 -32
- contentctl/actions/detection_testing/progress_bar.py +9 -6
- contentctl/actions/detection_testing/views/DetectionTestingView.py +16 -19
- contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +1 -5
- contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +2 -2
- contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +1 -4
- contentctl/actions/doc_gen.py +9 -5
- contentctl/actions/initialize.py +45 -33
- contentctl/actions/inspect.py +118 -61
- contentctl/actions/new_content.py +155 -108
- contentctl/actions/release_notes.py +276 -146
- contentctl/actions/reporting.py +23 -19
- contentctl/actions/test.py +33 -28
- contentctl/actions/validate.py +55 -34
- contentctl/api.py +54 -45
- contentctl/contentctl.py +124 -90
- contentctl/enrichments/attack_enrichment.py +112 -72
- contentctl/enrichments/cve_enrichment.py +34 -28
- contentctl/enrichments/splunk_app_enrichment.py +38 -36
- contentctl/helper/link_validator.py +101 -78
- contentctl/helper/splunk_app.py +69 -41
- contentctl/helper/utils.py +58 -53
- contentctl/input/director.py +68 -36
- contentctl/input/new_content_questions.py +27 -35
- contentctl/input/yml_reader.py +28 -18
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +303 -259
- contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +115 -52
- contentctl/objects/alert_action.py +10 -9
- contentctl/objects/annotated_types.py +1 -1
- contentctl/objects/atomic.py +65 -54
- contentctl/objects/base_test.py +5 -3
- contentctl/objects/base_test_result.py +19 -11
- contentctl/objects/baseline.py +62 -30
- contentctl/objects/baseline_tags.py +30 -24
- contentctl/objects/config.py +790 -597
- contentctl/objects/constants.py +33 -56
- contentctl/objects/correlation_search.py +150 -136
- contentctl/objects/dashboard.py +55 -41
- contentctl/objects/data_source.py +16 -17
- contentctl/objects/deployment.py +43 -44
- contentctl/objects/deployment_email.py +3 -2
- contentctl/objects/deployment_notable.py +4 -2
- contentctl/objects/deployment_phantom.py +7 -6
- contentctl/objects/deployment_rba.py +3 -2
- contentctl/objects/deployment_scheduling.py +3 -2
- contentctl/objects/deployment_slack.py +3 -2
- contentctl/objects/detection.py +5 -2
- contentctl/objects/detection_metadata.py +1 -0
- contentctl/objects/detection_stanza.py +7 -2
- contentctl/objects/detection_tags.py +58 -103
- contentctl/objects/drilldown.py +66 -34
- contentctl/objects/enums.py +81 -100
- contentctl/objects/errors.py +16 -24
- contentctl/objects/integration_test.py +3 -3
- contentctl/objects/integration_test_result.py +1 -0
- contentctl/objects/investigation.py +59 -36
- contentctl/objects/investigation_tags.py +30 -19
- contentctl/objects/lookup.py +304 -101
- contentctl/objects/macro.py +55 -39
- contentctl/objects/manual_test.py +3 -3
- contentctl/objects/manual_test_result.py +1 -0
- contentctl/objects/mitre_attack_enrichment.py +17 -16
- contentctl/objects/notable_action.py +2 -1
- contentctl/objects/notable_event.py +1 -3
- contentctl/objects/playbook.py +37 -35
- contentctl/objects/playbook_tags.py +23 -13
- contentctl/objects/rba.py +96 -0
- contentctl/objects/risk_analysis_action.py +15 -11
- contentctl/objects/risk_event.py +110 -160
- contentctl/objects/risk_object.py +1 -0
- contentctl/objects/savedsearches_conf.py +9 -7
- contentctl/objects/security_content_object.py +5 -2
- contentctl/objects/story.py +54 -49
- contentctl/objects/story_tags.py +56 -45
- contentctl/objects/test_attack_data.py +2 -1
- contentctl/objects/test_group.py +5 -2
- contentctl/objects/threat_object.py +1 -0
- contentctl/objects/throttling.py +27 -18
- contentctl/objects/unit_test.py +3 -4
- contentctl/objects/unit_test_baseline.py +5 -5
- contentctl/objects/unit_test_result.py +6 -6
- contentctl/output/api_json_output.py +233 -220
- contentctl/output/attack_nav_output.py +21 -21
- contentctl/output/attack_nav_writer.py +29 -37
- contentctl/output/conf_output.py +235 -172
- contentctl/output/conf_writer.py +201 -125
- contentctl/output/data_source_writer.py +38 -26
- contentctl/output/doc_md_output.py +53 -27
- contentctl/output/jinja_writer.py +19 -15
- contentctl/output/json_writer.py +21 -11
- contentctl/output/svg_output.py +56 -38
- contentctl/output/templates/analyticstories_detections.j2 +2 -2
- contentctl/output/templates/analyticstories_stories.j2 +1 -1
- contentctl/output/templates/collections.j2 +1 -1
- contentctl/output/templates/doc_detections.j2 +0 -5
- contentctl/output/templates/es_investigations_investigations.j2 +1 -1
- contentctl/output/templates/es_investigations_stories.j2 +1 -1
- contentctl/output/templates/savedsearches_baselines.j2 +2 -2
- contentctl/output/templates/savedsearches_detections.j2 +10 -11
- contentctl/output/templates/savedsearches_investigations.j2 +2 -2
- contentctl/output/templates/transforms.j2 +6 -8
- contentctl/output/yml_writer.py +29 -20
- contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +16 -34
- contentctl/templates/stories/cobalt_strike.yml +1 -0
- {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/METADATA +5 -4
- contentctl-5.0.0.dist-info/RECORD +168 -0
- {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/WHEEL +1 -1
- contentctl/actions/initialize_old.py +0 -245
- contentctl/objects/event_source.py +0 -11
- contentctl/objects/observable.py +0 -37
- 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/RECORD +0 -173
- {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/LICENSE.md +0 -0
- {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -3,9 +3,8 @@ from contentctl.objects.enums import DataSource
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class NewContentQuestions:
|
|
6
|
-
|
|
7
6
|
@classmethod
|
|
8
|
-
def get_questions_detection(cls) -> list[dict[str,Any]]:
|
|
7
|
+
def get_questions_detection(cls) -> list[dict[str, Any]]:
|
|
9
8
|
questions = [
|
|
10
9
|
{
|
|
11
10
|
"type": "text",
|
|
@@ -14,22 +13,16 @@ class NewContentQuestions:
|
|
|
14
13
|
"default": "Powershell Encoded Command",
|
|
15
14
|
},
|
|
16
15
|
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'cloud',
|
|
23
|
-
'application',
|
|
24
|
-
'network',
|
|
25
|
-
'web'
|
|
26
|
-
],
|
|
27
|
-
'default': 'endpoint'
|
|
16
|
+
"type": "select",
|
|
17
|
+
"message": "what kind of detection is this",
|
|
18
|
+
"name": "detection_kind",
|
|
19
|
+
"choices": ["endpoint", "cloud", "application", "network", "web"],
|
|
20
|
+
"default": "endpoint",
|
|
28
21
|
},
|
|
29
22
|
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
"type": "text",
|
|
24
|
+
"message": "enter author name",
|
|
25
|
+
"name": "detection_author",
|
|
33
26
|
},
|
|
34
27
|
{
|
|
35
28
|
"type": "select",
|
|
@@ -46,18 +39,17 @@ class NewContentQuestions:
|
|
|
46
39
|
"default": "TTP",
|
|
47
40
|
},
|
|
48
41
|
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#In the future, we should dynamically populate this from the DataSource Objects we have parsed from the data_sources directory
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
"type": "checkbox",
|
|
43
|
+
"message": "Your data source",
|
|
44
|
+
"name": "data_sources",
|
|
45
|
+
# In the future, we should dynamically populate this from the DataSource Objects we have parsed from the data_sources directory
|
|
46
|
+
"choices": sorted(DataSource._value2member_map_),
|
|
55
47
|
},
|
|
56
48
|
{
|
|
57
49
|
"type": "text",
|
|
58
50
|
"message": "enter search (spl)",
|
|
59
51
|
"name": "detection_search",
|
|
60
|
-
"default": "|
|
|
52
|
+
"default": "| __UPDATE__ SPL",
|
|
61
53
|
},
|
|
62
54
|
{
|
|
63
55
|
"type": "text",
|
|
@@ -66,24 +58,24 @@ class NewContentQuestions:
|
|
|
66
58
|
"default": "T1003.002",
|
|
67
59
|
},
|
|
68
60
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
"type": "select",
|
|
62
|
+
"message": "security_domain for detection",
|
|
63
|
+
"name": "security_domain",
|
|
64
|
+
"choices": [
|
|
65
|
+
"access",
|
|
66
|
+
"endpoint",
|
|
67
|
+
"network",
|
|
68
|
+
"threat",
|
|
69
|
+
"identity",
|
|
70
|
+
"audit",
|
|
79
71
|
],
|
|
80
|
-
|
|
72
|
+
"default": "endpoint",
|
|
81
73
|
},
|
|
82
74
|
]
|
|
83
75
|
return questions
|
|
84
76
|
|
|
85
77
|
@classmethod
|
|
86
|
-
def get_questions_story(cls)-> list[dict[str,Any]]:
|
|
78
|
+
def get_questions_story(cls) -> list[dict[str, Any]]:
|
|
87
79
|
questions = [
|
|
88
80
|
{
|
|
89
81
|
"type": "text",
|
contentctl/input/yml_reader.py
CHANGED
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
|
-
|
|
3
2
|
import yaml
|
|
4
|
-
|
|
5
|
-
|
|
6
3
|
import sys
|
|
7
4
|
import pathlib
|
|
8
5
|
|
|
9
|
-
class YmlReader():
|
|
10
6
|
|
|
7
|
+
class YmlReader:
|
|
11
8
|
@staticmethod
|
|
12
|
-
def load_file(
|
|
9
|
+
def load_file(
|
|
10
|
+
file_path: pathlib.Path,
|
|
11
|
+
add_fields: bool = True,
|
|
12
|
+
STRICT_YML_CHECKING: bool = False,
|
|
13
|
+
) -> Dict[str, Any]:
|
|
13
14
|
try:
|
|
14
|
-
file_handler = open(file_path,
|
|
15
|
-
|
|
16
|
-
# The following code can help diagnose issues with duplicate keys or
|
|
15
|
+
file_handler = open(file_path, "r", encoding="utf-8")
|
|
16
|
+
|
|
17
|
+
# The following code can help diagnose issues with duplicate keys or
|
|
17
18
|
# poorly-formatted but still "compliant" YML. This code should be
|
|
18
|
-
# enabled manually for debugging purposes. As such, strictyaml
|
|
19
|
+
# enabled manually for debugging purposes. As such, strictyaml
|
|
19
20
|
# library is intentionally excluded from the contentctl requirements
|
|
20
21
|
|
|
21
22
|
if STRICT_YML_CHECKING:
|
|
22
23
|
import strictyaml
|
|
24
|
+
|
|
23
25
|
try:
|
|
24
|
-
strictyaml.dirty_load(file_handler.read(), allow_flow_style
|
|
26
|
+
strictyaml.dirty_load(file_handler.read(), allow_flow_style=True)
|
|
25
27
|
file_handler.seek(0)
|
|
26
28
|
except Exception as e:
|
|
27
29
|
print(f"Error loading YML file {file_path}: {str(e)}")
|
|
28
30
|
sys.exit(1)
|
|
29
31
|
try:
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
+
# Ideally we should use
|
|
33
|
+
# from contentctl.actions.new_content import NewContent
|
|
34
|
+
# and use NewContent.UPDATE_PREFIX,
|
|
35
|
+
# but there is a circular dependency right now which makes that difficult.
|
|
36
|
+
# We have instead hardcoded UPDATE_PREFIX
|
|
37
|
+
UPDATE_PREFIX = "__UPDATE__"
|
|
38
|
+
data = file_handler.read()
|
|
39
|
+
if UPDATE_PREFIX in data:
|
|
40
|
+
raise Exception(
|
|
41
|
+
f"The file {file_path} contains the value '{UPDATE_PREFIX}'. Please fill out any unpopulated fields as required."
|
|
42
|
+
)
|
|
43
|
+
yml_obj = yaml.load(data, Loader=yaml.CSafeLoader)
|
|
32
44
|
except yaml.YAMLError as exc:
|
|
33
45
|
print(exc)
|
|
34
46
|
sys.exit(1)
|
|
@@ -36,12 +48,10 @@ class YmlReader():
|
|
|
36
48
|
except OSError as exc:
|
|
37
49
|
print(exc)
|
|
38
50
|
sys.exit(1)
|
|
39
|
-
|
|
40
|
-
if add_fields
|
|
51
|
+
|
|
52
|
+
if add_fields is False:
|
|
41
53
|
return yml_obj
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
yml_obj['file_path'] = str(file_path)
|
|
45
|
-
|
|
54
|
+
|
|
55
|
+
yml_obj["file_path"] = str(file_path)
|
|
46
56
|
|
|
47
57
|
return yml_obj
|