contentctl 5.6.0__tar.gz → 5.6.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 (171) hide show
  1. {contentctl-5.6.0 → contentctl-5.6.2}/PKG-INFO +18 -16
  2. {contentctl-5.6.0 → contentctl-5.6.2}/README.md +4 -2
  3. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/GitService.py +7 -5
  4. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +1 -1
  5. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/enrichments/cve_enrichment.py +19 -0
  6. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/abstract_security_content_objects/detection_abstract.py +1 -1
  7. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/base_test.py +1 -1
  8. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/base_test_result.py +2 -2
  9. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/data_source.py +5 -5
  10. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/drilldown.py +2 -2
  11. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/lookup.py +3 -3
  12. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/mitre_attack_enrichment.py +4 -4
  13. {contentctl-5.6.0 → contentctl-5.6.2}/pyproject.toml +21 -19
  14. {contentctl-5.6.0 → contentctl-5.6.2}/LICENSE.md +0 -0
  15. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/__init__.py +0 -0
  16. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/build.py +0 -0
  17. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/deploy_acs.py +0 -0
  18. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/DetectionTestingManager.py +0 -0
  19. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/generate_detection_coverage_badge.py +0 -0
  20. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +0 -0
  21. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py +0 -0
  22. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/progress_bar.py +0 -0
  23. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/views/DetectionTestingView.py +0 -0
  24. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +0 -0
  25. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -0
  26. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +0 -0
  27. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/doc_gen.py +0 -0
  28. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/initialize.py +0 -0
  29. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/inspect.py +0 -0
  30. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/new_content.py +0 -0
  31. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/release_notes.py +0 -0
  32. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/reporting.py +0 -0
  33. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/test.py +0 -0
  34. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/actions/validate.py +0 -0
  35. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/api.py +0 -0
  36. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/contentctl.py +0 -0
  37. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/enrichments/attack_enrichment.py +0 -0
  38. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/enrichments/splunk_app_enrichment.py +0 -0
  39. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/helper/link_validator.py +0 -0
  40. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/helper/logger.py +0 -0
  41. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/helper/splunk_app.py +0 -0
  42. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/helper/utils.py +0 -0
  43. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/input/director.py +0 -0
  44. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/input/new_content_questions.py +0 -0
  45. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/input/yml_reader.py +0 -0
  46. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +0 -0
  47. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/alert_action.py +0 -0
  48. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/annotated_types.py +0 -0
  49. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/atomic.py +0 -0
  50. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/base_security_event.py +0 -0
  51. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/baseline.py +0 -0
  52. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/baseline_tags.py +0 -0
  53. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/config.py +0 -0
  54. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/constants.py +0 -0
  55. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/content_versioning_service.py +0 -0
  56. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/correlation_search.py +0 -0
  57. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/dashboard.py +0 -0
  58. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment.py +0 -0
  59. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_email.py +0 -0
  60. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_notable.py +0 -0
  61. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_phantom.py +0 -0
  62. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_rba.py +0 -0
  63. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_scheduling.py +0 -0
  64. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/deployment_slack.py +0 -0
  65. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/detection.py +0 -0
  66. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/detection_metadata.py +0 -0
  67. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/detection_stanza.py +0 -0
  68. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/detection_tags.py +0 -0
  69. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/enums.py +0 -0
  70. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/errors.py +0 -0
  71. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/integration_test.py +0 -0
  72. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/integration_test_result.py +0 -0
  73. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/investigation.py +0 -0
  74. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/investigation_tags.py +0 -0
  75. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/macro.py +0 -0
  76. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/manual_test.py +0 -0
  77. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/manual_test_result.py +0 -0
  78. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/notable_action.py +0 -0
  79. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/notable_event.py +0 -0
  80. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/playbook.py +0 -0
  81. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/playbook_tags.py +0 -0
  82. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/rba.py +0 -0
  83. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/removed_security_content_object.py +0 -0
  84. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/risk_analysis_action.py +0 -0
  85. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/risk_event.py +0 -0
  86. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/risk_object.py +0 -0
  87. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/savedsearches_conf.py +0 -0
  88. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/security_content_object.py +0 -0
  89. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/story.py +0 -0
  90. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/story_tags.py +0 -0
  91. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/test_attack_data.py +0 -0
  92. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/test_group.py +0 -0
  93. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/threat_object.py +0 -0
  94. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/throttling.py +0 -0
  95. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/unit_test.py +0 -0
  96. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/unit_test_baseline.py +0 -0
  97. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/objects/unit_test_result.py +0 -0
  98. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/api_json_output.py +0 -0
  99. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/attack_nav_output.py +0 -0
  100. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/attack_nav_writer.py +0 -0
  101. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/conf_output.py +0 -0
  102. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/conf_writer.py +0 -0
  103. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/doc_md_output.py +0 -0
  104. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/jinja_writer.py +0 -0
  105. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/json_writer.py +0 -0
  106. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/runtime_csv_writer.py +0 -0
  107. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/svg_output.py +0 -0
  108. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/analyticstories_detections.j2 +0 -0
  109. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/analyticstories_investigations.j2 +0 -0
  110. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/analyticstories_stories.j2 +0 -0
  111. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/app.conf.j2 +0 -0
  112. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/app.manifest.j2 +0 -0
  113. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/collections.j2 +0 -0
  114. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/content-version.j2 +0 -0
  115. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/detection_count.j2 +0 -0
  116. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/detection_coverage.j2 +0 -0
  117. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_detection_page.j2 +0 -0
  118. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_detections.j2 +0 -0
  119. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_navigation.j2 +0 -0
  120. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_navigation_pages.j2 +0 -0
  121. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_playbooks.j2 +0 -0
  122. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_playbooks_page.j2 +0 -0
  123. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_stories.j2 +0 -0
  124. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/doc_story_page.j2 +0 -0
  125. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/es_investigations_investigations.j2 +0 -0
  126. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/es_investigations_stories.j2 +0 -0
  127. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/header.j2 +0 -0
  128. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/macros.j2 +0 -0
  129. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/panel.j2 +0 -0
  130. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/savedsearches_baselines.j2 +0 -0
  131. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/savedsearches_detections.j2 +0 -0
  132. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/savedsearches_fbds.j2 +0 -0
  133. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/savedsearches_investigations.j2 +0 -0
  134. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/server.conf.j2 +0 -0
  135. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/transforms.j2 +0 -0
  136. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/templates/workflow_actions.j2 +0 -0
  137. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/output/yml_writer.py +0 -0
  138. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/README.md +0 -0
  139. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_default.yml +0 -0
  140. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/README/essoc_story_detail.txt +0 -0
  141. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/README/essoc_summary.txt +0 -0
  142. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/README/essoc_usage_dashboard.txt +0 -0
  143. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/README.md +0 -0
  144. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/analytic_stories.conf +0 -0
  145. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/commands.conf +0 -0
  146. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/data/ui/nav/default.xml +0 -0
  147. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/data/ui/views/escu_summary.xml +0 -0
  148. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/data/ui/views/feedback.xml +0 -0
  149. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/default/use_case_library.conf +0 -0
  150. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/lookups/mitre_enrichment.csv +0 -0
  151. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/metadata/default.meta +0 -0
  152. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/static/appIcon.png +0 -0
  153. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/static/appIconAlt.png +0 -0
  154. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/static/appIconAlt_2x.png +0 -0
  155. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/app_template/static/appIcon_2x.png +0 -0
  156. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/data_sources/sysmon_eventid_1.yml +0 -0
  157. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/datamodels_cim.conf +0 -0
  158. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/datamodels_custom.conf +0 -0
  159. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/deployments/escu_default_configuration_anomaly.yml +0 -0
  160. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/deployments/escu_default_configuration_baseline.yml +0 -0
  161. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/deployments/escu_default_configuration_correlation.yml +0 -0
  162. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/deployments/escu_default_configuration_hunting.yml +0 -0
  163. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/deployments/escu_default_configuration_ttp.yml +0 -0
  164. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/detections/application/.gitkeep +0 -0
  165. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/detections/cloud/.gitkeep +0 -0
  166. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +0 -0
  167. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/detections/network/.gitkeep +0 -0
  168. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/detections/web/.gitkeep +0 -0
  169. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/macros/security_content_ctime.yml +0 -0
  170. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/macros/security_content_summariesonly.yml +0 -0
  171. {contentctl-5.6.0 → contentctl-5.6.2}/contentctl/templates/stories/cobalt_strike.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contentctl
