contentctl 4.3.1__tar.gz → 4.3.2__tar.gz

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 (170) hide show
  1. {contentctl-4.3.1 → contentctl-4.3.2}/PKG-INFO +8 -8
  2. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/enrichments/attack_enrichment.py +24 -11
  3. contentctl-4.3.2/contentctl/objects/mitre_attack_enrichment.py +95 -0
  4. {contentctl-4.3.1 → contentctl-4.3.2}/pyproject.toml +8 -8
  5. contentctl-4.3.1/contentctl/objects/mitre_attack_enrichment.py +0 -32
  6. {contentctl-4.3.1 → contentctl-4.3.2}/LICENSE.md +0 -0
  7. {contentctl-4.3.1 → contentctl-4.3.2}/README.md +0 -0
  8. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/__init__.py +0 -0
  9. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/build.py +0 -0
  10. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/deploy_acs.py +0 -0
  11. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/DetectionTestingManager.py +0 -0
  12. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/GitService.py +0 -0
  13. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/generate_detection_coverage_badge.py +0 -0
  14. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +0 -0
  15. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +0 -0
  16. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py +0 -0
  17. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/progress_bar.py +0 -0
  18. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/views/DetectionTestingView.py +0 -0
  19. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +0 -0
  20. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -0
  21. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +0 -0
  22. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/doc_gen.py +0 -0
  23. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/initialize.py +0 -0
  24. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/initialize_old.py +0 -0
  25. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/inspect.py +0 -0
  26. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/new_content.py +0 -0
  27. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/release_notes.py +0 -0
  28. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/reporting.py +0 -0
  29. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/test.py +0 -0
  30. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/actions/validate.py +0 -0
  31. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/api.py +0 -0
  32. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/contentctl.py +0 -0
  33. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/enrichments/cve_enrichment.py +0 -0
  34. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/enrichments/splunk_app_enrichment.py +0 -0
  35. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/helper/link_validator.py +0 -0
  36. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/helper/logger.py +0 -0
  37. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/helper/splunk_app.py +0 -0
  38. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/helper/utils.py +0 -0
  39. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/input/director.py +0 -0
  40. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/input/new_content_questions.py +0 -0
  41. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/input/yml_reader.py +0 -0
  42. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/abstract_security_content_objects/detection_abstract.py +0 -0
  43. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +0 -0
  44. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/alert_action.py +0 -0
  45. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/atomic.py +0 -0
  46. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/base_test.py +0 -0
  47. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/base_test_result.py +0 -0
  48. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/baseline.py +0 -0
  49. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/baseline_tags.py +0 -0
  50. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/config.py +0 -0
  51. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/constants.py +0 -0
  52. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/correlation_search.py +0 -0
  53. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/data_source.py +0 -0
  54. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment.py +0 -0
  55. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_email.py +0 -0
  56. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_notable.py +0 -0
  57. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_phantom.py +0 -0
  58. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_rba.py +0 -0
  59. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_scheduling.py +0 -0
  60. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/deployment_slack.py +0 -0
  61. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/detection.py +0 -0
  62. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/detection_tags.py +0 -0
  63. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/enums.py +0 -0
  64. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/errors.py +0 -0
  65. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/event_source.py +0 -0
  66. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/integration_test.py +0 -0
  67. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/integration_test_result.py +0 -0
  68. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/investigation.py +0 -0
  69. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/investigation_tags.py +0 -0
  70. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/lookup.py +0 -0
  71. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/macro.py +0 -0
  72. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/notable_action.py +0 -0
  73. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/notable_event.py +0 -0
  74. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/observable.py +0 -0
  75. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/playbook.py +0 -0
  76. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/playbook_tags.py +0 -0
  77. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/risk_analysis_action.py +0 -0
  78. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/risk_event.py +0 -0
  79. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/risk_object.py +0 -0
  80. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/security_content_object.py +0 -0
  81. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/ssa_detection.py +0 -0
  82. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/ssa_detection_tags.py +0 -0
  83. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/story.py +0 -0
  84. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/story_tags.py +0 -0
  85. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/test_group.py +0 -0
  86. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/threat_object.py +0 -0
  87. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test.py +0 -0
  88. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test_attack_data.py +0 -0
  89. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test_baseline.py +0 -0
  90. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test_old.py +0 -0
  91. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test_result.py +0 -0
  92. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/objects/unit_test_ssa.py +0 -0
  93. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/api_json_output.py +0 -0
  94. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/attack_nav_output.py +0 -0
  95. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/attack_nav_writer.py +0 -0
  96. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/conf_output.py +0 -0
  97. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/conf_writer.py +0 -0
  98. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/data_source_writer.py +0 -0
  99. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/detection_writer.py +0 -0
  100. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/doc_md_output.py +0 -0
  101. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/jinja_writer.py +0 -0
  102. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/json_writer.py +0 -0
  103. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/new_content_yml_output.py +0 -0
  104. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/svg_output.py +0 -0
  105. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/analyticstories_detections.j2 +0 -0
  106. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/analyticstories_investigations.j2 +0 -0
  107. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/analyticstories_stories.j2 +0 -0
  108. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/app.conf.j2 +0 -0
  109. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/app.manifest.j2 +0 -0
  110. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/collections.j2 +0 -0
  111. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/content-version.j2 +0 -0
  112. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/detection_count.j2 +0 -0
  113. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/detection_coverage.j2 +0 -0
  114. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_detection_page.j2 +0 -0
  115. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_detections.j2 +0 -0
  116. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_navigation.j2 +0 -0
  117. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_navigation_pages.j2 +0 -0
  118. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_playbooks.j2 +0 -0
  119. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_playbooks_page.j2 +0 -0
  120. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_stories.j2 +0 -0
  121. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/doc_story_page.j2 +0 -0
  122. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/es_investigations_investigations.j2 +0 -0
  123. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/es_investigations_stories.j2 +0 -0
  124. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/finding_report.j2 +0 -0
  125. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/header.j2 +0 -0
  126. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/macros.j2 +0 -0
  127. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/panel.j2 +0 -0
  128. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/savedsearches_baselines.j2 +0 -0
  129. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/savedsearches_detections.j2 +0 -0
  130. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/savedsearches_investigations.j2 +0 -0
  131. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/transforms.j2 +0 -0
  132. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/templates/workflow_actions.j2 +0 -0
  133. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/yml_output.py +0 -0
  134. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/output/yml_writer.py +0 -0
  135. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/README.md +0 -0
  136. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_default.yml +0 -0
  137. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/README/essoc_story_detail.txt +0 -0
  138. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/README/essoc_summary.txt +0 -0
  139. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/README/essoc_usage_dashboard.txt +0 -0
  140. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/README.md +0 -0
  141. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/analytic_stories.conf +0 -0
  142. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/app.conf +0 -0
  143. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/commands.conf +0 -0
  144. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/content-version.conf +0 -0
  145. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/data/ui/nav/default.xml +0 -0
  146. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/data/ui/views/escu_summary.xml +0 -0
  147. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/data/ui/views/feedback.xml +0 -0
  148. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/default/use_case_library.conf +0 -0
  149. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/lookups/mitre_enrichment.csv +0 -0
  150. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/metadata/default.meta +0 -0
  151. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/static/appIcon.png +0 -0
  152. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/static/appIconAlt.png +0 -0
  153. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/static/appIconAlt_2x.png +0 -0
  154. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/app_template/static/appIcon_2x.png +0 -0
  155. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/data_sources/sysmon_eventid_1.yml +0 -0
  156. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/datamodels_cim.conf +0 -0
  157. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/datamodels_custom.conf +0 -0
  158. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/deployments/escu_default_configuration_anomaly.yml +0 -0
  159. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/deployments/escu_default_configuration_baseline.yml +0 -0
  160. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/deployments/escu_default_configuration_correlation.yml +0 -0
  161. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/deployments/escu_default_configuration_hunting.yml +0 -0
  162. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/deployments/escu_default_configuration_ttp.yml +0 -0
  163. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/detections/application/.gitkeep +0 -0
  164. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/detections/cloud/.gitkeep +0 -0
  165. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +0 -0
  166. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/detections/network/.gitkeep +0 -0
  167. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/detections/web/.gitkeep +0 -0
  168. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/macros/security_content_ctime.yml +0 -0
  169. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/macros/security_content_summariesonly.yml +0 -0
  170. {contentctl-4.3.1 → contentctl-4.3.2}/contentctl/templates/stories/cobalt_strike.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: contentctl
