From 444026f32b79a10ac8cf8127eace51f822ace7a0 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:53:29 -0700 Subject: [PATCH 1/9] Another small modification to replace static ESCU name with the name of your app --- .../contentctl_core/application/use_cases/initialize.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index c0850bcdc0..e606d3d872 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -171,7 +171,14 @@ class Initialize: for fname in ["savedsearches_investigations.j2", "savedsearches_detections.j2", "analyticstories_investigations.j2", "analyticstories_detections.j2", "savedsearches_baselines.j2"]: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) - #Generate directories? + + raw ='''{app_name} - ''' + original = raw.format(app_name=".escu.") # + updated = raw.format(app_name=f".{self.app_name}.") + filename_root = os.path.join(self.path,"bin/contentctl_project/contentctl_infrastructure/adapter/templates/") + for fname in ["savedsearches_investigations.j2", "savedsearches_detections.j2", "savedsearches_baselines.j2"]: + full_path = os.path.join(filename_root, fname) + self.simple_replace_line(full_path, original, updated) def generate_content_version_file(self): new_content_version = CONTENT_VERSION_FILE.format(version=self.app_version) From b5bfc29fb5d6fabf1b492afba13ce8f92e677c4b Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:57:12 -0700 Subject: [PATCH 2/9] Basic validation on the app_name --- .../contentctl_core/application/use_cases/initialize.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index e606d3d872..adc5eb6d6c 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -106,6 +106,10 @@ class Initialize: #Information that will be used for generation of a custom manifest self.app_title = args.title self.app_name = args.name + if not self.app_name.replace('-','').isalnum() and len(self.app_name.replace('-','')) > 0: + # Basic check to see if the app_name is alphanumeric (no spaces or symbols) and, after any + # - character(s) are removed it is still non-zero length + raise(Exception(f"Error - app_name {self.app_name} is not valid. Name can ")) self.app_version = args.version self.app_description = args.description self.app_author_name = args.author_name From a4d143d4f18e10ed39546afe428dcd9969f66bab Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:58:52 -0700 Subject: [PATCH 3/9] better app name valdiation message --- .../contentctl_core/application/use_cases/initialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index adc5eb6d6c..134ee1563c 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -109,7 +109,7 @@ class Initialize: if not self.app_name.replace('-','').isalnum() and len(self.app_name.replace('-','')) > 0: # Basic check to see if the app_name is alphanumeric (no spaces or symbols) and, after any # - character(s) are removed it is still non-zero length - raise(Exception(f"Error - app_name {self.app_name} is not valid. Name can ")) + raise(Exception(f"Error - app_name {self.app_name} is not valid. Name must be alphanumeric (no symbols or spaces). The only allowed special character is -.")) self.app_version = args.version self.app_description = args.description self.app_author_name = args.author_name From 6fe804f8ed5100061c75f69fc84e362de6bbbbfe Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:18:00 -0700 Subject: [PATCH 4/9] changed one more location where the path to escu is hardcoded. --- .../application/use_cases/initialize.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index 134ee1563c..f8f70dc0a8 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -176,13 +176,24 @@ class Initialize: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) - raw ='''{app_name} - ''' - original = raw.format(app_name=".escu.") # - updated = raw.format(app_name=f".{self.app_name}.") + raw ='''.{app_name}.''' + original = raw.format(app_name="ESCU".lower()) # + updated = raw.format(app_name=self.app_name.lower()) filename_root = os.path.join(self.path,"bin/contentctl_project/contentctl_infrastructure/adapter/templates/") for fname in ["savedsearches_investigations.j2", "savedsearches_detections.j2", "savedsearches_baselines.j2"]: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) + + + raw ='''.{app_name}.''' + original = raw.format(app_name="ESCU".lower()) # + updated = raw.format(app_name=self.app_name.lower()) + filename_root = os.path.join(self.path,f"dist/{self.app_name.lower()}/default/default/data/ui/views/") + for fname in ["escu_summary.xml"]: + full_path = os.path.join(filename_root, fname) + self.simple_replace_line(full_path, original, updated) + + def generate_content_version_file(self): new_content_version = CONTENT_VERSION_FILE.format(version=self.app_version) From af16d25ca214569ebf26338dd45bfecfff03b11e Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:27:48 -0700 Subject: [PATCH 5/9] Fixed file name find and replace issue --- .../contentctl_core/application/use_cases/initialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index f8f70dc0a8..812b60d2f3 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -188,7 +188,7 @@ class Initialize: raw ='''.{app_name}.''' original = raw.format(app_name="ESCU".lower()) # updated = raw.format(app_name=self.app_name.lower()) - filename_root = os.path.join(self.path,f"dist/{self.app_name.lower()}/default/default/data/ui/views/") + filename_root = os.path.join(self.path,f"dist/{self.app_name}/default/default/data/ui/views/") for fname in ["escu_summary.xml"]: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) From d84aba8af06d52578d3eb833cdb200acc1b73270 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:28:36 -0700 Subject: [PATCH 6/9] Fixed another issue with the filename. --- .../contentctl_core/application/use_cases/initialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index 812b60d2f3..ad8cfba14b 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -188,7 +188,7 @@ class Initialize: raw ='''.{app_name}.''' original = raw.format(app_name="ESCU".lower()) # updated = raw.format(app_name=self.app_name.lower()) - filename_root = os.path.join(self.path,f"dist/{self.app_name}/default/default/data/ui/views/") + filename_root = os.path.join(self.path,f"dist/{self.app_name}/default/data/ui/views/") for fname in ["escu_summary.xml"]: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) From c8a7c61b743c3c0cfd54110e542b278d8a43a1d1 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 16:26:34 -0700 Subject: [PATCH 7/9] Changes to apss the name of the app to the story builder, since it is required for generate. --- .../builder/security_content_story_builder.py | 33 +++++++++++++++++-- contentctl.py | 3 +- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py b/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py index 670bd3a9fa..ba87e840a8 100644 --- a/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py +++ b/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py @@ -1,8 +1,11 @@ import re import sys +import json +import pathlib +import os from pydantic import ValidationError - +from typing import Union from bin.contentctl_project.contentctl_core.application.builder.story_builder import StoryBuilder from bin.contentctl_project.contentctl_core.domain.entities.story import Story from bin.contentctl_project.contentctl_core.domain.entities.enums.enums import SecurityContentType @@ -13,9 +16,33 @@ class SecurityContentStoryBuilder(StoryBuilder): story: Story check_references: bool - def __init__(self, check_references: bool = False): + def __init__(self, output_path:Union[str,None]=None, check_references: bool = False): self.check_references = check_references + self.app_name = self.get_app_name_from_manifest(output_path) + def get_app_name_from_manifest(self, output_path:Union[str,None])->str: + if output_path is None: + return "ESCU" + + try: + manifest_path = pathlib.Path(os.path.join(output_path, "app.manifest")) + except Exception as e: + raise(Exception(f"Failed to convert string {output_path} to path: {str(e)}")) + try: + with open(manifest_path, "r") as manifestFile: + manifest_obj = json.load(manifestFile) + except Exception as e: + raise(Exception(f"Failed to open manifest at path {manifest_path}: {str(e)}")) + + try: + app_name_from_manifest = manifest_obj['info']['id']['name'] + #Minor fix to shorten the name of ESCU detections and match with everything else + if app_name_from_manifest == "DA-ESS-ContentUpdate": + app_name_from_manifest = "ESCU" + return app_name_from_manifest + except Exception as e: + raise(Exception(f"Manifest file {manifest_path} missing nested object ['info']['id']['name']: {str(e)}")) + def setObject(self, path: str) -> None: yml_dict = YmlReader.load_file(path) yml_dict["tags"]["name"] = yml_dict["name"] @@ -46,7 +73,7 @@ class SecurityContentStoryBuilder(StoryBuilder): if detection: for detection_analytic_story in detection.tags.analytic_story: if detection_analytic_story == self.story.name: - matched_detection_names.append(str('ESCU - ' + detection.name + ' - Rule')) + matched_detection_names.append(str(f'{self.app_name} - ' + detection.name + ' - Rule')) # SSE-638: detections object should at least contain the name attribute. # We also need a minimal set of the following attributes to satisfy docgen (doc_stories.j2): # name, source, type, tags.mitre_attack_enrichments.mitre_attack_technique diff --git a/contentctl.py b/contentctl.py index b0ca086944..c82f3c1833 100644 --- a/contentctl.py +++ b/contentctl.py @@ -110,11 +110,12 @@ def generate(args) -> None: factory_input_dto = None ba_factory_input_dto = None if args.product in ["ESCU", "API"]: + factory_input_dto = FactoryInputDto( os.path.abspath(args.path), SecurityContentBasicBuilder(), SecurityContentDetectionBuilder(force_cached_or_offline=args.cached_and_offline, skip_enrichment=args.skip_enrichment), - SecurityContentStoryBuilder(), + SecurityContentStoryBuilder(output_path=args.output), SecurityContentBaselineBuilder(), SecurityContentInvestigationBuilder(), SecurityContentPlaybookBuilder(input_path=args.path), From bb84ef752b3cde4bcca50f811333c97843d7b4b7 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 16:30:32 -0700 Subject: [PATCH 8/9] One more change to substitution string --- .../contentctl_core/application/use_cases/initialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py index ad8cfba14b..0205cfe569 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/initialize.py @@ -176,7 +176,7 @@ class Initialize: full_path = os.path.join(filename_root, fname) self.simple_replace_line(full_path, original, updated) - raw ='''.{app_name}.''' + raw ='''.{app_name}''' original = raw.format(app_name="ESCU".lower()) # updated = raw.format(app_name=self.app_name.lower()) filename_root = os.path.join(self.path,"bin/contentctl_project/contentctl_infrastructure/adapter/templates/") From 23b140e8e6f07bf2baf2ee8634e6a9277fe45970 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:18:50 -0700 Subject: [PATCH 9/9] A few more changes in the story builder to get references working properly. --- .../builder/security_content_story_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py b/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py index ba87e840a8..f6a35f1c58 100644 --- a/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py +++ b/bin/contentctl_project/contentctl_infrastructure/builder/security_content_story_builder.py @@ -111,7 +111,7 @@ class SecurityContentStoryBuilder(StoryBuilder): for baseline in baselines: for baseline_analytic_story in baseline.tags.analytic_story: if baseline_analytic_story == self.story.name: - matched_baseline_names.append(str('ESCU - ' + baseline.name)) + matched_baseline_names.append(str(f'{self.app_name} - ' + baseline.name)) self.story.baseline_names = matched_baseline_names @@ -121,7 +121,7 @@ class SecurityContentStoryBuilder(StoryBuilder): for investigation in investigations: for investigation_analytic_story in investigation.tags.analytic_story: if investigation_analytic_story == self.story.name: - matched_investigation_names.append(str('ESCU - ' + investigation.name + ' - Response Task')) + matched_investigation_names.append(str(f'{self.app_name} - ' + investigation.name + ' - Response Task')) matched_investigations.append(investigation) self.story.investigation_names = matched_investigation_names