3
- Version: 5.6.0
3
+ Version: 5.6.2
4
4
  Summary: Splunk Content Control Tool
5
5
  License: Apache 2.0
6
6
  License-File: LICENSE.md
@@ -12,32 +12,34 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
- Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
16
- Requires-Dist: PyYAML (>=6.0.2,<7.0.0)
15
+ Requires-Dist: Jinja2 (>=3.1.6,<4.0.0)
16
+ Requires-Dist: PyYAML (>=6.0.3,<7.0.0)
17
17
  Requires-Dist: attackcti (>=0.5.4,<0.6)
18
18
  Requires-Dist: bottle (>=0.12.25,<0.14.0)
19
- Requires-Dist: docker (>=7.1.0,<8.0.0)
20
- Requires-Dist: gitpython (>=3.1.43,<4.0.0)
19
+ Requires-Dist: docker (>=7.2.0,<8.0.0)
20
+ Requires-Dist: gitpython (>=3.1.62,<4.0.0)
21
21
  Requires-Dist: pycvesearch (>=1.2,<2.0)
22
22
  Requires-Dist: pydantic (>=2.9.2,<2.10.0)
23
- Requires-Dist: pygit2 (>=1.15.1,<2.0.0)
24
- Requires-Dist: questionary (>=2.0.1,<3.0.0)
25
- Requires-Dist: requests (>=2.32.4)
26
- Requires-Dist: rich (>=14.0.0,<15.0.0)
23
+ Requires-Dist: pygit2 (>=1.20.1,<2.0.0)
24
+ Requires-Dist: questionary (>=2.1.1,<3.0.0)
25
+ Requires-Dist: requests (>=2.34.2)
26
+ Requires-Dist: rich (>=15.0.0,<16.0.0)
27
27
  Requires-Dist: semantic-version (>=2.10.0,<3.0.0)
