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