3
- Version: 4.3.1
3
+ Version: 4.3.2
4
4
  Summary: Splunk Content Control Tool
5
5
  License: Apache 2.0
6
6
  Author: STRT
@@ -11,20 +11,20 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
14
- Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
15
- Requires-Dist: attackcti (>=0.3.7,<0.5.0)
14
+ Requires-Dist: PyYAML (>=6.0.2,<7.0.0)
15
+ Requires-Dist: attackcti (>=0.4.0,<0.5.0)
16
16
  Requires-Dist: bottle (>=0.12.25,<0.13.0)
17
17
  Requires-Dist: docker (>=7.1.0,<8.0.0)
18
18
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
19
19
  Requires-Dist: pycvesearch (>=1.2,<2.0)
20
- Requires-Dist: pydantic (>=2.7.1,<3.0.0)
21
- Requires-Dist: pygit2 (>=1.14.1,<2.0.0)
20
+ Requires-Dist: pydantic (>=2.8.2,<3.0.0)
21
+ Requires-Dist: pygit2 (>=1.15.1,<2.0.0)
22
22
  Requires-Dist: questionary (>=2.0.1,<3.0.0)
23
- Requires-Dist: requests (>=2.32.2,<2.33.0)
23
+ Requires-Dist: requests (>=2.32.3,<2.33.0)
24
24
  Requires-Dist: semantic-version (>=2.10.0,<3.0.0)
