contentctl 5.5.7__tar.gz → 5.5.9__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-5.5.7 → contentctl-5.5.9}/PKG-INFO +5 -4
  2. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/deploy_acs.py +5 -3
  3. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/DetectionTestingManager.py +3 -3
  4. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/GitService.py +4 -4
  5. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/generate_detection_coverage_badge.py +3 -3
  6. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +15 -17
  7. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +9 -8
  8. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/progress_bar.py +2 -1
  9. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +4 -3
  10. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +4 -2
  11. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +7 -7
  12. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/doc_gen.py +1 -2
  13. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/release_notes.py +2 -2
  14. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/reporting.py +3 -3
  15. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/test.py +2 -3
  16. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/validate.py +1 -1
  17. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/api.py +7 -6
  18. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/contentctl.py +1 -1
  19. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/enrichments/attack_enrichment.py +1 -1
  20. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/enrichments/cve_enrichment.py +9 -6
  21. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/enrichments/splunk_app_enrichment.py +5 -4
  22. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/helper/link_validator.py +7 -7
  23. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/helper/splunk_app.py +6 -6
  24. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/helper/utils.py +8 -8
  25. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/input/director.py +3 -2
  26. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/input/new_content_questions.py +1 -0
  27. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/input/yml_reader.py +2 -2
  28. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/abstract_security_content_objects/detection_abstract.py +1 -1
  29. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/alert_action.py +4 -2
  30. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/atomic.py +8 -5
  31. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/base_test_result.py +2 -2
  32. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/baseline_tags.py +7 -6
  33. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/config.py +5 -5
  34. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/correlation_search.py +156 -139
  35. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/dashboard.py +1 -1
  36. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_email.py +1 -0
  37. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_notable.py +3 -1
  38. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_phantom.py +1 -0
  39. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_rba.py +1 -0
  40. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_scheduling.py +1 -0
  41. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment_slack.py +1 -0
  42. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/investigation_tags.py +6 -3
  43. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/playbook_tags.py +7 -2
  44. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/story_tags.py +1 -1
  45. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/throttling.py +2 -1
  46. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/unit_test_baseline.py +2 -1
  47. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/unit_test_result.py +4 -2
  48. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/conf_output.py +2 -2
  49. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/conf_writer.py +5 -5
  50. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/doc_md_output.py +0 -1
  51. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/jinja_writer.py +1 -0
  52. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/json_writer.py +1 -1
  53. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/yml_writer.py +3 -2
  54. {contentctl-5.5.7 → contentctl-5.5.9}/pyproject.toml +33 -33
  55. {contentctl-5.5.7 → contentctl-5.5.9}/LICENSE.md +0 -0
  56. {contentctl-5.5.7 → contentctl-5.5.9}/README.md +0 -0
  57. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/__init__.py +0 -0
  58. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/build.py +0 -0
  59. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py +0 -0
  60. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/detection_testing/views/DetectionTestingView.py +0 -0
  61. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/initialize.py +0 -0
  62. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/inspect.py +0 -0
  63. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/actions/new_content.py +0 -0
  64. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/helper/logger.py +0 -0
  65. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +0 -0
  66. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/annotated_types.py +0 -0
  67. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/base_security_event.py +0 -0
  68. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/base_test.py +1 -1
  69. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/baseline.py +0 -0
  70. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/constants.py +0 -0
  71. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/content_versioning_service.py +0 -0
  72. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/data_source.py +0 -0
  73. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/deployment.py +0 -0
  74. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/detection.py +0 -0
  75. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/detection_metadata.py +0 -0
  76. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/detection_stanza.py +1 -1
  77. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/detection_tags.py +0 -0
  78. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/drilldown.py +0 -0
  79. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/enums.py +0 -0
  80. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/errors.py +0 -0
  81. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/integration_test.py +2 -2
  82. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/integration_test_result.py +0 -0
  83. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/investigation.py +0 -0
  84. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/lookup.py +0 -0
  85. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/macro.py +0 -0
  86. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/manual_test.py +2 -2
  87. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/manual_test_result.py +0 -0
  88. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/mitre_attack_enrichment.py +0 -0
  89. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/notable_action.py +0 -0
  90. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/notable_event.py +0 -0
  91. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/playbook.py +0 -0
  92. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/rba.py +0 -0
  93. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/removed_security_content_object.py +0 -0
  94. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/risk_analysis_action.py +1 -1
  95. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/risk_event.py +0 -0
  96. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/risk_object.py +0 -0
  97. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/savedsearches_conf.py +3 -3
  98. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/security_content_object.py +0 -0
  99. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/story.py +0 -0
  100. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/test_attack_data.py +0 -0
  101. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/test_group.py +2 -2
  102. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/threat_object.py +0 -0
  103. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/objects/unit_test.py +2 -2
  104. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/api_json_output.py +0 -0
  105. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/attack_nav_output.py +0 -0
  106. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/attack_nav_writer.py +0 -0
  107. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/runtime_csv_writer.py +0 -0
  108. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/svg_output.py +0 -0
  109. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/analyticstories_detections.j2 +0 -0
  110. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/analyticstories_investigations.j2 +0 -0
  111. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/analyticstories_stories.j2 +0 -0
  112. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/app.conf.j2 +0 -0
  113. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/app.manifest.j2 +0 -0
  114. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/collections.j2 +0 -0
  115. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/content-version.j2 +0 -0
  116. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/detection_count.j2 +0 -0
  117. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/detection_coverage.j2 +0 -0
  118. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_detection_page.j2 +0 -0
  119. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_detections.j2 +0 -0
  120. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_navigation.j2 +0 -0
  121. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_navigation_pages.j2 +0 -0
  122. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_playbooks.j2 +0 -0
  123. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_playbooks_page.j2 +0 -0
  124. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_stories.j2 +0 -0
  125. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/doc_story_page.j2 +0 -0
  126. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/es_investigations_investigations.j2 +0 -0
  127. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/es_investigations_stories.j2 +0 -0
  128. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/header.j2 +0 -0
  129. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/macros.j2 +0 -0
  130. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/panel.j2 +0 -0
  131. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/savedsearches_baselines.j2 +0 -0
  132. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/savedsearches_detections.j2 +0 -0
  133. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/savedsearches_investigations.j2 +0 -0
  134. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/server.conf.j2 +0 -0
  135. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/transforms.j2 +0 -0
  136. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/output/templates/workflow_actions.j2 +0 -0
  137. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/README.md +0 -0
  138. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_default.yml +0 -0
  139. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/README/essoc_story_detail.txt +0 -0
  140. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/README/essoc_summary.txt +0 -0
  141. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/README/essoc_usage_dashboard.txt +0 -0
  142. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/README.md +0 -0
  143. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/analytic_stories.conf +0 -0
  144. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/commands.conf +0 -0
  145. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/data/ui/nav/default.xml +0 -0
  146. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/data/ui/views/escu_summary.xml +0 -0
  147. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/data/ui/views/feedback.xml +0 -0
  148. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/default/use_case_library.conf +0 -0
  149. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/lookups/mitre_enrichment.csv +0 -0
  150. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/metadata/default.meta +0 -0
  151. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/static/appIcon.png +0 -0
  152. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/static/appIconAlt.png +0 -0
  153. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/static/appIconAlt_2x.png +0 -0
  154. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/app_template/static/appIcon_2x.png +0 -0
  155. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/data_sources/sysmon_eventid_1.yml +0 -0
  156. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/datamodels_cim.conf +0 -0
  157. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/datamodels_custom.conf +0 -0
  158. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/deployments/escu_default_configuration_anomaly.yml +0 -0
  159. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/deployments/escu_default_configuration_baseline.yml +0 -0
  160. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/deployments/escu_default_configuration_correlation.yml +0 -0
  161. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/deployments/escu_default_configuration_hunting.yml +0 -0
  162. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/deployments/escu_default_configuration_ttp.yml +0 -0
  163. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/detections/application/.gitkeep +0 -0
  164. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/detections/cloud/.gitkeep +0 -0
  165. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +0 -0
  166. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/detections/network/.gitkeep +0 -0
  167. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/detections/web/.gitkeep +0 -0
  168. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/macros/security_content_ctime.yml +0 -0
  169. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/macros/security_content_summariesonly.yml +0 -0
  170. {contentctl-5.5.7 → contentctl-5.5.9}/contentctl/templates/stories/cobalt_strike.yml +0 -0
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: contentctl
3
- Version: 5.5.7
3
+ Version: 5.5.9
4
4
  Summary: Splunk Content Control Tool