28
- Requires-Dist: setuptools (<81)
29
- Requires-Dist: splunk-sdk (>=2.0.2,<3.0.0)
30
- Requires-Dist: tqdm (>=4.66.5,<5.0.0)
31
- Requires-Dist: tyro (>=0.9.2,<0.9.23)
32
- Requires-Dist: xmltodict (>=0.13,<0.15)
28
+ Requires-Dist: setuptools (>=83.0.0,<84)
29
+ Requires-Dist: splunk-sdk (>=2.1.1,<3.0.0)
30
+ Requires-Dist: tqdm (>=4.70.1,<5.0.0)
31
+ Requires-Dist: tyro (>=1.0.16,<2.0.0)
32
+ Requires-Dist: xmltodict (>=1.0.4,<2.0.0)
33
33
  Description-Content-Type: text/markdown
34
34
 
35
35
  # contentctl
36
+ > ## The Future of contentctl
37
+ > We are shifting future investment from contentctl to Detection Studio as we work to bring this functionality into Splunk as an officially supported capability. This repository will remain publicly available for reference, but we are no longer accepting new pull requests or feature requests. Going forward, continued use of contentctl may require customer-managed customization to support specific environments and requirements.
38
+
36
39
  <p align="center">
37
40
  <img src="https://raw.githubusercontent.com/splunk/contentctl/refs/heads/main/docs/contentctl_logo_white.png" title="In case you're wondering, it's a capybara" alt="the logo for the contentctl project, which depicts a doodled 4 legged animal that is supposed to represent a capybara, with the name of the project below it" width="250" height="250"></p>
38
41
 
39
- > [!NOTE]
40
- > Looking to migrate from an earlier release to the new contentctl v5+ ? Check out our migration guide [here](docs/contentctl_v5_migration_guide.md).
42
+
41
43
 
42
44
  ## What is contentctl?