25
25
  Requires-Dist: setuptools (>=69.5.1,<74.0.0)
26
- Requires-Dist: splunk-sdk (>=2.0.1,<3.0.0)
27
- Requires-Dist: tqdm (>=4.66.4,<5.0.0)
26
+ Requires-Dist: splunk-sdk (>=2.0.2,<3.0.0)
27
+ Requires-Dist: tqdm (>=4.66.5,<5.0.0)
28
28
  Requires-Dist: tyro (>=0.8.3,<0.9.0)
29
29
  Requires-Dist: xmltodict (>=0.13.0,<0.14.0)
30
30
  Description-Content-Type: text/markdown
@@ -7,7 +7,7 @@ from attackcti import attack_client
7
7
  import logging
8
8
  from pydantic import BaseModel, Field
9
9
  from dataclasses import field
10
- from typing import Annotated
10
+ from typing import Annotated,Any
11
11
  from contentctl.objects.mitre_attack_enrichment import MitreAttackEnrichment
12
12
  from contentctl.objects.config import validate
13
13
  logging.getLogger('taxii2client').setLevel(logging.CRITICAL)
@@ -33,21 +33,33 @@ class AttackEnrichment(BaseModel):
33
33
  else:
34
34
  raise Exception(f"Error, Unable to find Mitre Enrichment for MitreID {mitre_id}")
35
35
 
36
-
37
- def addMitreID(self, technique:dict, tactics:list[str], groups:list[str])->None:
38
-
36
+ def addMitreIDViaGroupNames(self, technique:dict, tactics:list[str], groupNames:list[str])->None:
39
37
  technique_id = technique['technique_id']
40
38
  technique_obj = technique['technique']
41
39
  tactics.sort()
42
- groups.sort()
43
-
40
+
44
41
  if technique_id in self.data:
45
42
  raise Exception(f"Error, trying to redefine MITRE ID '{technique_id}'")
43
+ self.data[technique_id] = MitreAttackEnrichment(mitre_attack_id=technique_id,
44
+ mitre_attack_technique=technique_obj,
45
+ mitre_attack_tactics=tactics,
46
+ mitre_attack_groups=groupNames,
47
+ mitre_attack_group_objects=[])
48
+
49
+ def addMitreIDViaGroupObjects(self, technique:dict, tactics:list[str], groupObjects:list[dict[str,Any]])->None:
50
+ technique_id = technique['technique_id']
51
+ technique_obj = technique['technique']
52
+ tactics.sort()
46
53
 
54
+ groupNames:list[str] = sorted([group['group'] for group in groupObjects])
55
+
56
+ if technique_id in self.data:
57
+ raise Exception(f"Error, trying to redefine MITRE ID '{technique_id}'")
47
58
  self.data[technique_id] = MitreAttackEnrichment(mitre_attack_id=technique_id,
48
59
  mitre_attack_technique=technique_obj,
49
60
  mitre_attack_tactics=tactics,
50
- mitre_attack_groups=groups)
61
+ mitre_attack_groups=groupNames,
62
+ mitre_attack_group_objects=groupObjects)
51
63
 
52
64
 
53
65
  def get_attack_lookup(self, input_path: str, store_csv: bool = False, force_cached_or_offline: bool = False, skip_enrichment:bool = False) -> dict:
@@ -86,19 +98,20 @@ class AttackEnrichment(BaseModel):
86
98
  progress_percent = ((index+1)/len(all_enterprise_techniques)) * 100
87
99
  if (sys.stdout.isatty() and sys.stdin.isatty() and sys.stderr.isatty()):