5
5
  License: Apache 2.0
6
+ License-File: LICENSE.md
6
7
  Author: STRT
7
8
  Author-email: research@splunk.com
8
9
  Requires-Python: >=3.11,<3.14
@@ -21,10 +22,10 @@ Requires-Dist: pycvesearch (>=1.2,<2.0)
21
22
  Requires-Dist: pydantic (>=2.9.2,<2.10.0)
22
23
  Requires-Dist: pygit2 (>=1.15.1,<2.0.0)
23
24
  Requires-Dist: questionary (>=2.0.1,<3.0.0)
24
- Requires-Dist: requests (>=2.32.3,<2.33.0)
25
+ Requires-Dist: requests (>=2.32.4)
25
26
  Requires-Dist: rich (>=14.0.0,<15.0.0)
26
27
  Requires-Dist: semantic-version (>=2.10.0,<3.0.0)
27
- Requires-Dist: setuptools (>=69.5.1,<81.0.0)
28
+ Requires-Dist: setuptools (>=80.9.0)
28
29
  Requires-Dist: splunk-sdk (>=2.0.2,<3.0.0)
29
30
  Requires-Dist: tqdm (>=4.66.5,<5.0.0)
30
31
  Requires-Dist: tyro (>=0.9.2,<0.9.23)
