contentctl 4.4.3__tar.gz → 4.4.5__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 (176) hide show
  1. {contentctl-4.4.3 → contentctl-4.4.5}/PKG-INFO +1 -1
  2. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/build.py +3 -1
  3. contentctl-4.4.5/contentctl/actions/deploy_acs.py +55 -0
  4. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/GitService.py +17 -6
  5. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/new_content.py +1 -2
  6. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/contentctl.py +6 -2
  7. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/abstract_security_content_objects/detection_abstract.py +1 -0
  8. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/config.py +1 -0
  9. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/conf_output.py +14 -7
  10. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/conf_writer.py +117 -5
  11. contentctl-4.4.5/contentctl/output/templates/app.conf.j2 +37 -0
  12. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/app.manifest.j2 +2 -1
  13. contentctl-4.4.5/contentctl/output/templates/server.conf.j2 +4 -0
  14. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/metadata/default.meta +1 -1
  15. {contentctl-4.4.3 → contentctl-4.4.5}/pyproject.toml +1 -1
  16. contentctl-4.4.3/contentctl/actions/deploy_acs.py +0 -38
  17. contentctl-4.4.3/contentctl/output/templates/app.conf.j2 +0 -35
  18. contentctl-4.4.3/contentctl/templates/app_template/default/app.conf +0 -30
  19. contentctl-4.4.3/contentctl/templates/app_template/default/content-version.conf +0 -2
  20. {contentctl-4.4.3 → contentctl-4.4.5}/LICENSE.md +0 -0
  21. {contentctl-4.4.3 → contentctl-4.4.5}/README.md +0 -0
  22. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/__init__.py +0 -0
  23. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/DetectionTestingManager.py +0 -0
  24. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/generate_detection_coverage_badge.py +0 -0
  25. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +0 -0
  26. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +0 -0
  27. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py +0 -0
  28. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/progress_bar.py +0 -0
  29. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/views/DetectionTestingView.py +0 -0
  30. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +0 -0
  31. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -0
  32. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +0 -0
  33. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/doc_gen.py +0 -0
  34. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/initialize.py +0 -0
  35. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/initialize_old.py +0 -0
  36. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/inspect.py +0 -0
  37. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/release_notes.py +0 -0
  38. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/reporting.py +0 -0
  39. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/test.py +0 -0
  40. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/actions/validate.py +0 -0
  41. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/api.py +0 -0
  42. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/enrichments/attack_enrichment.py +0 -0
  43. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/enrichments/cve_enrichment.py +0 -0
  44. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/enrichments/splunk_app_enrichment.py +0 -0
  45. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/helper/link_validator.py +0 -0
  46. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/helper/logger.py +0 -0
  47. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/helper/splunk_app.py +0 -0
  48. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/helper/utils.py +0 -0
  49. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/input/director.py +0 -0
  50. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/input/new_content_questions.py +0 -0
  51. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/input/yml_reader.py +0 -0
  52. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +0 -0
  53. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/alert_action.py +0 -0
  54. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/annotated_types.py +0 -0
  55. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/atomic.py +0 -0
  56. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/base_test.py +0 -0
  57. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/base_test_result.py +0 -0
  58. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/baseline.py +0 -0
  59. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/baseline_tags.py +0 -0
  60. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/constants.py +0 -0
  61. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/correlation_search.py +0 -0
  62. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/dashboard.py +0 -0
  63. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/data_source.py +0 -0
  64. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment.py +0 -0
  65. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_email.py +0 -0
  66. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_notable.py +0 -0
  67. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_phantom.py +0 -0
  68. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_rba.py +0 -0
  69. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_scheduling.py +0 -0
  70. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/deployment_slack.py +0 -0
  71. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/detection.py +0 -0
  72. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/detection_metadata.py +0 -0
  73. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/detection_stanza.py +0 -0
  74. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/detection_tags.py +0 -0
  75. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/drilldown.py +0 -0
  76. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/enums.py +0 -0
  77. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/errors.py +0 -0
  78. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/event_source.py +0 -0
  79. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/integration_test.py +0 -0
  80. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/integration_test_result.py +0 -0
  81. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/investigation.py +0 -0
  82. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/investigation_tags.py +0 -0
  83. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/lookup.py +0 -0
  84. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/macro.py +0 -0
  85. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/manual_test.py +0 -0
  86. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/manual_test_result.py +0 -0
  87. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/mitre_attack_enrichment.py +0 -0
  88. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/notable_action.py +0 -0
  89. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/notable_event.py +0 -0
  90. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/observable.py +0 -0
  91. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/playbook.py +0 -0
  92. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/playbook_tags.py +0 -0
  93. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/risk_analysis_action.py +0 -0
  94. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/risk_event.py +0 -0
  95. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/risk_object.py +0 -0
  96. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/savedsearches_conf.py +0 -0
  97. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/security_content_object.py +0 -0
  98. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/story.py +0 -0
  99. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/story_tags.py +0 -0
  100. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/test_attack_data.py +0 -0
  101. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/test_group.py +0 -0
  102. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/threat_object.py +0 -0
  103. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/throttling.py +0 -0
  104. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/unit_test.py +0 -0
  105. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/unit_test_baseline.py +0 -0
  106. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/objects/unit_test_result.py +0 -0
  107. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/api_json_output.py +0 -0
  108. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/attack_nav_output.py +0 -0
  109. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/attack_nav_writer.py +0 -0
  110. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/data_source_writer.py +0 -0
  111. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/detection_writer.py +0 -0
  112. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/doc_md_output.py +0 -0
  113. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/jinja_writer.py +0 -0
  114. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/json_writer.py +0 -0
  115. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/new_content_yml_output.py +0 -0
  116. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/svg_output.py +0 -0
  117. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/analyticstories_detections.j2 +0 -0
  118. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/analyticstories_investigations.j2 +0 -0
  119. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/analyticstories_stories.j2 +0 -0
  120. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/collections.j2 +0 -0
  121. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/content-version.j2 +0 -0
  122. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/detection_count.j2 +0 -0
  123. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/detection_coverage.j2 +0 -0
  124. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_detection_page.j2 +0 -0
  125. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_detections.j2 +0 -0
  126. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_navigation.j2 +0 -0
  127. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_navigation_pages.j2 +0 -0
  128. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_playbooks.j2 +0 -0
  129. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_playbooks_page.j2 +0 -0
  130. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_stories.j2 +0 -0
  131. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/doc_story_page.j2 +0 -0
  132. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/es_investigations_investigations.j2 +0 -0
  133. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/es_investigations_stories.j2 +0 -0
  134. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/header.j2 +0 -0
  135. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/macros.j2 +0 -0
  136. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/panel.j2 +0 -0
  137. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/savedsearches_baselines.j2 +0 -0
  138. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/savedsearches_detections.j2 +0 -0
  139. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/savedsearches_investigations.j2 +0 -0
  140. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/transforms.j2 +0 -0
  141. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/templates/workflow_actions.j2 +0 -0
  142. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/yml_output.py +0 -0
  143. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/output/yml_writer.py +0 -0
  144. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/README.md +0 -0
  145. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_default.yml +0 -0
  146. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/README/essoc_story_detail.txt +0 -0
  147. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/README/essoc_summary.txt +0 -0
  148. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/README/essoc_usage_dashboard.txt +0 -0
  149. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/README.md +0 -0
  150. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/analytic_stories.conf +0 -0
  151. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/commands.conf +0 -0
  152. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/data/ui/nav/default.xml +0 -0
  153. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/data/ui/views/escu_summary.xml +0 -0
  154. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/data/ui/views/feedback.xml +0 -0
  155. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/default/use_case_library.conf +0 -0
  156. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/lookups/mitre_enrichment.csv +0 -0
  157. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/static/appIcon.png +0 -0
  158. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/static/appIconAlt.png +0 -0
  159. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/static/appIconAlt_2x.png +0 -0
  160. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/app_template/static/appIcon_2x.png +0 -0
  161. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/data_sources/sysmon_eventid_1.yml +0 -0
  162. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/datamodels_cim.conf +0 -0
  163. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/datamodels_custom.conf +0 -0
  164. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/deployments/escu_default_configuration_anomaly.yml +0 -0
  165. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/deployments/escu_default_configuration_baseline.yml +0 -0
  166. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/deployments/escu_default_configuration_correlation.yml +0 -0
  167. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/deployments/escu_default_configuration_hunting.yml +0 -0
  168. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/deployments/escu_default_configuration_ttp.yml +0 -0
  169. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/detections/application/.gitkeep +0 -0
  170. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/detections/cloud/.gitkeep +0 -0
  171. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +0 -0
  172. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/detections/network/.gitkeep +0 -0
  173. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/detections/web/.gitkeep +0 -0
  174. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/macros/security_content_ctime.yml +0 -0
  175. {contentctl-4.4.3 → contentctl-4.4.5}/contentctl/templates/macros/security_content_summariesonly.yml +0 -0
  176. {contentctl-4.4.3 → contentctl-4.4.5}/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.4.3
