From 15531b5cfc00aaa897cdcacf293deaf96afb08fc Mon Sep 17 00:00:00 2001 From: P4T12ICK Date: Mon, 17 Jan 2022 10:49:57 +0100 Subject: [PATCH] add content_organizer --- .../application/adapter/adapter.py | 6 +++- .../application/use_cases/content_changer.py | 2 +- .../use_cases/content_organizer.py | 22 ++++++++++++ .../adapter/obj_to_conf_adapter.py | 6 +++- .../adapter/obj_to_yml_adapter.py | 36 ++++++++++++++----- .../tests/adapter/test_obj_to_yml_adapter.py | 2 +- bin/contentctl_project/main/contentctl.py | 30 ++++++++++++++-- 7 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 bin/contentctl_project/contentctl_core/application/use_cases/content_organizer.py diff --git a/bin/contentctl_project/contentctl_core/application/adapter/adapter.py b/bin/contentctl_project/contentctl_core/application/adapter/adapter.py index bedb848828..205618b349 100644 --- a/bin/contentctl_project/contentctl_core/application/adapter/adapter.py +++ b/bin/contentctl_project/contentctl_core/application/adapter/adapter.py @@ -27,5 +27,9 @@ class Adapter(abc.ABC): pass @abc.abstractmethod - def writeObjects(self, objects: list) -> None: + def writeObjectsInPlace(self, objects: list) -> None: + pass + + @abc.abstractmethod + def writeObjects(self, objects: list, security_content_folder: str) -> None: pass \ No newline at end of file diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/content_changer.py b/bin/contentctl_project/contentctl_core/application/use_cases/content_changer.py index 7f8d5fecc2..6290b997d8 100644 --- a/bin/contentctl_project/contentctl_core/application/use_cases/content_changer.py +++ b/bin/contentctl_project/contentctl_core/application/use_cases/content_changer.py @@ -23,7 +23,7 @@ class ContentChanger: converter_func = getattr(self, input_dto.converter_func_name) converter_func(objects) - input_dto.adapter.writeObjects(objects) + input_dto.adapter.writeObjectsInPlace(objects) # Define Converter Functions here diff --git a/bin/contentctl_project/contentctl_core/application/use_cases/content_organizer.py b/bin/contentctl_project/contentctl_core/application/use_cases/content_organizer.py new file mode 100644 index 0000000000..2f012f8ada --- /dev/null +++ b/bin/contentctl_project/contentctl_core/application/use_cases/content_organizer.py @@ -0,0 +1,22 @@ + +from dataclasses import dataclass + +from contentctl_core.application.factory.object_factory import ObjectFactory, ObjectFactoryInputDto +from contentctl_core.application.adapter.adapter import Adapter + + +@dataclass(frozen=True) +class ContentOrganizerInputDto: + adapter : Adapter + factory_input_dto : ObjectFactoryInputDto + output_path : str + + +class ContentOrganizer: + + def execute(self, input_dto: ContentOrganizerInputDto) -> None: + objects = list() + factory = ObjectFactory(objects) + factory.execute(input_dto.factory_input_dto) + + input_dto.adapter.writeObjects(objects, input_dto.output_path) \ No newline at end of file diff --git a/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_conf_adapter.py b/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_conf_adapter.py index 016ccdc26a..37ecdc168b 100644 --- a/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_conf_adapter.py +++ b/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_conf_adapter.py @@ -93,5 +93,9 @@ class ObjToConfAdapter(Adapter): macros) - def writeObjects(self, objects: list) -> None: + def writeObjectsInPlace(self, objects: list) -> None: + pass + + + def writeObjects(self, objects: list, security_content_folder: str) -> None: pass \ No newline at end of file diff --git a/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_yml_adapter.py b/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_yml_adapter.py index 9c8f4953f9..8175683351 100644 --- a/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_yml_adapter.py +++ b/bin/contentctl_project/contentctl_infrastructure/adapter/obj_to_yml_adapter.py @@ -34,12 +34,30 @@ class ObjToYmlAdapter(Adapter): object.pop('deprecated') YmlWriter.writeYmlFile(file_path, object) - # def convertNameToFileName(self, obj: dict): - # file_name = obj['name'] \ - # .replace(' ', '_') \ - # .replace('-','_') \ - # .replace('.','_') \ - # .replace('/','_') \ - # .lower() - # file_name = file_name + '.yml' - # return file_name \ No newline at end of file + def writeObjects(self, objects: list, security_content_folder: str) -> None: + for object in objects: + if object['type'] == 'Investigation': + file_name = self.convertNameToFileName(object) + file_path = object['file_path'] + object.pop('file_path') + object.pop('deprecated') + YmlWriter.writeYmlFile(os.path.join(security_content_folder, 'investigations', file_name)) + os.remove(file_path) + elif object['type'] == 'Baseline': + file_name = self.convertNameToFileName(object) + file_path = object['file_path'] + object.pop('file_path') + object.pop('deprecated') + YmlWriter.writeYmlFile(os.path.join(security_content_folder, 'baselines', file_name)) + os.remove(file_path) + + + def convertNameToFileName(self, obj: dict): + file_name = obj['name'] \ + .replace(' ', '_') \ + .replace('-','_') \ + .replace('.','_') \ + .replace('/','_') \ + .lower() + file_name = file_name + '.yml' + return file_name \ No newline at end of file diff --git a/bin/contentctl_project/contentctl_infrastructure/tests/adapter/test_obj_to_yml_adapter.py b/bin/contentctl_project/contentctl_infrastructure/tests/adapter/test_obj_to_yml_adapter.py index 8f31d41780..35c5232b43 100644 --- a/bin/contentctl_project/contentctl_infrastructure/tests/adapter/test_obj_to_yml_adapter.py +++ b/bin/contentctl_project/contentctl_infrastructure/tests/adapter/test_obj_to_yml_adapter.py @@ -10,7 +10,7 @@ def test_read_and_write_yml(): 'obj_to_yml_data/attempted_credential_dump_from_registry_via_reg_exe.yml') yml_obj = YmlReader.load_file(file_path) adapter = ObjToYmlAdapter() - adapter.writeObjects([yml_obj]) + adapter.writeObjectsInPlace([yml_obj]) ref_file_path = os.path.join(os.path.dirname(__file__), 'obj_to_yml_data_ref/attempted_credential_dump_from_registry_via_reg_exe.yml') diff --git a/bin/contentctl_project/main/contentctl.py b/bin/contentctl_project/main/contentctl.py index 331fbbebb3..8c4d4bdf03 100644 --- a/bin/contentctl_project/main/contentctl.py +++ b/bin/contentctl_project/main/contentctl.py @@ -3,6 +3,7 @@ import argparse import os from contentctl_core.application.use_cases.content_changer import ContentChanger, ContentChangerInputDto +from contentctl_core.application.use_cases.content_organizer import ContentOrganizer, ContentOrganizerInputDto from contentctl_core.application.factory.object_factory import ObjectFactoryInputDto from contentctl_infrastructure.builder.security_content_object_builder import SecurityContentObjectBuilder from contentctl_infrastructure.builder.security_content_director import SecurityContentDirector @@ -69,12 +70,27 @@ def content_changer(args) -> None: content_changer.execute(input_dto) +def content_organizer(args) -> None: + factory_input_dto = ObjectFactoryInputDto( + os.path.abspath(args.path), + SecurityContentObjectBuilder(), + SecurityContentDirector() + ) + + input_dto = ContentOrganizerInputDto( + ObjToYmlAdapter(), + factory_input_dto, + os.path.abspath(args.security_content_path) + ) + + content_organizer = ContentOrganizer() + content_organizer.execute(input_dto) + + def main(args): # grab arguments parser = argparse.ArgumentParser( description="Use `contentctl.py action -h` to get help with any Splunk Security Content action") - parser.add_argument("-p", "--path", required=False, default=".", - help="path to the Splunk Security Content. Defaults to `.`") parser.set_defaults(func=lambda _: parser.print_help()) actions_parser = parser.add_subparsers(title="Splunk Security Content actions", dest="action") @@ -82,6 +98,7 @@ def main(args): #validate_parser = actions_parser.add_parser("validate", help="Validates written content") #generate_parser = actions_parser.add_parser("generate", help="Generates a deployment package for different platforms (splunk_app)") content_changer_parser = actions_parser.add_parser("content_changer", help="Change Security Content based on defined rules") + content_organizer_parser = actions_parser.add_parser("content_organizer", help="Organize Security Content") # # new arguments # new_parser.add_argument("-t", "--type", required=False, type=str, default="detection", @@ -102,11 +119,18 @@ def main(args): # generate_parser.set_defaults(func=generate) content_changer_parser.add_argument("-p", "--path", required=True, - help="path to the Splunk Security Content") + help="path to the Splunk Security Content folder") content_changer_parser.add_argument("-cf", "--change_function", required=True, type=str, help="Define a change funtion defined in bin/contentctl_core/contentctl/application/use_cases/content_changer.py") content_changer_parser.set_defaults(func=content_changer) + content_organizer_parser.add_argument("-p", "--path", required=True, + help="path to the Splunk Security Content folder") + content_organizer_parser.add_argument("-scp", "--security_content_path", required=True, + help="path to the Splunk Security Content") + content_organizer_parser.set_defaults(func=content_organizer) + + # # parse them args = parser.parse_args() return args.func(args)