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.
Files changed (142) hide show
  1. contentctl/actions/build.py +89 -0
  2. contentctl/actions/detection_testing/DetectionTestingManager.py +48 -49
  3. contentctl/actions/detection_testing/GitService.py +148 -230
  4. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +14 -24
  5. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +43 -17
  6. contentctl/actions/detection_testing/views/DetectionTestingView.py +3 -2
  7. contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -8
  8. contentctl/actions/doc_gen.py +1 -1
  9. contentctl/actions/initialize.py +28 -65
  10. contentctl/actions/inspect.py +260 -0
  11. contentctl/actions/new_content.py +106 -13
  12. contentctl/actions/release_notes.py +168 -144
  13. contentctl/actions/reporting.py +24 -13
  14. contentctl/actions/test.py +39 -20
  15. contentctl/actions/validate.py +25 -48
  16. contentctl/contentctl.py +196 -754
  17. contentctl/enrichments/attack_enrichment.py +69 -19
  18. contentctl/enrichments/cve_enrichment.py +28 -13
  19. contentctl/helper/link_validator.py +24 -26
  20. contentctl/helper/utils.py +7 -3
  21. contentctl/input/director.py +139 -201
  22. contentctl/input/new_content_questions.py +63 -61
  23. contentctl/input/sigma_converter.py +1 -2
  24. contentctl/input/ssa_detection_builder.py +16 -7
  25. contentctl/input/yml_reader.py +4 -3
  26. contentctl/objects/abstract_security_content_objects/detection_abstract.py +487 -154
  27. contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +155 -51
  28. contentctl/objects/alert_action.py +40 -0
  29. contentctl/objects/atomic.py +212 -0
  30. contentctl/objects/baseline.py +44 -43
  31. contentctl/objects/baseline_tags.py +69 -20
  32. contentctl/objects/config.py +857 -125
  33. contentctl/objects/constants.py +0 -1
  34. contentctl/objects/correlation_search.py +1 -1
  35. contentctl/objects/data_source.py +2 -4
  36. contentctl/objects/deployment.py +61 -21
  37. contentctl/objects/deployment_email.py +2 -2
  38. contentctl/objects/deployment_notable.py +4 -4
  39. contentctl/objects/deployment_phantom.py +2 -2
  40. contentctl/objects/deployment_rba.py +3 -4
  41. contentctl/objects/deployment_scheduling.py +2 -3
  42. contentctl/objects/deployment_slack.py +2 -2
  43. contentctl/objects/detection.py +1 -5
  44. contentctl/objects/detection_tags.py +210 -119
  45. contentctl/objects/enums.py +312 -24
  46. contentctl/objects/integration_test.py +1 -1
  47. contentctl/objects/integration_test_result.py +0 -2
  48. contentctl/objects/investigation.py +62 -53
  49. contentctl/objects/investigation_tags.py +30 -6
  50. contentctl/objects/lookup.py +80 -31
  51. contentctl/objects/macro.py +29 -45
  52. contentctl/objects/mitre_attack_enrichment.py +29 -5
  53. contentctl/objects/observable.py +3 -7
  54. contentctl/objects/playbook.py +60 -30
  55. contentctl/objects/playbook_tags.py +45 -8
  56. contentctl/objects/security_content_object.py +1 -5
  57. contentctl/objects/ssa_detection.py +8 -4
  58. contentctl/objects/ssa_detection_tags.py +19 -26
  59. contentctl/objects/story.py +142 -44
  60. contentctl/objects/story_tags.py +46 -33
  61. contentctl/objects/unit_test.py +7 -2
  62. contentctl/objects/unit_test_attack_data.py +10 -19
  63. contentctl/objects/unit_test_baseline.py +1 -1
  64. contentctl/objects/unit_test_old.py +4 -3
  65. contentctl/objects/unit_test_result.py +5 -3
  66. contentctl/objects/unit_test_ssa.py +31 -0
  67. contentctl/output/api_json_output.py +202 -130
  68. contentctl/output/attack_nav_output.py +20 -9
  69. contentctl/output/attack_nav_writer.py +3 -3
  70. contentctl/output/ba_yml_output.py +3 -3
  71. contentctl/output/conf_output.py +125 -391
  72. contentctl/output/conf_writer.py +169 -31
  73. contentctl/output/jinja_writer.py +2 -2
  74. contentctl/output/json_writer.py +17 -5
  75. contentctl/output/new_content_yml_output.py +8 -7
  76. contentctl/output/svg_output.py +17 -27
  77. contentctl/output/templates/analyticstories_detections.j2 +8 -4
  78. contentctl/output/templates/analyticstories_investigations.j2 +1 -1
  79. contentctl/output/templates/analyticstories_stories.j2 +6 -6
  80. contentctl/output/templates/app.conf.j2 +2 -2
  81. contentctl/output/templates/app.manifest.j2 +2 -2
  82. contentctl/output/templates/detection_coverage.j2 +6 -8
  83. contentctl/output/templates/doc_detection_page.j2 +2 -2
  84. contentctl/output/templates/doc_detections.j2 +2 -2
  85. contentctl/output/templates/doc_stories.j2 +1 -1
  86. contentctl/output/templates/es_investigations_investigations.j2 +1 -1
  87. contentctl/output/templates/es_investigations_stories.j2 +1 -1
  88. contentctl/output/templates/header.j2 +2 -1
  89. contentctl/output/templates/macros.j2 +6 -10
  90. contentctl/output/templates/savedsearches_baselines.j2 +5 -5
  91. contentctl/output/templates/savedsearches_detections.j2 +36 -33
  92. contentctl/output/templates/savedsearches_investigations.j2 +4 -4
  93. contentctl/output/templates/transforms.j2 +4 -4
  94. contentctl/output/yml_writer.py +2 -2
  95. contentctl/templates/app_template/README.md +7 -0
  96. contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/nav/default.xml +1 -0
  97. contentctl/templates/app_template/lookups/mitre_enrichment.csv +638 -0
  98. contentctl/templates/deployments/{00_default_anomaly.yml → escu_default_configuration_anomaly.yml} +1 -2
  99. contentctl/templates/deployments/{00_default_baseline.yml → escu_default_configuration_baseline.yml} +1 -2
  100. contentctl/templates/deployments/{00_default_correlation.yml → escu_default_configuration_correlation.yml} +2 -2
  101. contentctl/templates/deployments/{00_default_hunting.yml → escu_default_configuration_hunting.yml} +2 -2
  102. contentctl/templates/deployments/{00_default_ttp.yml → escu_default_configuration_ttp.yml} +1 -2
  103. contentctl/templates/detections/anomalous_usage_of_7zip.yml +0 -1
  104. contentctl/templates/stories/cobalt_strike.yml +0 -1
  105. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/METADATA +36 -15
  106. contentctl-4.0.2.dist-info/RECORD +168 -0
  107. contentctl/actions/detection_testing/DataManipulation.py +0 -149
  108. contentctl/actions/generate.py +0 -91
  109. contentctl/helper/config_handler.py +0 -75
  110. contentctl/input/baseline_builder.py +0 -66
  111. contentctl/input/basic_builder.py +0 -58
  112. contentctl/input/detection_builder.py +0 -370
  113. contentctl/input/investigation_builder.py +0 -42
  114. contentctl/input/new_content_generator.py +0 -95
  115. contentctl/input/playbook_builder.py +0 -68
  116. contentctl/input/story_builder.py +0 -106
  117. contentctl/objects/app.py +0 -214
  118. contentctl/objects/repo_config.py +0 -163
  119. contentctl/objects/test_config.py +0 -630
  120. contentctl/output/templates/macros_detections.j2 +0 -7
  121. contentctl/output/templates/splunk_app/README.md +0 -7
  122. contentctl-3.6.0.dist-info/RECORD +0 -176
  123. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_story_detail.txt +0 -0
  124. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_summary.txt +0 -0
  125. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_usage_dashboard.txt +0 -0
  126. /contentctl/{output/templates/splunk_app → templates/app_template}/default/analytic_stories.conf +0 -0
  127. /contentctl/{output/templates/splunk_app → templates/app_template}/default/app.conf +0 -0
  128. /contentctl/{output/templates/splunk_app → templates/app_template}/default/commands.conf +0 -0
  129. /contentctl/{output/templates/splunk_app → templates/app_template}/default/content-version.conf +0 -0
  130. /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/escu_summary.xml +0 -0
  131. /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/feedback.xml +0 -0
  132. /contentctl/{output/templates/splunk_app → templates/app_template}/default/distsearch.conf +0 -0
  133. /contentctl/{output/templates/splunk_app → templates/app_template}/default/usage_searches.conf +0 -0
  134. /contentctl/{output/templates/splunk_app → templates/app_template}/default/use_case_library.conf +0 -0
  135. /contentctl/{output/templates/splunk_app → templates/app_template}/metadata/default.meta +0 -0
  136. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon.png +0 -0
  137. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt.png +0 -0
  138. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt_2x.png +0 -0
  139. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon_2x.png +0 -0
  140. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/LICENSE.md +0 -0
  141. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/WHEEL +0 -0
  142. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/entry_points.txt +0 -0