88
100
  print(f"\r\t{'MITRE Technique Progress'.rjust(23)}: [{progress_percent:3.0f}%]...", end="", flush=True)
89
- apt_groups = []
101
+ apt_groups:list[dict[str,Any]] = []
90
102
  for relationship in enterprise_relationships:
91
103
  if (relationship['target_object'] == technique['id']) and relationship['source_object'].startswith('intrusion-set'):
92
104
  for group in enterprise_groups:
93
105
  if relationship['source_object'] == group['id']:
94
- apt_groups.append(group['group'])
106
+ apt_groups.append(group)
107
+ #apt_groups.append(group['group'])
95
108
 
96
109
  tactics = []
97
110
  if ('tactic' in technique):
98
111
  for tactic in technique['tactic']:
99
112
  tactics.append(tactic.replace('-',' ').title())
100
113
 
101
- self.addMitreID(technique, tactics, apt_groups)
114
+ self.addMitreIDViaGroupObjects(technique, tactics, apt_groups)
102
115
  attack_lookup[technique['technique_id']] = {'technique': technique['technique'], 'tactics': tactics, 'groups': apt_groups}
103
116
 
104
117
  if store_csv:
@@ -131,7 +144,7 @@ class AttackEnrichment(BaseModel):
131
144
  technique_input = {'technique_id': key , 'technique': attack_lookup[key]['technique'] }
132
145
  tactics_input = attack_lookup[key]['tactics']
133
146
  groups_input = attack_lookup[key]['groups']
134
- self.addMitreID(technique=technique_input, tactics=tactics_input, groups=groups_input)
147
+ self.addMitreIDViaGroupNames(technique=technique_input, tactics=tactics_input, groups=groups_input)
135
148
 
136
149
 
137
150
 
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+ from pydantic import BaseModel, Field, ConfigDict, HttpUrl, field_validator
3
+ from typing import List, Annotated
4
+ from enum import StrEnum
5
+ import datetime
6
+
7
+ class MitreTactics(StrEnum):
8
+ RECONNAISSANCE = "Reconnaissance"
9
+ RESOURCE_DEVELOPMENT = "Resource Development"
10
+ INITIAL_ACCESS = "Initial Access"
11
+ EXECUTION = "Execution"
12
+ PERSISTENCE = "Persistence"
13
+ PRIVILEGE_ESCALATION = "Privilege Escalation"
14
+ DEFENSE_EVASION = "Defense Evasion"
15
+ CREDENTIAL_ACCESS = "Credential Access"
16
+ DISCOVERY = "Discovery"
17
+ LATERAL_MOVEMENT = "Lateral Movement"
18
+ COLLECTION = "Collection"
19
+ COMMAND_AND_CONTROL = "Command And Control"
20
+ EXFILTRATION = "Exfiltration"
21
+ IMPACT = "Impact"
22
+
23
+
24
+ class AttackGroupMatrix(StrEnum):
25
+ enterprise_attack = "enterprise-attack"
26
+ ics_attack = "ics-attack"
27
+ mobile_attack = "mobile-attack"
28
+
29
+
30
+ class AttackGroupType(StrEnum):
31
+ intrusion_set = "intrusion-set"
32
+
33
+ class MitreExternalReference(BaseModel):
34
+ model_config = ConfigDict(extra='forbid')
35
+ source_name: str
36
+ external_id: None | str = None
37
+ url: None | HttpUrl = None
38
+ description: None | str = None
39
+
40
+
41
+ class MitreAttackGroup(BaseModel):
42
+ model_config = ConfigDict(extra='forbid')
43
+ contributors: list[str] = []
44
+ created: datetime.datetime
45
+ created_by_ref: str
46
+ external_references: list[MitreExternalReference]
47
+ group: str
48
+ group_aliases: list[str]
49
+ group_description: str
50
+ group_id: str
51
+ id: str
52
+ matrix: list[AttackGroupMatrix]
53
+ mitre_attack_spec_version: None | str
54
+ mitre_version: str
55
+ #assume that if the deprecated field is not present, then the group is not deprecated
56
+ mitre_deprecated: bool
57
+ modified: datetime.datetime
58
+ modified_by_ref: str
59
+ object_marking_refs: list[str]
60
+ type: AttackGroupType
61
+ url: HttpUrl
62
+
63
+
64
+ @field_validator("mitre_deprecated", mode="before")
65
+ def standardize_mitre_deprecated(cls, mitre_deprecated:bool | None) -> bool:
66
+ '''
67
+ For some reason, the API will return either a bool for mitre_deprecated OR
68
+ None. We simplify our typing by converting None to False, and assuming that
69
+ if deprecated is None, then the group is not deprecated.
70
+ '''
71
+ if mitre_deprecated is None:
72
+ return False
73
+ return mitre_deprecated
74
+
75
+ @field_validator("contributors", mode="before")
76
+ def standardize_contributors(cls, contributors:list[str] | None) -> list[str]:
77
+ '''
78
+ For some reason, the API will return either a list of strings for contributors OR
79
+ None. We simplify our typing by converting None to an empty list.
80
+ '''
81
+ if contributors is None:
82
+ return []
83
+ return contributors
84
+
85
+ class MitreAttackEnrichment(BaseModel):
86
+ ConfigDict(use_enum_values=True)
87
+ mitre_attack_id: Annotated[str, Field(pattern=r"^T\d{4}(.\d{3})?$")] = Field(...)
88
+ mitre_attack_technique: str = Field(...)
89
+ mitre_attack_tactics: List[MitreTactics] = Field(...)
90
+ mitre_attack_groups: List[str] = Field(...)
91
+ #Exclude this field from serialization - it is very large and not useful in JSON objects
92
+ mitre_attack_group_objects: list[MitreAttackGroup] = Field(..., exclude=True)
93
+ def __hash__(self) -> int:
94
+ return id(self)
95
+
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "contentctl"
3
- version = "4.3.1"
3
+ version = "4.3.2"
4
4
  description = "Splunk Content Control Tool"