3
+ Version: 4.4.5
4
4
  Summary: Splunk Content Control Tool
5
5
  License: Apache 2.0
6
6
  Author: STRT
@@ -51,7 +51,9 @@ class Build:
51
51
  updated_conf_files.update(conf_output.writeObjects(input_dto.director_output_dto.lookups, SecurityContentType.lookups))
52
52
  updated_conf_files.update(conf_output.writeObjects(input_dto.director_output_dto.macros, SecurityContentType.macros))
53
53
  updated_conf_files.update(conf_output.writeObjects(input_dto.director_output_dto.dashboards, SecurityContentType.dashboards))
54
- updated_conf_files.update(conf_output.writeAppConf())
54
+ updated_conf_files.update(conf_output.writeMiscellaneousAppFiles())
55
+
56
+
55
57
 
56
58
  #Ensure that the conf file we just generated/update is syntactically valid
57
59
  for conf_file in updated_conf_files:
@@ -0,0 +1,55 @@
1
+ from contentctl.objects.config import deploy_acs, StackType
2
+ from requests import post
3
+ import pprint
4
+
5
+
6
+ class Deploy:
7
+ def execute(self, config: deploy_acs, appinspect_token:str) -> None:
8
+
9
+ #The following common headers are used by both Clasic and Victoria
10
+ headers = {
11
+ 'Authorization': f'Bearer {config.splunk_cloud_jwt_token}',
12
+ 'ACS-Legal-Ack': 'Y'
13
+ }
14
+ try:
15
+
16
+ with open(config.getPackageFilePath(include_version=False),'rb') as app_data:
17
+ #request_data = app_data.read()
18
+ if config.stack_type == StackType.classic:
19
+ # Classic instead uses a form to store token and package
20
+ # https://docs.splunk.com/Documentation/SplunkCloud/9.1.2308/Config/ManageApps#Manage_private_apps_using_the_ACS_API_on_Classic_Experience
21
+ address = f"https://admin.splunk.com/{config.splunk_cloud_stack}/adminconfig/v2/apps"
22
+
23
+ form_data = {
24
+ 'token': (None, appinspect_token),
25
+ 'package': app_data
26
+ }
27
+ res = post(address, headers=headers, files = form_data)
28
+ elif config.stack_type == StackType.victoria:
29
+ # Victoria uses the X-Splunk-Authorization Header
30
+ # It also uses --data-binary for the app content
31
+ # https://docs.splunk.com/Documentation/SplunkCloud/9.1.2308/Config/ManageApps#Manage_private_apps_using_the_ACS_API_on_Victoria_Experience
32
+ headers.update({'X-Splunk-Authorization': appinspect_token})
33
+ address = f"https://admin.splunk.com/{config.splunk_cloud_stack}/adminconfig/v2/apps/victoria"
34
+ res = post(address, headers=headers, data=app_data.read())
35
+ else:
36
+ raise Exception(f"Unsupported stack type: '{config.stack_type}'")
37
+ except Exception as e:
38
+ raise Exception(f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{str(e)}")
39
+
40
+ try:
41
+ # Request went through and completed, but may have returned a non-successful error code.
42
+ # This likely includes a more verbose response describing the error
43
+ res.raise_for_status()
44
+ print(res.json())
45
+ except Exception as e:
46
+ try:
47
+ error_text = res.json()
48
+ except Exception as e:
49
+ error_text = "No error text - request failed"
50
+ formatted_error_text = pprint.pformat(error_text)
51
+ print("While this may not be the cause of your error, ensure that the uid and appid of your Private App does not exist in Splunkbase\n"
52
+ "ACS cannot deploy and app with the same uid or appid as one that exists in Splunkbase.")
53
+ raise Exception(f"Error installing to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS:\n{formatted_error_text}")
54
+
55
+ print(f"'{config.getPackageFilePath(include_version=False)}' successfully installed to stack '{config.splunk_cloud_stack}' (stack_type='{config.stack_type}') via ACS!")
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
13
13
  from contentctl.objects.macro import Macro
14
14
  from contentctl.objects.lookup import Lookup
15
15
  from contentctl.objects.detection import Detection
16
+ from contentctl.objects.data_source import DataSource
16
17
  from contentctl.objects.security_content_object import SecurityContentObject
17
18
  from contentctl.objects.config import test_common, All, Changes, Selected
18
19
 
@@ -67,9 +68,12 @@ class GitService(BaseModel):
67
68
 
68
69
  #Make a filename to content map
69
70
  filepath_to_content_map = { obj.file_path:obj for (_,obj) in self.director.name_to_content_map.items()}
70
- updated_detections:set[Detection] = set()
71
- updated_macros:set[Macro] = set()
72
- updated_lookups:set[Lookup] = set()
71
+
72
+ updated_detections: set[Detection] = set()
73
+ updated_macros: set[Macro] = set()
74
+ updated_lookups: set[Lookup] = set()
75
+ updated_datasources: set[DataSource] = set()
76
+
73
77
 
74
78
  for diff in all_diffs:
75
79
  if type(diff) == pygit2.Patch:
@@ -90,6 +94,13 @@ class GitService(BaseModel):
90
94
  updated_macros.add(macroObject)
91
95
  else:
92
96
  raise Exception(f"Error getting macro object for file {str(decoded_path)}")
97
+
98
+ elif decoded_path.is_relative_to(self.config.path/"data_sources") and decoded_path.suffix == ".yml":
99
+ datasourceObject = filepath_to_content_map.get(decoded_path, None)
100
+ if isinstance(datasourceObject, DataSource):
101
+ updated_datasources.add(datasourceObject)
102
+ else:
103
+ raise Exception(f"Error getting data source object for file {str(decoded_path)}")
93
104
 
94
105
  elif decoded_path.is_relative_to(self.config.path/"lookups"):
95
106
  # We need to convert this to a yml. This means we will catch
@@ -115,7 +126,6 @@ class GitService(BaseModel):
115
126
  # Detected a changed .mlmodel file. However, since we do not have testing for these detections at
116
127
  # this time, we will ignore this change.
117
128
  updatedLookup = None
118
-
119
129
 
120
130
  else:
121
131
  raise Exception(f"Detected a changed file in the lookups/ directory '{str(decoded_path)}'.\n"
@@ -136,7 +146,8 @@ class GitService(BaseModel):
136
146
 
137
147
  # If a detection has at least one dependency on changed content,
138
148
  # then we must test it again
139
- changed_macros_and_lookups:set[SecurityContentObject] = updated_macros.union(updated_lookups)
149
+
150
+ changed_macros_and_lookups_and_datasources:set[SecurityContentObject] = updated_macros.union(updated_lookups, updated_datasources)
140
151
 
141
152
  for detection in self.director.detections:
142
153
  if detection in updated_detections:
@@ -144,7 +155,7 @@ class GitService(BaseModel):
144
155
  # to add it again
145
156
  continue
146
157
 
147
- for obj in changed_macros_and_lookups:
158
+ for obj in changed_macros_and_lookups_and_datasources:
148
159
  if obj in detection.get_content_dependencies():
149
160
  updated_detections.add(detection)
150
161
  break
@@ -29,8 +29,7 @@ class NewContent:
29
29
  answers['date'] = datetime.today().strftime('%Y-%m-%d')
30
30
  answers['author'] = answers['detection_author']
31
31
  del answers['detection_author']
32
- answers['data_sources'] = answers['data_source']
33
- del answers['data_source']
32
+ answers['data_source'] = answers['data_source']
34
33
  answers['type'] = answers['detection_type']
35
34
  del answers['detection_type']
36
35
  answers['status'] = "production" #start everything as production since that's what we INTEND the content to become
@@ -19,6 +19,7 @@ from contentctl.actions.test import TestInputDto
19
19
  from contentctl.actions.reporting import ReportingInputDto, Reporting
20
20
  from contentctl.actions.inspect import Inspect
21
21
  from contentctl.input.yml_reader import YmlReader
22
+ from contentctl.actions.deploy_acs import Deploy
22
23
  from contentctl.actions.release_notes import ReleaseNotes
23
24
 
24
25
  # def print_ascii_art():
@@ -95,8 +96,11 @@ def new_func(config:new):
95
96
 
96
97
 
97
98
  def deploy_acs_func(config:deploy_acs):
98
- #This is a bit challenging to get to work with the default values.
99
- raise Exception("deploy acs not yet implemented")
99
+ print("Building and inspecting app...")
100
+ token = inspect_func(config)
101
+ print("App successfully built and inspected.")
102
+ print("Deploying app...")
103
+ Deploy().execute(config, token)
100
104
 
101
105
  def test_common_func(config:test_common):
102
106
  if type(config) == test:
@@ -689,6 +689,7 @@ class Detection_Abstract(SecurityContentObject):
689
689
  objects: list[SecurityContentObject] = []
690
690
  objects += self.macros
691
691
  objects += self.lookups
692
+ objects += self.data_source_objects
692
693
  return objects
693
694
 
694
695
  @field_validator("deployment", mode="before")
@@ -294,6 +294,7 @@ class StackType(StrEnum):
294
294
 
295
295
 
296
296
  class inspect(build):
297
+
297
298
  splunk_api_username: str = Field(
298
299
  description="Splunk API username used for appinspect and Splunkbase downloads."
299
300
  )
@@ -57,19 +57,26 @@ class ConfOutput:
57
57
  pass
58
58
 
59
59
 
60
- def writeAppConf(self)->set[pathlib.Path]:
60
+
61
+
62
+ def writeMiscellaneousAppFiles(self)->set[pathlib.Path]:
61
63
  written_files:set[pathlib.Path] = set()
62
- for output_app_path, template_name in [ ("default/app.conf", "app.conf.j2"),
63
- ("default/content-version.conf", "content-version.j2")]:
64
- written_files.add(ConfWriter.writeConfFile(pathlib.Path(output_app_path),
65
- template_name,
66
- self.config,
67
- [self.config.app]))
64
+
65
+ written_files.add(ConfWriter.writeConfFile(pathlib.Path("default/content-version.conf"),
66
+ "content-version.j2",
67
+ self.config,
68
+ [self.config.app]))
68
69
 
69
70
  written_files.add(ConfWriter.writeManifestFile(pathlib.Path("app.manifest"),
70
71
  "app.manifest.j2",
71
72
  self.config,
72
73
  [self.config.app]))
74
+
75
+ written_files.add(ConfWriter.writeServerConf(self.config))
76
+
77
+ written_files.add(ConfWriter.writeAppConf(self.config))
78
+
79
+
73
80
  return written_files
74
81
 
75
82
 
@@ -12,6 +12,76 @@ from contentctl.objects.dashboard import Dashboard
12
12
  from contentctl.objects.config import build
13
13
  import xml.etree.ElementTree as ET
14
14
 
15
+ # This list is not exhaustive of all default conf files, but should be
16
+ # sufficient for our purposes.
17
+ DEFAULT_CONF_FILES = [
18
+ "alert_actions.conf",
19
+ "app.conf",
20
+ "audit.conf",
21
+ "authentication.conf",
22
+ "authorize.conf",
23
+ "bookmarks.conf",
24
+ "checklist.conf",
25
+ "collections.conf",
26
+ "commands.conf",
27
+ "conf.conf",
28
+ "datamodels.conf",
29
+ "datatypesbnf.conf",
30
+ "default-mode.conf",
31
+ "deploymentclient.conf",
32
+ "distsearch.conf",
33
+ "event_renderers.conf",
34
+ "eventdiscoverer.conf",
35
+ "eventtypes.conf",
36
+ "federated.conf",
37
+ "fields.conf",
38
+ "global-banner.conf",
39
+ "health.conf",
40
+ "indexes.conf",
41
+ "inputs.conf",
42
+ "limits.conf",
43
+ "literals.conf",
44
+ "livetail.conf",
45
+ "macros.conf",
46
+ "messages.conf",
47
+ "metric_alerts.conf",
48
+ "metric_rollups.conf",
49
+ "multikv.conf",
50
+ "outputs.conf",
51
+ "passwords.conf",
52
+ "procmon-filters.conf",
53
+ "props.conf",
54
+ "pubsub.conf",
55
+ "restmap.conf",
56
+ "rolling_upgrade.conf",
57
+ "savedsearches.conf",
58
+ "searchbnf.conf",
59
+ "segmenters.conf",
60
+ "server.conf",
61
+ "serverclass.conf",
62
+ "serverclass.seed.xml.conf",
63
+ "source-classifier.conf",
64
+ "sourcetypes.conf",
65
+ "tags.conf",
66
+ "telemetry.conf",
67
+ "times.conf",
68
+ "transactiontypes.conf",
69
+ "transforms.conf",
70
+ "ui-prefs.conf",
71
+ "ui-tour.conf",
72
+ "user-prefs.conf",
73
+ "user-seed.conf",
74
+ "viewstates.conf",
75
+ "visualizations.conf",
76
+ "web-features.conf",
77
+ "web.conf",
78
+ "wmi.conf",
79
+ "workflow_actions.conf",
80
+ "workload_policy.conf",
81
+ "workload_pools.conf",
82
+ "workload_rules.conf",
83
+ ]
84
+
15
85
  class ConfWriter():
16
86
 
17
87
  @staticmethod
@@ -57,6 +127,52 @@ class ConfWriter():
57
127
  ConfWriter.validateConfFile(output_path)
58
128
  return output_path
59
129
 
130
+ @staticmethod
131
+ def getCustomConfFileStems(config:build)->list[str]:
132
+ # Get all the conf files in the default directory. We must make a reload.conf_file = simple key/value for them if
133
+ # they are custom conf files
134
+ default_path = config.getPackageDirectoryPath()/"default"
135
+ conf_files = default_path.glob("*.conf")
136
+
137
+ custom_conf_file_stems = [conf_file.stem for conf_file in conf_files if conf_file.name not in DEFAULT_CONF_FILES]
138
+ return sorted(custom_conf_file_stems)
139
+
140
+ @staticmethod
141
+ def writeServerConf(config: build) -> pathlib.Path:
142
+ app_output_path = pathlib.Path("default/server.conf")
143
+ template_name = "server.conf.j2"
144
+
145
+ j2_env = ConfWriter.getJ2Environment()
146
+ template = j2_env.get_template(template_name)
147
+
148
+ output = template.render(custom_conf_files=ConfWriter.getCustomConfFileStems(config))
149
+
150
+ output_path = config.getPackageDirectoryPath()/app_output_path
151
+ output_path.parent.mkdir(parents=True, exist_ok=True)
152
+ with open(output_path, 'a') as f:
153
+ output = output.encode('utf-8', 'ignore').decode('utf-8')
154
+ f.write(output)
155
+ return output_path
156
+
157
+
158
+ @staticmethod
159
+ def writeAppConf(config: build) -> pathlib.Path:
160
+ app_output_path = pathlib.Path("default/app.conf")
161
+ template_name = "app.conf.j2"
162
+
163
+ j2_env = ConfWriter.getJ2Environment()
164
+ template = j2_env.get_template(template_name)
165
+
166
+ output = template.render(custom_conf_files=ConfWriter.getCustomConfFileStems(config),
167
+ app=config.app)
168
+
169
+ output_path = config.getPackageDirectoryPath()/app_output_path
170
+ output_path.parent.mkdir(parents=True, exist_ok=True)
171
+ with open(output_path, 'a') as f:
172
+ output = output.encode('utf-8', 'ignore').decode('utf-8')
173
+ f.write(output)
174
+ return output_path
175
+
60
176
  @staticmethod
61
177
  def writeManifestFile(app_output_path:pathlib.Path, template_name : str, config: build, objects : list) -> pathlib.Path:
62
178
  j2_env = ConfWriter.getJ2Environment()
@@ -70,6 +186,7 @@ class ConfWriter():
70
186
  output = output.encode('utf-8', 'ignore').decode('utf-8')
71
187
  f.write(output)
72
188
  return output_path
189
+
73
190
 
74
191
 
75
192
  @staticmethod
@@ -218,8 +335,3 @@ class ConfWriter():
218
335
  _ = json.load(manifestFile)
219
336
  except Exception as e:
220
337
  raise Exception(f"Failed to validate .manifest file {str(path)} (Note that .manifest files should contain only valid JSON-formatted data): {str(e)}")
221
-
222
-
223
-
224
-
225
-
@@ -0,0 +1,37 @@
1
+ ## Splunk app configuration file
2
+
3
+ [install]
4
+ is_configured = false
5
+ state = enabled
6
+ state_change_requires_restart = false
7
+ build = {{ app.build }}
8
+
9
+ [triggers]
10
+ {% for custom_conf_file in custom_conf_files%}
11
+ reload.{{custom_conf_file}} = simple
12
+ {% endfor %}
13
+
14
+ [launcher]
15
+ author = {{ app.author_company }}
16
+ version = {{ app.version }}
17
+ description = {{ app.description | escapeNewlines() }}
18
+
19
+ [ui]
20
+ is_visible = true
21
+ label = {{ app.title }}
22
+
23
+ [package]
24
+ id = {{ app.appid }}
25
+ {% if app.uid == 3449 %}
26
+ check_for_updates = true
27
+ {% else %}
28
+ check_for_updates = false
29
+ {% endif %}
30
+
31
+ [id]
32
+ version = {{ app.version }}
33
+ name = {{ app.appid }}
34
+
35
+
36
+
37
+
@@ -1,5 +1,6 @@
1
1
  {
2
- "schemaVersion": "1.0.0",
2
+ "schemaVersion": "1.0.0",
3
+ "targetWorkloads": ["_search_heads"],
3
4
  "info": {
4
5
  "title": "{{ objects[0].title }}",
5
6
  "id": {
@@ -0,0 +1,4 @@
1
+ [shclustering]
2
+ {% for custom_conf_file in custom_conf_files%}
3
+ conf_replication_include.{{custom_conf_file}} = true
4
+ {% endfor %}
@@ -1,6 +1,6 @@
1
1
  ## shared Application-level permissions
2
2
  []
3
- access = read : [ * ], write : [ admin ]
3
+ access = read : [ * ], write : [ admin, sc_admin ]
4
4
  export = system
5
5
 
6
6
  [savedsearches]
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "contentctl"
3
- version = "4.4.3"
3
+ version = "4.4.5"
4
4
 
5
5
  description = "Splunk Content Control Tool"
6
6
  authors = ["STRT <research@splunk.com>"]
@@ -1,38 +0,0 @@
1
- from dataclasses import dataclass
2
- from contentctl.input.director import DirectorInputDto
3
- from contentctl.output.conf_output import ConfOutput
4
-
5
-
6
- from typing import Union
7
-
8
- @dataclass(frozen=True)
9
- class ACSDeployInputDto:
10
- director_input_dto: DirectorInputDto
11
- splunk_api_username: str
12
- splunk_api_password: str
13
- splunk_cloud_jwt_token: str
14
- splunk_cloud_stack: str
15
- stack_type: str
16
-
17
-
18
- class Deploy:
19
- def execute(self, input_dto: ACSDeployInputDto) -> None:
20
-
21
- conf_output = ConfOutput(input_dto.director_input_dto.input_path, input_dto.director_input_dto.config)
22
-
23
- appinspect_token = conf_output.inspectAppAPI(input_dto.splunk_api_username, input_dto.splunk_api_password, input_dto.stack_type)
24
-
25
-
26
- if input_dto.splunk_cloud_jwt_token is None or input_dto.splunk_cloud_stack is None:
27
- if input_dto.splunk_cloud_jwt_token is None:
28
- raise Exception("Cannot deploy app via ACS, --splunk_cloud_jwt_token was not defined on command line.")
29
- else:
30
- raise Exception("Cannot deploy app via ACS, --splunk_cloud_stack was not defined on command line.")
31
-
32
- conf_output.deploy_via_acs(input_dto.splunk_cloud_jwt_token,
33
- input_dto.splunk_cloud_stack,
34
- appinspect_token,
35
- input_dto.stack_type)
36
-
37
-
38
-
@@ -1,35 +0,0 @@
1
- ## Splunk app configuration file
2
-
3
- [install]
4
- is_configured = false
5
- state = enabled
6
- state_change_requires_restart = false
7
- build = {{ objects[0].build }}
8
-
9
- [triggers]
10
- reload.analytic_stories = simple
11
- reload.usage_searches = simple
12
- reload.use_case_library = simple
13
- reload.correlationsearches = simple
14
- reload.analyticstories = simple
15
- reload.governance = simple
16
- reload.managed_configurations = simple
17
- reload.postprocess = simple
18
- reload.content-version = simple
19
- reload.es_investigations = simple
20
-
21
- [launcher]
22
- author = {{ objects[0].author_company }}
23
- version = {{ objects[0].version }}
24
- description = {{ objects[0].description | escapeNewlines() }}
25
-
26
- [ui]
27
- is_visible = true
28
- label = {{ objects[0].title }}
29
-
30
- [package]
31
- id = {{ objects[0].appid }}
32
-
33
-
34
-
35
-
@@ -1,30 +0,0 @@
1
- ## Splunk app configuration file
2
-
3
- [install]
4
- is_configured = false
5
- state = enabled
6
- state_change_requires_restart = false
7
- build = 16367
8
-
9
- [triggers]
10
- reload.analytic_stories = simple
11
- reload.use_case_library = simple
12
- reload.correlationsearches = simple
13
- reload.analyticstories = simple
14
- reload.governance = simple
15
- reload.managed_configurations = simple
16
- reload.postprocess = simple
17
- reload.content-version = simple
18
- reload.es_investigations = simple
19
-
20
- [launcher]
21
- author = Splunk
22
- version = 4.9.0
23
- description = Explore the Analytic Stories included with ES Content Updates.
24
-
25
- [ui]
26
- is_visible = true
27
- label = ES Content Updates
28
-
29
- [package]
30
- id = DA-ESS-ContentUpdate
@@ -1,2 +0,0 @@
1
- [content-version]
2
- version = 4.9.0
File without changes
File without changes
File without changes