@@ -1,14 +1,12 @@
1
1
  from dataclasses import dataclass
2
+ from typing import List
2
3
 
3
- from contentctl.objects.test_config import TestConfig
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 argparse import Namespace
36
- from os.path import relpath
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
- test_director_output_dto: DirectorOutputDto
44
- gitService: GitService
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
- testContent=input_dto.test_director_output_dto,
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.test_director_output_dto.detections) == 0:
73
- print(f"With Detection Testing Mode '{input_dto.config.mode.value}', there were detections [{len(input_dto.test_director_output_dto.detections)}] found to test.\nAs such, we will quit immediately.")
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.mode.value}] - Test [{len(input_dto.test_director_output_dto.detections)}] detections")
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([relpath(detection.file_path) for detection in input_dto.test_director_output_dto.detections])
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()
@@ -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
- DirectorInputDto,
12
- DirectorOutputDto,
12
+ DirectorOutputDto
13
13
  )
14
14
 
15
-
16
- @dataclass(frozen=True)
17
- class ValidateInputDto:
18
- director_input_dto: DirectorInputDto
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: ValidateInputDto) -> None:
23
- director_output_dto = DirectorOutputDto([], [], [], [], [], [], [], [], [])
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.director_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 None
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
- content_with_duplicate_uuid = [
61
- content_object
62
- for content_object in security_content_objects
63
- if content_object.id in duplicate_uuids
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([obj.name for obj in content_with_duplicate_uuid])
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
- # )