@@ -1,7 +1,9 @@
1
- from contentctl.objects.config import deploy_acs, StackType
2
- from requests import post
3
1
  import pprint
4
2
 
3
+ from requests import post
4
+
5
+ from contentctl.objects.config import StackType, deploy_acs
6
+
5
7
 
6
8
  class Deploy:
7
9
  def execute(self, config: deploy_acs, appinspect_token: str) -> None:
@@ -33,7 +35,7 @@ class Deploy:
33
35
  raise Exception(f"Unsupported stack type: '{config.stack_type}'")
34
36
  except Exception as e:
35
37
  raise Exception(
36
- f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{str(e)}"
38
+ f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{e!s}"
37
39
  )
38
40
 
39
41
  try:
@@ -162,10 +162,10 @@ class DetectionTestingManager(BaseModel):
162
162
  print()
163
163
  print(f"[{error_type}]:")
164
164
  for error in errors[error_type]:
165
- print(f"\t❌ {str(error)}")
165
+ print(f"\t❌ {error!s}")
166
166
  if isinstance(error, ExceptionGroup):
167
167
  for suberror in error.exceptions: # type: ignore
168
- print(f"\t\t❌ {str(suberror)}") # type: ignore
168
+ print(f"\t\t❌ {suberror!s}") # type: ignore
169
169
  print()
170
170
 
171
171
  return self.output_dto
@@ -208,7 +208,7 @@ class DetectionTestingManager(BaseModel):
208
208
  except Exception as e:
209
209
  raise Exception(
210
210
  "Failed to pull docker container image "
211
- f"[{self.input_dto.config.container_settings.full_image_path}]: {str(e)}"
211
+ f"[{self.input_dto.config.container_settings.full_image_path}]: {e!s}"
212
212
  )
213
213
 
214
214
  already_staged_container_files = False
@@ -99,7 +99,7 @@ class GitService(BaseModel):
99
99
  updated_detections.add(detectionObject)
100
100
  else:
101
101
  raise Exception(
102
- f"Error getting detection object for file {str(decoded_path)}"
102
+ f"Error getting detection object for file {decoded_path!s}"
103
103
  )
104
104
 
