Merge pull request #2374 from splunk/fix_app_renaming_in_init

Fix App Renaming in contentctl init/generate/build for custom apps
This commit is contained in:
Bhavin Patel
2022-09-29 11:19:06 -07:00
committed by GitHub
3 changed files with 57 additions and 7 deletions
@@ -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 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
@@ -171,7 +175,25 @@ 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".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}/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)
@@ -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
@@ -84,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
@@ -94,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
+2 -1
View File
@@ -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),