43
45
  `contentctl` is a tool developed by the Splunk Threat Research Team to help with managing the content living in [splunk/security_content](https://github.com/splunk/security_content) and producing the Enterprise Security Content Update app for Splunk. While its development is largely driven by STRT's needs, it has been somewhat genericized and can be used by customers and partners to package their own content. Simply put, `contentctl` is the workhorse that packages detections, macros, lookups, dashboards into a Splunk app that you can use, and that understands the YAML structure and project layout we've selected to keep development clean.
@@ -1,9 +1,11 @@
1
1
  # contentctl
2
+ > ## The Future of contentctl
3
+ > We are shifting future investment from contentctl to Detection Studio as we work to bring this functionality into Splunk as an officially supported capability. This repository will remain publicly available for reference, but we are no longer accepting new pull requests or feature requests. Going forward, continued use of contentctl may require customer-managed customization to support specific environments and requirements.
4
+
2
5
  <p align="center">
3
6
  <img src="https://raw.githubusercontent.com/splunk/contentctl/refs/heads/main/docs/contentctl_logo_white.png" title="In case you're wondering, it's a capybara" alt="the logo for the contentctl project, which depicts a doodled 4 legged animal that is supposed to represent a capybara, with the name of the project below it" width="250" height="250"></p>
4
7
 
5
- > [!NOTE]
6
- > Looking to migrate from an earlier release to the new contentctl v5+ ? Check out our migration guide [here](docs/contentctl_v5_migration_guide.md).
8
+
7
9
 
8
10
  ## What is contentctl?
9
11
  `contentctl` is a tool developed by the Splunk Threat Research Team to help with managing the content living in [splunk/security_content](https://github.com/splunk/security_content) and producing the Enterprise Security Content Update app for Splunk. While its development is largely driven by STRT's needs, it has been somewhat genericized and can be used by customers and partners to package their own content. Simply put, `contentctl` is the workhorse that packages detections, macros, lookups, dashboards into a Splunk app that you can use, and that understands the YAML structure and project layout we've selected to keep development clean.
@@ -147,11 +147,13 @@ class GitService(BaseModel):
147
147
  # Filter to find the Lookup Object the references this CSV
148
148
  matched = list(
149
149
  filter(
150
- lambda x: isinstance(x, CSVLookup)
151
- and not isinstance(
152
- x, RuntimeCSV
153
- ) # RuntimeCSV is not used directly by any content
154
- and x.filename == decoded_path,
150
+ lambda x: (
151
+ isinstance(x, CSVLookup)
152
+ and not isinstance(
153
+ x, RuntimeCSV
154
+ ) # RuntimeCSV is not used directly by any content
155
+ and x.filename == decoded_path
156
+ ),
155
157
  self.director.lookups,
156
158
  )
157
159
  )
@@ -149,7 +149,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
149
149
 
150
150
  # Init the list of setup functions we always need
151
151
  primary_setup_functions: list[
152
- tuple[Callable[[], None | client.Service], str]
152
+ tuple[Callable[[], client.Service | None], str]
153
153
  ] = [
154
154
  (self.start, "Starting"),
155
155
  (self.get_conn, "Waiting for App Installation"),
@@ -1,8 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
4
+ import types
3
5
  from decimal import Decimal
4
6
  from typing import TYPE_CHECKING, Annotated, Union
5
7
 
8
+ try:
9
+ import pkg_resources # noqa: F401
10
+ except ModuleNotFoundError:
11
+ # setuptools >= 82 no longer ships pkg_resources. pycvesearch (unmaintained,
12
+ # no newer release available) still does `import pkg_resources` just to read
13
+ # its own version for a User-Agent header. Stub out the single attribute it
14
+ # needs so the import succeeds, rather than pinning setuptools to a version
15
+ # with a known vulnerability.
16
+ def _get_distribution(name: str) -> types.SimpleNamespace:
17
+ from importlib.metadata import version
18
+
19
+ return types.SimpleNamespace(version=version(name))
20
+
21
+ _pkg_resources_shim = types.ModuleType("pkg_resources")
22
+ _pkg_resources_shim.get_distribution = _get_distribution # type: ignore[attr-defined]
23
+ sys.modules["pkg_resources"] = _pkg_resources_shim
24
+
6
25
  from pycvesearch import CVESearch
7
26
  from pydantic import BaseModel, ConfigDict, Field, computed_field
8
27
 
@@ -117,7 +117,7 @@ class Detection_Abstract(SecurityContentObject):
117
117
  f"Error getting severity - risk_score must be between 0-100, but was actually {self.risk_score}"
118
118
  )
119
119
 
120
- explanation: None | str = Field(
120
+ explanation: str | None = Field(
121
121
  default=None,
122
122
  exclude=True, # Don't serialize this value when dumping the object
123
123
  description="Provide an explanation to be included "
@@ -39,7 +39,7 @@ class BaseTest(BaseModel, ABC):
39
39
  latest_time: Union[str, None] = None
40
40
 
41
41
  # The test result
42
- result: Union[None, BaseTestResult] = None
42
+ result: Union[BaseTestResult, None] = None
43
43
 
44
44
  @abstractmethod
45
45
  def skip(self, message: str) -> None:
@@ -38,7 +38,7 @@ class BaseTestResult(BaseModel):
38
38
  """
39
39
 
40
40
  # Message for the result
41
- message: Union[None, str] = None
41
+ message: Union[str, None] = None
42
42
 
43
43
  # Any exception that was raised (may be None)
44
44
  exception: Union[Exception, None] = None
@@ -53,7 +53,7 @@ class BaseTestResult(BaseModel):
53
53
  job_content: Union[Record, None] = None
54
54
 
55
55
  # The Splunk endpoint URL
56
- sid_link: Union[None, str] = None
56
+ sid_link: Union[str, None] = None
57
57
 
58
58
  # Needed to allow for embedding of Exceptions in the model
59
59
  model_config = ConfigDict(validate_assignment=True, arbitrary_types_allowed=True)
@@ -19,14 +19,14 @@ class DataSource(SecurityContentObject):
19
19
  source: str = Field(...)
20
20
  sourcetype: str = Field(...)
21
21
  separator: Optional[str] = None
22
- separator_value: None | str = None
22
+ separator_value: str | None = None
23
23
  configuration: Optional[str] = None
24
24
  supported_TA: list[TA] = []
25
- fields: None | list = None
26
- field_mappings: None | list = None
25
+ fields: list | None = None
26
+ field_mappings: list | None = None
27
27
  mitre_components: list[str] = []
28
- convert_to_log_source: None | list = None
29
- example_log: None | str = None
28
+ convert_to_log_source: list | None = None
29
+ example_log: str | None = None
30
30
  output_fields: list[str] = []
31
31
  status: ContentStatus = ContentStatus.production
32
32
 
@@ -22,14 +22,14 @@ class Drilldown(BaseModel):
22
22
  description="The text of a drilldown search. This must be valid SPL.",
23
23
  min_length=1,
24
24
  )
25
- earliest_offset: None | str = Field(
25
+ earliest_offset: str | None = Field(
26
26
  ...,
27
27
  description="Earliest offset time for the drilldown search. "
28
28
  f"The most common value for this field is '{EARLIEST_OFFSET}', "
29
29
  "but it is NOT the default value and must be supplied explicitly.",
30
30
  min_length=1,
31
31
  )
32
- latest_offset: None | str = Field(
32
+ latest_offset: str | None = Field(
33
33
  ...,
34
34
  description="Latest offset time for the driolldown search. "
35
35
  f"The most common value for this field is '{LATEST_OFFSET}', "
@@ -100,11 +100,11 @@ class Lookup(SecurityContentObject, abc.ABC):
100
100
  match_type: list[Annotated[str, Field(pattern=r"(^WILDCARD|CIDR)\(.+\)$")]] = Field(
101
101
  default=[]
102
102
  )
103
- min_matches: None | NonNegativeInt = Field(default=None)
104
- max_matches: None | Annotated[NonNegativeInt, Field(ge=1, le=1000)] = Field(
103
+ min_matches: NonNegativeInt | None = Field(default=None)
104
+ max_matches: Annotated[NonNegativeInt, Field(ge=1, le=1000)] | None = Field(
105
105
  default=None
106
106
  )
107
- case_sensitive_match: None | bool = Field(default=None)
107
+ case_sensitive_match: bool | None = Field(default=None)
108
108
  status: ContentStatus = ContentStatus.production
109
109
 
110
110
  @field_validator("status", mode="after")
@@ -41,9 +41,9 @@ class AttackGroupType(StrEnum):
41
41
  class MitreExternalReference(BaseModel):
42
42
  model_config = ConfigDict(extra="forbid")
43
43
  source_name: str
44
- external_id: None | str = None
45
- url: None | HttpUrl = None
46
- description: None | str = None
44
+ external_id: str | None = None
45
+ url: HttpUrl | None = None
46
+ description: str | None = None
47
47
 
48
48
 
49
49
  class MitreAttackGroup(BaseModel):
@@ -58,7 +58,7 @@ class MitreAttackGroup(BaseModel):
58
58
  group_id: str
59
59
  id: str
60
60
  matrix: list[AttackGroupMatrix]
61
- mitre_attack_spec_version: None | str
61
+ mitre_attack_spec_version: str | None
62
62
  mitre_version: str
63
63
  # assume that if the deprecated field is not present, then the group is not deprecated
64
64
  mitre_deprecated: bool
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "contentctl"
3
3
 
4
- version = "5.6.0"
4
+ version = "5.6.2"
5
5
 
6
6
  description = "Splunk Content Control Tool"
7
7
  authors = ["STRT <research@splunk.com>"]
@@ -14,31 +14,33 @@ contentctl = 'contentctl.contentctl:main'
14
14
  [tool.poetry.dependencies]
15
15
 
16
16
  python = "^3.11,<3.14"
17
+ # Pinned to 2.9.2. Newer pydantic versions (tested through 2.13.5) fail to
18
+ # resolve the forward reference from Story to Detection, raising
19
+ # "`Story` is not fully defined; you should define `Detection`, then call
20
+ # `Story.model_rebuild()`." during `contentctl validate`/`build`.
17
21
  pydantic = "~2.9.2"
18
- PyYAML = "^6.0.2"
19
- requests = ">=2.32.4"
22
+ PyYAML = "^6.0.3"
23
+ requests = ">=2.34.2"
20
24
  pycvesearch = "^1.2"
21
- xmltodict = ">=0.13,<0.15"
25
+ xmltodict = "^1.0.4"
26
+ # attackcti 0.6.x requires pydantic>=2.12.5, which conflicts with our
27
+ # pydantic pin above, so this stays capped below 0.6.
22
28
  attackcti = ">=0.5.4,<0.6"
23
- Jinja2 = "^3.1.4"
24
- questionary = "^2.0.1"
25
- docker = "^7.1.0"
26
- splunk-sdk = "^2.0.2"
29
+ Jinja2 = "^3.1.6"
30
+ questionary = "^2.1.1"
31
+ docker = "^7.2.0"
32
+ splunk-sdk = "^2.1.1"
27
33
  semantic-version = "^2.10.0"
28
34
  bottle = ">=0.12.25,<0.14.0"
29
- tqdm = "^4.66.5"
30
- pygit2 = "^1.15.1"
31
- #We are pinned to this version of tyro because 0.9.23 and above
32
- #have an issue when parsing an extremely large number of files
33
- #(in our testing great than 130) when using the mode:selected
34
- #--mode.files command.
35
- tyro = "^0.9.2,<0.9.23"
36
- gitpython = "^3.1.43"
37
- setuptools = "<81"
38
- rich = "^14.0.0"
35
+ tqdm = "^4.70.1"
36
+ pygit2 = "^1.20.1"
37
+ tyro = "^1.0.16"
38
+ gitpython = "^3.1.62"
39
+ setuptools = ">=83.0.0,<84"
40
+ rich = "^15.0.0"
39
41
 
40
42
  [tool.poetry.group.dev.dependencies]
41
- ruff = "^0.12.10"
43
+ ruff = "^0.16.8"
42
44
 
43
45
  [build-system]
44
46
  requires = ["poetry-core>=1.0.0"]
File without changes
File without changes