5
5
  authors = ["STRT <research@splunk.com>"]
6
6
  license = "Apache 2.0"
@@ -11,20 +11,20 @@ contentctl = 'contentctl.contentctl:main'
11
11
 
12
12
  [tool.poetry.dependencies]
13
13
  python = "^3.11"
14
- pydantic = "^2.7.1"
15
- PyYAML = "^6.0.1"
16
- requests = "~2.32.2"
14
+ pydantic = "^2.8.2"
15
+ PyYAML = "^6.0.2"
16
+ requests = "~2.32.3"
17
17
  pycvesearch = "^1.2"
18
18
  xmltodict = "^0.13.0"
19
- attackcti = ">=0.3.7,<0.5.0"
19
+ attackcti = "^0.4.0"
20
20
  Jinja2 = "^3.1.4"
21
21
  questionary = "^2.0.1"
22
22
  docker = "^7.1.0"
23
- splunk-sdk = "^2.0.1"
23
+ splunk-sdk = "^2.0.2"
24
24
  semantic-version = "^2.10.0"
25
25
  bottle = "^0.12.25"
26
- tqdm = "^4.66.4"
27
- pygit2 = "^1.14.1"
26
+ tqdm = "^4.66.5"
27
+ pygit2 = "^1.15.1"
28
28
  tyro = "^0.8.3"
29
29
  gitpython = "^3.1.43"
30
30
  setuptools = ">=69.5.1,<74.0.0"
@@ -1,32 +0,0 @@
1
- from __future__ import annotations
2
- from pydantic import BaseModel, Field, ConfigDict
3
- from typing import List, Annotated
4
- from enum import StrEnum
5
-
6
-
7
- class MitreTactics(StrEnum):
8
- RECONNAISSANCE = "Reconnaissance"
9
- RESOURCE_DEVELOPMENT = "Resource Development"
10
- INITIAL_ACCESS = "Initial Access"
11
- EXECUTION = "Execution"
12
- PERSISTENCE = "Persistence"
13
- PRIVILEGE_ESCALATION = "Privilege Escalation"
14
- DEFENSE_EVASION = "Defense Evasion"
15
- CREDENTIAL_ACCESS = "Credential Access"
16
- DISCOVERY = "Discovery"
17
- LATERAL_MOVEMENT = "Lateral Movement"
18
- COLLECTION = "Collection"
19
- COMMAND_AND_CONTROL = "Command And Control"
20
- EXFILTRATION = "Exfiltration"
21
- IMPACT = "Impact"
22
-
23
-
24
- class MitreAttackEnrichment(BaseModel):
25
- ConfigDict(use_enum_values=True)
26
- mitre_attack_id: Annotated[str, Field(pattern=r"^T\d{4}(.\d{3})?$")] = Field(...)
27
- mitre_attack_technique: str = Field(...)
28
- mitre_attack_tactics: List[MitreTactics] = Field(...)
29
- mitre_attack_groups: List[str] = Field(...)
30
-
31
- def __hash__(self) -> int:
32
- return id(self)
File without changes
File without changes
File without changes