105
105
  elif (
@@ -111,7 +111,7 @@ class GitService(BaseModel):
111
111
  updated_macros.add(macroObject)
112
112
  else:
113
113
  raise Exception(
114
- f"Error getting macro object for file {str(decoded_path)}"
114
+ f"Error getting macro object for file {decoded_path!s}"
115
115
  )
116
116
 
117
117
  elif (
@@ -125,7 +125,7 @@ class GitService(BaseModel):
125
125
  updated_datasources.add(datasourceObject)
126
126
  else:
127
127
  raise Exception(
128
- f"Error getting data source object for file {str(decoded_path)}"
128
+ f"Error getting data source object for file {decoded_path!s}"
129
129
  )
130
130
 
131
131
  elif decoded_path.is_relative_to(self.config.path / "lookups"):
@@ -172,7 +172,7 @@ class GitService(BaseModel):
172
172
 
173
173
  else:
174
174
  raise Exception(
175
- f"Detected a changed file in the lookups/ directory '{str(decoded_path)}'.\n"
175
+ f"Detected a changed file in the lookups/ directory '{decoded_path!s}'.\n"
176
176
  "Only files ending in .csv, .yml, or .mlmodel are supported in this "
177
177
  "directory. This file must be removed from the lookups/ directory."
178
178
  )
@@ -47,13 +47,13 @@ parser.add_argument(
47
47
  try:
48
48
  results = parser.parse_args()
49
49
  except Exception as e:
50
- print(f"Error parsing arguments: {str(e)}")
50
+ print(f"Error parsing arguments: {e!s}")
51
51
  exit(1)
52
52
 
53
53
  try:
54
54
  summary_info = json.loads(results.input_summary_file.read())
55
55
  except Exception as e:
56
- print(f"Error loading {results.input_summary_file.name} JSON file: {str(e)}")
56
+ print(f"Error loading {results.input_summary_file.name} JSON file: {e!s}")
57
57
  sys.exit(1)
58
58
 
59
59
  if "summary" not in summary_info:
@@ -75,7 +75,7 @@ try:
75
75
  RAW_BADGE_SVG.format(results.badge_string, "{:2.1f}%".format(pass_percent))
76
76
  )
77
77
  except Exception as e:
78
- print(f"Error generating badge: {str(e)}")
78
+ print(f"Error generating badge: {e!s}")
79
79
  sys.exit(1)
80
80
 
81
81
 
@@ -197,7 +197,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
197
197
  self.check_for_teardown()
198
198
 
199
199
  except Exception as e:
200
- msg = f"[{self.get_name()}]: {str(e)}"
200
+ msg = f"[{self.get_name()}]: {e!s}"
201
201
  self.finish()
202
202
  if isinstance(e, ExceptionGroup):
203
203
  raise ExceptionGroup(msg, e.exceptions) from e # type: ignore
@@ -310,7 +310,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
310
310
  return
311
311
 
312
312
  except Exception as e:
313
- raise (Exception(f"Failure creating HEC Endpoint: {str(e)}"))
313
+ raise (Exception(f"Failure creating HEC Endpoint: {e!s}"))
314
314
 
315
315
  def get_all_indexes(self) -> None:
316
316
  """
@@ -327,7 +327,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
327
327
  # Retrieve all available indexes on the splunk instance
328
328
  self.all_indexes_on_server = indexes
329
329
  except Exception as e:
330
- raise (Exception(f"Failure getting indexes: {str(e)}"))
330
+ raise (Exception(f"Failure getting indexes: {e!s}"))
331
331
 
332
332
  def get_conn(self) -> client.Service:
333
333
  try:
@@ -382,7 +382,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
382
382
  pass
383
383
  except Exception as e:
384
384
  self.pbar.write(
385
- f"Error getting API connection (not quitting) '{type(e).__name__}': {str(e)}"
385
+ f"Error getting API connection (not quitting) '{type(e).__name__}': {e!s}"
386
386
  )
387
387
 
388
388
  for _ in range(sleep_seconds):
@@ -402,7 +402,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
402
402
  pass
403
403
  else:
404
404
  raise Exception(
405
- f"Error creating index {self.sync_obj.replay_index} - {str(e)}"
405
+ f"Error creating index {self.sync_obj.replay_index} - {e!s}"
406
406
  )
407
407
 
408
408
  def configure_imported_roles(
@@ -426,7 +426,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
426
426
  )
427
427
  return
428
428
  except Exception as e:
429
- msg = f"Error configuring roles: {str(e)}"
429
+ msg = f"Error configuring roles: {e!s}"
430
430
  self.pbar.write(msg)
431
431
  raise Exception(msg) from e
432
432
 
@@ -436,7 +436,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
436
436
  self.get_conn().post(endpoint, value=";".join(self.all_indexes_on_server))
437
437
  except Exception as e:
438
438
  self.pbar.write(
439
- f"Error configuring deleteIndexesAllowed with '{self.all_indexes_on_server}': [{str(e)}]"
439
+ f"Error configuring deleteIndexesAllowed with '{self.all_indexes_on_server}': [{e!s}]"
440
440
  )
441
441
 
442
442
  def wait_for_conf_file(self, app_name: str, conf_file_name: str):
@@ -474,12 +474,12 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
474
474
  parser.read(custom_acceleration_datamodels)
475
475
  if len(parser.keys()) > 1:
476
476
  self.pbar.write(
477
- f"Read {len(parser) - 1} custom datamodels from {str(custom_acceleration_datamodels)}!"
477
+ f"Read {len(parser) - 1} custom datamodels from {custom_acceleration_datamodels!s}!"
478
478
  )
479
479
 
480
480
  if not cim_acceleration_datamodels.is_file():
481
481
  self.pbar.write(
482
- f"******************************\nDATAMODEL ACCELERATION FILE {str(cim_acceleration_datamodels)} NOT "
482
+ f"******************************\nDATAMODEL ACCELERATION FILE {cim_acceleration_datamodels!s} NOT "
483
483
  "FOUND. CIM DATAMODELS NOT ACCELERATED\n******************************\n"
484
484
  )
485
485
  else:
@@ -499,7 +499,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
499
499
 
500
500
  except Exception as e:
501
501
  self.pbar.write(
502
- f"Error creating the conf Datamodel {datamodel_name} key/value {name}/{value}: {str(e)}"
502
+ f"Error creating the conf Datamodel {datamodel_name} key/value {name}/{value}: {e!s}"
503
503
  )
504
504
 
505
505
  def execute(self):
@@ -528,9 +528,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
528
528
  self.finish()
529
529
  return
530
530
  except Exception as e:
531
- self.pbar.write(
532
- f"Error testing detection: {type(e).__name__}: {str(e)}"
533
- )
531
+ self.pbar.write(f"Error testing detection: {type(e).__name__}: {e!s}")
534
532
  raise e
535
533
  finally:
536
534
  self.sync_obj.outputQueue.append(detection)
@@ -1365,7 +1363,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
1365
1363
  except Exception as e:
1366
1364
  raise (
1367
1365
  Exception(
1368
- f"Trouble deleting data using the search {splunk_search}: {str(e)}"
1366
+ f"Trouble deleting data using the search {splunk_search}: {e!s}"
1369
1367
  )
1370
1368
  )
1371
1369
 
@@ -1440,7 +1438,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
1440
1438
  except Exception as e:
1441
1439
  raise (
1442
1440
  Exception(
1443
- f"Could not download attack data file [{attack_data_file.data}]:{str(e)}"
1441
+ f"Could not download attack data file [{attack_data_file.data}]:{e!s}"
1444
1442
  )
1445
1443
  )
1446
1444
 
@@ -1514,7 +1512,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
1514
1512
  except Exception as e:
1515
1513
  raise (
1516
1514
  Exception(
1517
- f"There was an exception sending attack_data to HEC: {str(e)}"
1515
+ f"There was an exception sending attack_data to HEC: {e!s}"
1518
1516
  )
1519
1517
  )
1520
1518
 
@@ -1558,7 +1556,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
1558
1556
  )
1559
1557
  )
1560
1558
  except Exception as e:
1561
- raise (Exception(f"There was an exception in the post: {str(e)}"))
1559
+ raise (Exception(f"There was an exception in the post: {e!s}"))
1562
1560
 
1563
1561
  def status(self):
1564
1562
  pass
@@ -1,11 +1,12 @@
1
+ import docker
2
+ import docker.models.containers
3
+ import docker.models.resource
4
+ import docker.types
5
+
1
6
  from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructure import (
2
7
  DetectionTestingInfrastructure,
3
8
  )
4
9
  from contentctl.objects.config import test
5
- import docker.models.resource
6
- import docker.models.containers
7
- import docker
8
- import docker.types
9
10
 
10
11
 
11
12
  class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
@@ -34,7 +35,7 @@ class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
34
35
  self.removeContainer()
35
36
  pass
36
37
  except Exception as e:
37
- raise (Exception(f"Error removing container: {str(e)}"))
38
+ raise (Exception(f"Error removing container: {e!s}"))
38
39
  super().finish()
39
40
 
40
41
  def get_name(self) -> str:
@@ -46,7 +47,7 @@ class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
46
47
 
47
48
  return c
48
49
  except Exception as e:
49
- raise (Exception(f"Failed to get docker client: {str(e)}"))
50
+ raise (Exception(f"Failed to get docker client: {e!s}"))
50
51
 
51
52
  def check_for_teardown(self):
52
53
  try:
@@ -56,7 +57,7 @@ class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
56
57
  except Exception as e:
57
58
  if self.sync_obj.terminate is not True:
58
59
  self.pbar.write(
59
- f"Error: could not get container [{self.get_name()}]: {str(e)}"
60
+ f"Error: could not get container [{self.get_name()}]: {e!s}"
60
61
  )
61
62
  self.sync_obj.terminate = True
62
63
  else:
@@ -175,6 +176,6 @@ class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
175
176
  except Exception as e:
176
177
  raise (
177
178
  Exception(
178
- f"Could not remove Docker Container [{self.get_name()}]: {str(e)}"
179
+ f"Could not remove Docker Container [{self.get_name()}]: {e!s}"
179
180
  )
180
181
  )
@@ -1,7 +1,8 @@
1
+ import datetime
1
2
  import time
2
3
  from enum import StrEnum
4
+
3
5
  from tqdm import tqdm
4
- import datetime
5
6
 
6
7
 
7
8
  class TestReportingType(StrEnum):
@@ -1,10 +1,11 @@
1
+ import time
2
+
3
+ import tqdm
4
+
1
5
  from contentctl.actions.detection_testing.views.DetectionTestingView import (
2
6
  DetectionTestingView,
3
7
  )
4
8
 
5
- import time
6
- import tqdm
7
-
8
9
 
9
10
  class DetectionTestingViewCLI(DetectionTestingView, arbitrary_types_allowed=True):
10
11
  pbar: tqdm.tqdm = None
@@ -1,8 +1,10 @@
1
+ import pathlib
2
+
3
+ import yaml
4
+
1
5
  from contentctl.actions.detection_testing.views.DetectionTestingView import (
2
6
  DetectionTestingView,
3
7
  )
4
- import pathlib
5
- import yaml
6
8
 
7
9
  OUTPUT_FOLDER = "test_results"
8
10
  OUTPUT_FILENAME = "summary.yml"
@@ -1,9 +1,9 @@
1
+ import webbrowser
1
2
  from threading import Thread
3
+ from wsgiref.simple_server import WSGIRequestHandler, make_server
2
4
 
3
- from bottle import template, Bottle, ServerAdapter
4
- from wsgiref.simple_server import make_server, WSGIRequestHandler
5
5
  import jinja2
6
- import webbrowser
6
+ from bottle import Bottle, ServerAdapter, template
7
7
  from pydantic import ConfigDict
8
8
 
9
9
  from contentctl.actions.detection_testing.views.DetectionTestingView import (
@@ -39,8 +39,8 @@ $(document).ready(function () {
39
39
  {% for containerName, data in currentTestingQueue.items() %}
40
40
  <tr>
41
41
  <td>{{ containerName }}</td>
42
- <td>{{ data["name"] }}</td>
43
- <td>{{ data["search"] }}</td>
42
+ <td>{{ data["name"] }}</td>
43
+ <td>{{ data["search"] }}</td>
44
44
  </tr>
45
45
  {% endfor %}
46
46
  </tbody>
@@ -69,7 +69,7 @@ $(document).ready(function () {
69
69
  {% else %}
70
70
  <td style="font-weight: bold;background-color: #ff9999"><b>False</b></td>
71
71
  {% endif %}
72
-
72
+
73
73
  </tr>
74
74
  {% endfor %}
75
75
  {% endfor %}
@@ -118,7 +118,7 @@ class DetectionTestingViewWeb(DetectionTestingView):
118
118
  try:
119
119
  webbrowser.open(f"http://{self.server.host}:{DEFAULT_WEB_UI_PORT}")
120
120
  except Exception as e:
121
- print(f"Could not open webbrowser for status page: {str(e)}")
121
+ print(f"Could not open webbrowser for status page: {e!s}")
122
122
 
123
123
  def stop(self):
124
124
  if self.server.server is None:
@@ -1,8 +1,7 @@
1
1
  import os
2
-
3
2
  from dataclasses import dataclass
4
3
 
5
- from contentctl.input.director import DirectorInputDto, Director, DirectorOutputDto
4
+ from contentctl.input.director import Director, DirectorInputDto, DirectorOutputDto
6
5
  from contentctl.output.doc_md_output import DocMdOutput
7
6
 
8
7
 
@@ -46,7 +46,7 @@ class ReleaseNotes:
46
46
  file_path
47
47
  ).replace(str(repo_path), "")
48
48
  playbook_link = playbook_link.replace(
49
- ".yml", "/"
49
+ "yml", "/"
50
50
  ).lower()
51
51
  updates.append(
52
52
  "- "
@@ -155,7 +155,7 @@ class ReleaseNotes:
155
155
 
156
156
  except yaml.YAMLError as exc:
157
157
  raise Exception(
158
- f"Error parsing YAML file for release_notes {file_path}: {str(exc)}"
158
+ f"Error parsing YAML file for release_notes {file_path}: {exc!s}"
159
159
  )
160
160
  else:
161
161
  warnings.append(
@@ -1,9 +1,9 @@
1
1
  from dataclasses import dataclass
2
2
 
3
3
  from contentctl.input.director import DirectorOutputDto
4
- from contentctl.output.svg_output import SvgOutput
5
- from contentctl.output.attack_nav_output import AttackNavOutput
6
4
  from contentctl.objects.config import report
5
+ from contentctl.output.attack_nav_output import AttackNavOutput
6
+ from contentctl.output.svg_output import SvgOutput
7
7
 
8
8
 
9
9
  @dataclass(frozen=True)
@@ -24,7 +24,7 @@ class Reporting:
24
24
  )
25
25
  else:
26
26
  raise Exception(
27
- f"Error writing reporting : '{input_dto.config.getReportingPath()}': {str(e)}"
27
+ f"Error writing reporting : '{input_dto.config.getReportingPath()}': {e!s}"
28
28
  )
29
29
 
30
30
  print("Creating GitHub Badges...")
@@ -18,9 +18,8 @@ from contentctl.actions.detection_testing.views.DetectionTestingViewFile import
18
18
  from contentctl.actions.detection_testing.views.DetectionTestingViewWeb import (
19
19
  DetectionTestingViewWeb,
20
20
  )
21
- from contentctl.objects.config import Changes, Selected
21
+ from contentctl.objects.config import Changes, Selected, test_servers
22
22
  from contentctl.objects.config import test as test_
23
- from contentctl.objects.config import test_servers
24
23
  from contentctl.objects.detection import Detection
25
24
  from contentctl.objects.integration_test import IntegrationTest
26
25
 
@@ -151,5 +150,5 @@ class Test:
151
150
  return summary.get("success", False)
152
151
 
153
152
  except Exception as e:
154
- print(f"Error determining if whole test was successful: {str(e)}")
153
+ print(f"Error determining if whole test was successful: {e!s}")
155
154
  return False
@@ -118,7 +118,7 @@ class Validate:
118
118
  )
119
119
  except Exception as e:
120
120
  errors.append(
121
- f"Error processing checking version of TA {supported_TA.name}: {str(e)}"
121
+ f"Error processing checking version of TA {supported_TA.name}: {e!s}"
122
122
  )
123
123
 
124
124
  if len(errors) > 0:
@@ -1,9 +1,10 @@
1
1
  from pathlib import Path
2
- from typing import Any, Union, Type
2
+ from typing import Any, Type, Union
3
+
4
+ from contentctl.input.director import DirectorOutputDto
3
5
  from contentctl.input.yml_reader import YmlReader
4
- from contentctl.objects.config import test_common, test, test_servers
6
+ from contentctl.objects.config import test, test_common, test_servers
5
7
  from contentctl.objects.security_content_object import SecurityContentObject
6
- from contentctl.input.director import DirectorOutputDto
7
8
 
8
9
 
9
10
  def config_from_file(
@@ -36,7 +37,7 @@ def config_from_file(
36
37
 
37
38
  except Exception as e:
38
39
  raise Exception(
39
- f"Failed to load contentctl configuration from file '{path}': {str(e)}"
40
+ f"Failed to load contentctl configuration from file '{path}': {e!s}"
40
41
  )
41
42
 
42
43
  # Apply settings that have been overridden from the ones in the file
@@ -45,7 +46,7 @@ def config_from_file(
45
46
  except Exception as e:
46
47
  raise Exception(
47
48
  f"Failed updating dictionary of values read from file '{path}'"
48
- f" with the dictionary of arguments passed: {str(e)}"
49
+ f" with the dictionary of arguments passed: {e!s}"
49
50
  )
50
51
 
51
52
  # The function below will throw its own descriptive exception if it fails
@@ -78,7 +79,7 @@ def config_from_dict(
78
79
  try:
79
80
  test_object = configType.model_validate(config)
80
81
  except Exception as e:
81
- raise Exception(f"Failed to load contentctl configuration from dict:\n{str(e)}")
82
+ raise Exception(f"Failed to load contentctl configuration from dict:\n{e!s}")
82
83
 
83
84
  return test_object
84
85
 
@@ -206,7 +206,7 @@ def main():
206
206
  config_obj = YmlReader().load_file(configFile, add_fields=False)
207
207
  t = test.model_validate(config_obj)
208
208
  except Exception as e:
209
- print(f"Error validating 'contentctl.yml':\n{str(e)}")
209
+ print(f"Error validating 'contentctl.yml':\n{e!s}")
210
210
  sys.exit(1)
211
211
 
212
212
  # For ease of generating the constructor, we want to allow construction
@@ -183,7 +183,7 @@ class AttackEnrichment(BaseModel):
183
183
  )
184
184
 
185
185
  except Exception as err:
186
- raise Exception(f"Error getting MITRE Enrichment: {str(err)}")
186
+ raise Exception(f"Error getting MITRE Enrichment: {err!s}")
187
187
 
188
188
  print("Done!")
189
189
  return attack_lookup
@@ -1,8 +1,11 @@
1
1
  from __future__ import annotations
2
- from pycvesearch import CVESearch
3
- from typing import Annotated, Union, TYPE_CHECKING
4
- from pydantic import ConfigDict, BaseModel, Field, computed_field
2
+
5
3
  from decimal import Decimal
4
+ from typing import TYPE_CHECKING, Annotated, Union
5
+
6
+ from pycvesearch import CVESearch
7
+ from pydantic import BaseModel, ConfigDict, Field, computed_field
8
+
6
9
  from contentctl.objects.annotated_types import CVE_TYPE
7
10
 
8
11
  if TYPE_CHECKING:
@@ -46,7 +49,7 @@ class CveEnrichment(BaseModel):
46
49
  return CveEnrichment(use_enrichment=True, cve_api_obj=cve_api_obj)
47
50
  except Exception as e:
48
51
  raise Exception(
49
- f"Error setting CVE_SEARCH API to: {CVESSEARCH_API_URL}: {str(e)}"
52
+ f"Error setting CVE_SEARCH API to: {CVESSEARCH_API_URL}: {e!s}"
50
53
  )
51
54
 
52
55
  return CveEnrichment(use_enrichment=False, cve_api_obj=None)
@@ -57,14 +60,14 @@ class CveEnrichment(BaseModel):
57
60
  if not self.use_enrichment:
58
61
  return CveEnrichmentObj(
59
62
  id=cve_id,
60
- cvss=Decimal(5.0),
63
+ cvss=Decimal("5.0"),
61
64
  summary="SUMMARY NOT AVAILABLE! ONLY THE LINK WILL BE USED AT THIS TIME",
62
65
  )
63
66
  else:
64
67
  print("WARNING - Dynamic enrichment not supported at this time.")
65
68
  return CveEnrichmentObj(
66
69
  id=cve_id,
67
- cvss=Decimal(5.0),
70
+ cvss=Decimal("5.0"),
68
71
  summary="SUMMARY NOT AVAILABLE! ONLY THE LINK WILL BE USED AT THIS TIME",
69
72
  )
70
73
  # Depending on needs, we may add dynamic enrichment functionality back to the tool
@@ -1,8 +1,9 @@
1
- import requests
2
- import xmltodict
3
1
  import functools
4
- import shelve
5
2
  import os
3
+ import shelve
4
+
5
+ import requests
6
+ import xmltodict
6
7
 
7
8
  SPLUNKBASE_API_URL = "https://apps.splunk.com/api/apps/entriesbyid/"
8
9
 
@@ -75,7 +76,7 @@ class SplunkAppEnrichment:
75
76
  splunk_app_enriched["url"] = ""
76
77
  except Exception as e:
77
78
  print(
78
- f"There was an unknown error enriching the Splunk TA [{splunk_ta}]: {str(e)}"
79
+ f"There was an unknown error enriching the Splunk TA [{splunk_ta}]: {e!s}"
79
80
  )
80
81
  splunk_app_enriched["name"] = splunk_ta
81
82
  splunk_app_enriched["url"] = ""
@@ -1,13 +1,13 @@
1
- from pydantic import BaseModel, model_validator
2
- from typing import Union, Callable, Any
3
- import requests
4
- import urllib3
5
- import urllib3.exceptions
6
- import time
7
1
  import abc
8
-
9
2
  import os
10
3
  import shelve
4
+ import time
5
+ from typing import Any, Callable, Union
6
+
7
+ import requests
8
+ import urllib3
9
+ import urllib3.exceptions
10
+ from pydantic import BaseModel, model_validator
11
11
 
12
12
  DEFAULT_USER_AGENT_STRING = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36"
13
13
  ALLOWED_HTTP_CODES = [200]