contentctl 5.0.2__tar.gz → 5.0.4__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-5.0.2 → contentctl-5.0.4}/PKG-INFO +1 -1
- contentctl-5.0.4/contentctl/objects/annotated_types.py +9 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/constants.py +2 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/dashboard.py +15 -9
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/detection_tags.py +57 -4
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/mitre_attack_enrichment.py +16 -3
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/story_tags.py +6 -5
- contentctl-5.0.4/contentctl/templates/app_template/default/data/ui/nav/default.xml +7 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +0 -1
- {contentctl-5.0.2 → contentctl-5.0.4}/pyproject.toml +1 -1
- contentctl-5.0.2/contentctl/objects/annotated_types.py +0 -6
- contentctl-5.0.2/contentctl/templates/app_template/default/data/ui/nav/default.xml +0 -7
- {contentctl-5.0.2 → contentctl-5.0.4}/LICENSE.md +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/README.md +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/__init__.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/build.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/deploy_acs.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/DetectionTestingManager.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/GitService.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/generate_detection_coverage_badge.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureServer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/progress_bar.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/views/DetectionTestingView.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/views/DetectionTestingViewCLI.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/detection_testing/views/DetectionTestingViewWeb.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/doc_gen.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/initialize.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/inspect.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/new_content.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/release_notes.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/reporting.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/test.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/actions/validate.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/api.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/contentctl.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/enrichments/attack_enrichment.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/enrichments/cve_enrichment.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/enrichments/splunk_app_enrichment.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/helper/link_validator.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/helper/logger.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/helper/splunk_app.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/helper/utils.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/input/director.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/input/new_content_questions.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/input/yml_reader.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/abstract_security_content_objects/detection_abstract.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/alert_action.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/atomic.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/base_test.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/base_test_result.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/baseline.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/baseline_tags.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/config.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/correlation_search.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/data_source.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_email.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_notable.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_phantom.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_rba.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_scheduling.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/deployment_slack.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/detection.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/detection_metadata.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/detection_stanza.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/drilldown.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/enums.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/errors.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/integration_test.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/integration_test_result.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/investigation.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/investigation_tags.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/lookup.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/macro.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/manual_test.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/manual_test_result.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/notable_action.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/notable_event.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/playbook.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/playbook_tags.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/rba.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/risk_analysis_action.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/risk_event.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/risk_object.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/savedsearches_conf.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/security_content_object.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/story.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/test_attack_data.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/test_group.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/threat_object.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/throttling.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/unit_test.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/unit_test_baseline.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/objects/unit_test_result.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/api_json_output.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/attack_nav_output.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/attack_nav_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/conf_output.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/conf_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/data_source_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/doc_md_output.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/jinja_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/json_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/svg_output.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/analyticstories_detections.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/analyticstories_investigations.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/analyticstories_stories.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/app.conf.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/app.manifest.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/collections.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/content-version.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/detection_count.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/detection_coverage.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_detection_page.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_detections.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_navigation.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_navigation_pages.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_playbooks.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_playbooks_page.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_stories.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/doc_story_page.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/es_investigations_investigations.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/es_investigations_stories.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/header.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/macros.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/panel.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/savedsearches_baselines.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/savedsearches_detections.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/savedsearches_investigations.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/server.conf.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/transforms.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/workflow_actions.j2 +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/yml_writer.py +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/README.md +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_default.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/README/essoc_story_detail.txt +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/README/essoc_summary.txt +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/README/essoc_usage_dashboard.txt +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/README.md +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/analytic_stories.conf +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/commands.conf +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/data/ui/views/escu_summary.xml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/data/ui/views/feedback.xml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/use_case_library.conf +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/lookups/mitre_enrichment.csv +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/metadata/default.meta +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIcon.png +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIconAlt.png +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIconAlt_2x.png +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIcon_2x.png +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/data_sources/sysmon_eventid_1.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/datamodels_cim.conf +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/datamodels_custom.conf +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/deployments/escu_default_configuration_anomaly.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/deployments/escu_default_configuration_baseline.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/deployments/escu_default_configuration_correlation.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/deployments/escu_default_configuration_hunting.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/deployments/escu_default_configuration_ttp.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/detections/application/.gitkeep +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/detections/cloud/.gitkeep +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/detections/network/.gitkeep +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/detections/web/.gitkeep +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/macros/security_content_ctime.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/macros/security_content_summariesonly.yml +0 -0
- {contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/stories/cobalt_strike.yml +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
CVE_TYPE = Annotated[str, Field(pattern=r"^CVE-[1|2]\d{3}-\d+$")]
|
|
6
|
+
MITRE_ATTACK_ID_TYPE_PARENT = Annotated[str, Field(pattern=r"^T\d{4}$")]
|
|
7
|
+
MITRE_ATTACK_ID_TYPE_SUBTYPE = Annotated[str, Field(pattern=r"^T\d{4}(.\d{3})$")]
|
|
8
|
+
MITRE_ATTACK_ID_TYPE = MITRE_ATTACK_ID_TYPE_PARENT | MITRE_ATTACK_ID_TYPE_SUBTYPE
|
|
9
|
+
APPID_TYPE = Annotated[str, Field(pattern="^[a-zA-Z0-9_-]+$")]
|
|
@@ -123,6 +123,8 @@ ES_MAX_STANZA_LENGTH = 99
|
|
|
123
123
|
CONTENTCTL_DETECTION_STANZA_NAME_FORMAT_TEMPLATE = (
|
|
124
124
|
"{app_label} - {detection_name} - Rule"
|
|
125
125
|
)
|
|
126
|
+
|
|
127
|
+
CONTENTCTL_DASHBOARD_LABEL_TEMPLATE = "{app_label} - {dashboard_name}"
|
|
126
128
|
CONTENTCTL_BASELINE_STANZA_NAME_FORMAT_TEMPLATE = "{app_label} - {detection_name}"
|
|
127
129
|
CONTENTCTL_RESPONSE_TASK_NAME_FORMAT_TEMPLATE = (
|
|
128
130
|
"{app_label} - {detection_name} - Response Task"
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pathlib
|
|
3
|
+
from enum import StrEnum
|
|
1
4
|
from typing import Any
|
|
2
|
-
from pydantic import Field, Json, model_validator
|
|
3
5
|
|
|
4
|
-
import pathlib
|
|
5
6
|
from jinja2 import Environment
|
|
6
|
-
import
|
|
7
|
-
|
|
7
|
+
from pydantic import Field, Json, model_validator
|
|
8
|
+
|
|
8
9
|
from contentctl.objects.config import build
|
|
9
|
-
from
|
|
10
|
+
from contentctl.objects.constants import CONTENTCTL_DASHBOARD_LABEL_TEMPLATE
|
|
11
|
+
from contentctl.objects.security_content_object import SecurityContentObject
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
DEFAULT_DASHBOARD_JINJA2_TEMPLATE = """<dashboard version="2" theme="{{ dashboard.theme }}">
|
|
12
14
|
<label>{{ dashboard.label(config) }}</label>
|
|
13
15
|
<description></description>
|
|
14
16
|
<definition><![CDATA[
|
|
@@ -31,7 +33,7 @@ class DashboardTheme(StrEnum):
|
|
|
31
33
|
|
|
32
34
|
class Dashboard(SecurityContentObject):
|
|
33
35
|
j2_template: str = Field(
|
|
34
|
-
default=
|
|
36
|
+
default=DEFAULT_DASHBOARD_JINJA2_TEMPLATE,
|
|
35
37
|
description="Jinja2 Template used to construct the dashboard",
|
|
36
38
|
)
|
|
37
39
|
description: str = Field(
|
|
@@ -49,7 +51,9 @@ class Dashboard(SecurityContentObject):
|
|
|
49
51
|
)
|
|
50
52
|
|
|
51
53
|
def label(self, config: build) -> str:
|
|
52
|
-
return
|
|
54
|
+
return CONTENTCTL_DASHBOARD_LABEL_TEMPLATE.format(
|
|
55
|
+
app_label=config.app.label, dashboard_name=self.name
|
|
56
|
+
)
|
|
53
57
|
|
|
54
58
|
@model_validator(mode="before")
|
|
55
59
|
@classmethod
|
|
@@ -98,7 +102,9 @@ class Dashboard(SecurityContentObject):
|
|
|
98
102
|
return json.dumps(self.json_obj, indent=4)
|
|
99
103
|
|
|
100
104
|
def getOutputFilepathRelativeToAppRoot(self, config: build) -> pathlib.Path:
|
|
101
|
-
|
|
105
|
+
# for clarity, the name of the dashboard file will follow the same convention
|
|
106
|
+
# as we use for detections, prefixing it with app_name -
|
|
107
|
+
filename = f"{self.label(config)}.xml"
|
|
102
108
|
return pathlib.Path("default/data/ui/views") / filename
|
|
103
109
|
|
|
104
110
|
def writeDashboardFile(self, j2_env: Environment, config: build):
|
|
@@ -33,7 +33,10 @@ from contentctl.objects.enums import (
|
|
|
33
33
|
SecurityContentProductName,
|
|
34
34
|
SecurityDomain,
|
|
35
35
|
)
|
|
36
|
-
from contentctl.objects.mitre_attack_enrichment import
|
|
36
|
+
from contentctl.objects.mitre_attack_enrichment import (
|
|
37
|
+
MitreAttackEnrichment,
|
|
38
|
+
MitreAttackGroup,
|
|
39
|
+
)
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
class DetectionTags(BaseModel):
|
|
@@ -44,7 +47,7 @@ class DetectionTags(BaseModel):
|
|
|
44
47
|
asset_type: AssetType = Field(...)
|
|
45
48
|
group: list[str] = []
|
|
46
49
|
|
|
47
|
-
mitre_attack_id:
|
|
50
|
+
mitre_attack_id: list[MITRE_ATTACK_ID_TYPE] = []
|
|
48
51
|
nist: list[NistCategory] = []
|
|
49
52
|
|
|
50
53
|
product: list[SecurityContentProductName] = Field(..., min_length=1)
|
|
@@ -68,6 +71,15 @@ class DetectionTags(BaseModel):
|
|
|
68
71
|
phases.add(phase)
|
|
69
72
|
return sorted(list(phases))
|
|
70
73
|
|
|
74
|
+
# We do not want this to be included in serialization. By default, @property
|
|
75
|
+
# objects are not included in dumps
|
|
76
|
+
@property
|
|
77
|
+
def unique_mitre_attack_groups(self) -> list[MitreAttackGroup]:
|
|
78
|
+
group_set: set[MitreAttackGroup] = set()
|
|
79
|
+
for enrichment in self.mitre_attack_enrichments:
|
|
80
|
+
group_set.update(set(enrichment.mitre_attack_group_objects))
|
|
81
|
+
return sorted(group_set, key=lambda k: k.group)
|
|
82
|
+
|
|
71
83
|
# enum is intentionally Cis18 even though field is named cis20 for legacy reasons
|
|
72
84
|
@computed_field
|
|
73
85
|
@property
|
|
@@ -134,8 +146,8 @@ class DetectionTags(BaseModel):
|
|
|
134
146
|
|
|
135
147
|
if len(missing_tactics) > 0:
|
|
136
148
|
raise ValueError(f"Missing Mitre Attack IDs. {missing_tactics} not found.")
|
|
137
|
-
|
|
138
|
-
|
|
149
|
+
|
|
150
|
+
self.mitre_attack_enrichments = mitre_enrichments
|
|
139
151
|
|
|
140
152
|
return self
|
|
141
153
|
|
|
@@ -159,6 +171,44 @@ class DetectionTags(BaseModel):
|
|
|
159
171
|
return enrichments
|
|
160
172
|
"""
|
|
161
173
|
|
|
174
|
+
@field_validator("mitre_attack_id", mode="after")
|
|
175
|
+
@classmethod
|
|
176
|
+
def sameTypeAndSubtypeNotPresent(
|
|
177
|
+
cls, techniques_and_subtechniques: list[MITRE_ATTACK_ID_TYPE]
|
|
178
|
+
) -> list[MITRE_ATTACK_ID_TYPE]:
|
|
179
|
+
techniques: list[str] = [
|
|
180
|
+
f"{unknown_technique}."
|
|
181
|
+
for unknown_technique in techniques_and_subtechniques
|
|
182
|
+
if "." not in unknown_technique
|
|
183
|
+
]
|
|
184
|
+
subtechniques: list[MITRE_ATTACK_ID_TYPE] = [
|
|
185
|
+
unknown_technique
|
|
186
|
+
for unknown_technique in techniques_and_subtechniques
|
|
187
|
+
if "." in unknown_technique
|
|
188
|
+
]
|
|
189
|
+
subtype_and_parent_exist_exceptions: list[ValueError] = []
|
|
190
|
+
|
|
191
|
+
for subtechnique in subtechniques:
|
|
192
|
+
for technique in techniques:
|
|
193
|
+
if subtechnique.startswith(technique):
|
|
194
|
+
subtype_and_parent_exist_exceptions.append(
|
|
195
|
+
ValueError(
|
|
196
|
+
f" Technique : {technique.split('.')[0]}\n"
|
|
197
|
+
f" SubTechnique: {subtechnique}\n"
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if len(subtype_and_parent_exist_exceptions):
|
|
202
|
+
error_string = "\n".join(
|
|
203
|
+
str(e) for e in subtype_and_parent_exist_exceptions
|
|
204
|
+
)
|
|
205
|
+
raise ValueError(
|
|
206
|
+
"Overlapping MITRE Attack ID Techniques and Subtechniques may not be defined. "
|
|
207
|
+
f"Remove the Technique and keep the Subtechnique:\n{error_string}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return techniques_and_subtechniques
|
|
211
|
+
|
|
162
212
|
@field_validator("analytic_story", mode="before")
|
|
163
213
|
@classmethod
|
|
164
214
|
def mapStoryNamesToStoryObjects(
|
|
@@ -238,3 +288,6 @@ class DetectionTags(BaseModel):
|
|
|
238
288
|
return matched_tests + [
|
|
239
289
|
AtomicTest.AtomicTestWhenTestIsMissing(test) for test in missing_tests
|
|
240
290
|
]
|
|
291
|
+
return matched_tests + [
|
|
292
|
+
AtomicTest.AtomicTestWhenTestIsMissing(test) for test in missing_tests
|
|
293
|
+
]
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import List
|
|
4
|
-
from enum import StrEnum
|
|
2
|
+
|
|
5
3
|
import datetime
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator
|
|
8
|
+
|
|
6
9
|
from contentctl.objects.annotated_types import MITRE_ATTACK_ID_TYPE
|
|
7
10
|
|
|
8
11
|
|
|
@@ -84,6 +87,16 @@ class MitreAttackGroup(BaseModel):
|
|
|
84
87
|
return []
|
|
85
88
|
return contributors
|
|
86
89
|
|
|
90
|
+
def __lt__(self, other: MitreAttackGroup) -> bool:
|
|
91
|
+
if not isinstance(object, MitreAttackGroup):
|
|
92
|
+
raise Exception(
|
|
93
|
+
f"Cannot compare object of type MitreAttackGroup to object of type [{type(object).__name__}]"
|
|
94
|
+
)
|
|
95
|
+
return self.group < other.group
|
|
96
|
+
|
|
97
|
+
def __hash__(self) -> int:
|
|
98
|
+
return hash(self.group)
|
|
99
|
+
|
|
87
100
|
|
|
88
101
|
class MitreAttackEnrichment(BaseModel):
|
|
89
102
|
ConfigDict(extra="forbid")
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from pydantic import BaseModel, Field, model_serializer, ConfigDict
|
|
3
|
-
from typing import List, Set, Optional
|
|
4
2
|
|
|
5
3
|
from enum import Enum
|
|
4
|
+
from typing import List, Optional, Set
|
|
6
5
|
|
|
7
|
-
from
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, model_serializer
|
|
7
|
+
|
|
8
|
+
from contentctl.objects.annotated_types import CVE_TYPE, MITRE_ATTACK_ID_TYPE
|
|
8
9
|
from contentctl.objects.enums import (
|
|
9
|
-
StoryCategory,
|
|
10
10
|
DataModel,
|
|
11
11
|
KillChainPhase,
|
|
12
12
|
SecurityContentProductName,
|
|
13
|
+
StoryCategory,
|
|
13
14
|
)
|
|
14
|
-
from contentctl.objects.
|
|
15
|
+
from contentctl.objects.mitre_attack_enrichment import MitreAttackEnrichment
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class StoryUseCase(str, Enum):
|
|
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-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/analyticstories_detections.j2
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/analyticstories_investigations.j2
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/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-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/es_investigations_stories.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/savedsearches_baselines.j2
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/output/templates/savedsearches_detections.j2
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/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-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/README/essoc_summary.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/default/commands.conf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/lookups/mitre_enrichment.csv
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/metadata/default.meta
RENAMED
|
File without changes
|
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIconAlt.png
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIconAlt_2x.png
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/app_template/static/appIcon_2x.png
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/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
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/macros/security_content_ctime.yml
RENAMED
|
File without changes
|
{contentctl-5.0.2 → contentctl-5.0.4}/contentctl/templates/macros/security_content_summariesonly.yml
RENAMED
|
File without changes
|
|
File without changes
|