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.
Files changed (123) hide show
  1. contentctl/__init__.py +1 -1
  2. contentctl/actions/build.py +102 -57
  3. contentctl/actions/deploy_acs.py +29 -24
  4. contentctl/actions/detection_testing/DetectionTestingManager.py +66 -42
  5. contentctl/actions/detection_testing/GitService.py +134 -76
  6. contentctl/actions/detection_testing/generate_detection_coverage_badge.py +48 -30
  7. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +192 -147
  8. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +45 -32
  9. contentctl/actions/detection_testing/progress_bar.py +9 -6
  10. contentctl/actions/detection_testing/views/DetectionTestingView.py +16 -19
  11. contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +1 -5
  12. contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +2 -2
  13. contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +1 -4
  14. contentctl/actions/doc_gen.py +9 -5
  15. contentctl/actions/initialize.py +45 -33
  16. contentctl/actions/inspect.py +118 -61
  17. contentctl/actions/new_content.py +155 -108
  18. contentctl/actions/release_notes.py +276 -146
  19. contentctl/actions/reporting.py +23 -19
  20. contentctl/actions/test.py +33 -28
  21. contentctl/actions/validate.py +55 -34
  22. contentctl/api.py +54 -45
  23. contentctl/contentctl.py +124 -90
  24. contentctl/enrichments/attack_enrichment.py +112 -72
  25. contentctl/enrichments/cve_enrichment.py +34 -28
  26. contentctl/enrichments/splunk_app_enrichment.py +38 -36
  27. contentctl/helper/link_validator.py +101 -78
  28. contentctl/helper/splunk_app.py +69 -41
  29. contentctl/helper/utils.py +58 -53
  30. contentctl/input/director.py +68 -36
  31. contentctl/input/new_content_questions.py +27 -35
  32. contentctl/input/yml_reader.py +28 -18
  33. contentctl/objects/abstract_security_content_objects/detection_abstract.py +303 -259
  34. contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +115 -52
  35. contentctl/objects/alert_action.py +10 -9
  36. contentctl/objects/annotated_types.py +1 -1
  37. contentctl/objects/atomic.py +65 -54
  38. contentctl/objects/base_test.py +5 -3
  39. contentctl/objects/base_test_result.py +19 -11
  40. contentctl/objects/baseline.py +62 -30
  41. contentctl/objects/baseline_tags.py +30 -24
  42. contentctl/objects/config.py +790 -597
  43. contentctl/objects/constants.py +33 -56
  44. contentctl/objects/correlation_search.py +150 -136
  45. contentctl/objects/dashboard.py +55 -41
  46. contentctl/objects/data_source.py +16 -17
  47. contentctl/objects/deployment.py +43 -44
  48. contentctl/objects/deployment_email.py +3 -2
  49. contentctl/objects/deployment_notable.py +4 -2
  50. contentctl/objects/deployment_phantom.py +7 -6
  51. contentctl/objects/deployment_rba.py +3 -2
  52. contentctl/objects/deployment_scheduling.py +3 -2
  53. contentctl/objects/deployment_slack.py +3 -2
  54. contentctl/objects/detection.py +5 -2
  55. contentctl/objects/detection_metadata.py +1 -0
  56. contentctl/objects/detection_stanza.py +7 -2
  57. contentctl/objects/detection_tags.py +58 -103
  58. contentctl/objects/drilldown.py +66 -34
  59. contentctl/objects/enums.py +81 -100
  60. contentctl/objects/errors.py +16 -24
  61. contentctl/objects/integration_test.py +3 -3
  62. contentctl/objects/integration_test_result.py +1 -0
  63. contentctl/objects/investigation.py +59 -36
  64. contentctl/objects/investigation_tags.py +30 -19
  65. contentctl/objects/lookup.py +304 -101
  66. contentctl/objects/macro.py +55 -39
  67. contentctl/objects/manual_test.py +3 -3
  68. contentctl/objects/manual_test_result.py +1 -0
  69. contentctl/objects/mitre_attack_enrichment.py +17 -16
  70. contentctl/objects/notable_action.py +2 -1
  71. contentctl/objects/notable_event.py +1 -3
  72. contentctl/objects/playbook.py +37 -35
  73. contentctl/objects/playbook_tags.py +23 -13
  74. contentctl/objects/rba.py +96 -0
  75. contentctl/objects/risk_analysis_action.py +15 -11
  76. contentctl/objects/risk_event.py +110 -160
  77. contentctl/objects/risk_object.py +1 -0
  78. contentctl/objects/savedsearches_conf.py +9 -7
  79. contentctl/objects/security_content_object.py +5 -2
  80. contentctl/objects/story.py +54 -49
  81. contentctl/objects/story_tags.py +56 -45
  82. contentctl/objects/test_attack_data.py +2 -1
  83. contentctl/objects/test_group.py +5 -2
  84. contentctl/objects/threat_object.py +1 -0
  85. contentctl/objects/throttling.py +27 -18
  86. contentctl/objects/unit_test.py +3 -4
  87. contentctl/objects/unit_test_baseline.py +5 -5
  88. contentctl/objects/unit_test_result.py +6 -6
  89. contentctl/output/api_json_output.py +233 -220
  90. contentctl/output/attack_nav_output.py +21 -21
  91. contentctl/output/attack_nav_writer.py +29 -37
  92. contentctl/output/conf_output.py +235 -172
  93. contentctl/output/conf_writer.py +201 -125
  94. contentctl/output/data_source_writer.py +38 -26
  95. contentctl/output/doc_md_output.py +53 -27
  96. contentctl/output/jinja_writer.py +19 -15
  97. contentctl/output/json_writer.py +21 -11
  98. contentctl/output/svg_output.py +56 -38
  99. contentctl/output/templates/analyticstories_detections.j2 +2 -2
  100. contentctl/output/templates/analyticstories_stories.j2 +1 -1
  101. contentctl/output/templates/collections.j2 +1 -1
  102. contentctl/output/templates/doc_detections.j2 +0 -5
  103. contentctl/output/templates/es_investigations_investigations.j2 +1 -1
  104. contentctl/output/templates/es_investigations_stories.j2 +1 -1
  105. contentctl/output/templates/savedsearches_baselines.j2 +2 -2
  106. contentctl/output/templates/savedsearches_detections.j2 +10 -11
  107. contentctl/output/templates/savedsearches_investigations.j2 +2 -2
  108. contentctl/output/templates/transforms.j2 +6 -8
  109. contentctl/output/yml_writer.py +29 -20
  110. contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +16 -34
  111. contentctl/templates/stories/cobalt_strike.yml +1 -0
  112. {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/METADATA +5 -4
  113. contentctl-5.0.0.dist-info/RECORD +168 -0
  114. {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/WHEEL +1 -1
  115. contentctl/actions/initialize_old.py +0 -245
  116. contentctl/objects/event_source.py +0 -11
  117. contentctl/objects/observable.py +0 -37
  118. contentctl/output/detection_writer.py +0 -28
  119. contentctl/output/new_content_yml_output.py +0 -56
  120. contentctl/output/yml_output.py +0 -66
  121. contentctl-4.4.7.dist-info/RECORD +0 -173
  122. {contentctl-4.4.7.dist-info → contentctl-5.0.0.dist-info}/LICENSE.md +0 -0
  123. {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
- 'type': 'select',
18
- 'message': 'what kind of detection is this',
19
- 'name': 'detection_kind',
20
- 'choices': [
21
- 'endpoint',
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
- 'type': 'text',
31
- 'message': 'enter author name',
32
- 'name': 'detection_author',
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
- 'type': 'checkbox',
50
- 'message': 'Your data source',
51
- 'name': 'data_source',
52
- #In the future, we should dynamically populate this from the DataSource Objects we have parsed from the data_sources directory
53
- 'choices': sorted(DataSource._value2member_map_ )
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": "| UPDATE_SPL",
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
- 'type': 'select',
70
- 'message': 'security_domain for detection',
71
- 'name': 'security_domain',
72
- 'choices': [
73
- 'access',
74
- 'endpoint',
75
- 'network',
76
- 'threat',
77
- 'identity',
78
- 'audit'
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
- 'default': 'endpoint'
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",
@@ -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(file_path: pathlib.Path, add_fields=True, STRICT_YML_CHECKING=False) -> Dict[str,Any]:
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, 'r', encoding="utf-8")
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 = True)
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
- #yml_obj = list(yaml.safe_load_all(file_handler))[0]
31
- yml_obj = yaml.load(file_handler, Loader=yaml.CSafeLoader)
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 == False:
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