diff --git a/scripts/main.py b/scripts/main.py index cf5b61b..7f4a3cb 100755 --- a/scripts/main.py +++ b/scripts/main.py @@ -130,8 +130,8 @@ if __name__ == '__main__': dn=args.dataneeded, dr=args.detectionrule, tg=args.triggers, en=args.enrichment, ra=args.responseactions, rp=args.responseplaybook, - cu=args.customers, hp=args.hardeningpolicy, - init=args.init) + rs=args.responsestage, cu=args.customers, + hp=args.hardeningpolicy, init=args.init) elif args.react_stix: GenerateSTIX() diff --git a/scripts/populateconfluence.py b/scripts/populateconfluence.py index 25a1b60..611cf93 100755 --- a/scripts/populateconfluence.py +++ b/scripts/populateconfluence.py @@ -11,6 +11,7 @@ from triggers import Triggers from enrichment import Enrichment from responseaction import ResponseAction from responseplaybook import ResponsePlaybook +from responsestage import ResponseStage from customer import Customer from attack_mapping import te_mapping # , ta_mapping @@ -30,11 +31,11 @@ class PopulateConfluence: """Desc""" def __init__(self, auth, lp=False, dn=False, dr=False, en=False, tg=False, - ra=False, rp=False, cu=False, ms=False, mp=False, hp=False, + ra=False, rp=False, rs=False, cu=False, ms=False, mp=False, hp=False, auto=False, art_dir=False, atc_dir=False, lp_path=False, dn_path=False, dr_path=False, en_path=False, tg_path=False, - ra_path=False, rp_path=False, cu_path=False, hp_path=False, - ms_path=False, mp_path=False, init=False): + ra_path=False, rp_path=False, rs_path=False, cu_path=False, + hp_path=False, ms_path=False, mp_path=False, init=False): """Desc""" self.auth = auth @@ -79,8 +80,10 @@ class PopulateConfluence: self.data_needed(dn_path) self.enrichment(en_path) self.triggers(tg_path) + self.response_stage(rs_path) self.response_action(ra_path) self.response_playbook(rp_path) + self.response_stage(rs_path) self.detection_rule(dr_path) self.customer(cu_path) @@ -106,11 +109,18 @@ class PopulateConfluence: self.detection_rule(dr_path) if ra: + print("[*] We need to create Response Stages first...") + self.response_stage(rs_path) self.response_action(ra_path) + print("[*] Updating Response Stages...") + self.response_stage(rs_path) if rp: self.response_playbook(rp_path) + if rs: + self.response_stage(rs_path) + if tg: self.triggers(tg_path) @@ -127,7 +137,7 @@ class PopulateConfluence: def triggers(self, tg_path): """Populate Triggers""" - print("Populating Triggers..") + print("[*] Populating Triggers...") if tg_path: tg_list = glob.glob(tg_path + '*.yml') else: @@ -160,12 +170,12 @@ class PopulateConfluence: traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Triggers populated!") + print("[+] Triggers populated!") def hardening_policy(self, hp_path): """Populate Hardening Policies""" - print("Populating Hardening Policies..") + print("[*] Populating Hardening Policies...") if hp_path: hp_list = glob.glob(hp_path + '*.yml') else: @@ -195,12 +205,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Hardening Policies populated!") + print("[+] Hardening Policies populated!") def mitigation_system(self, ms_path): """Populate Mitigation Systems""" - print("Populating Mitigation Systems..") + print("[*] Populating Mitigation Systems...") if ms_path: ms_list = glob.glob(ms_path + '*.yml') else: @@ -230,12 +240,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Mitigation Systems populated!") + print("[+] Mitigation Systems populated!") def mitigation_policy(self, mp_path): """Populate Mitigation Policies""" - print("Populating Mitigation Policies..") + print("[*] Populating Mitigation Policies...") if mp_path: mp_list = glob.glob(mp_path + '*.yml') else: @@ -266,12 +276,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Mitigation Policies populated!") + print("[+] Mitigation Policies populated!") def logging_policy(self, lp_path): """Desc""" - print("Populating Logging Policies..") + print("[*] Populating Logging Policies...") if lp_path: lp_list = glob.glob(lp_path + '*.yml') else: @@ -302,12 +312,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Logging Policies populated!") + print("[+] Logging Policies populated!") def data_needed(self, dn_path): """Desc""" - print("Populating Data Needed..") + print("[*] Populating Data Needed...") if dn_path: dn_list = glob.glob(dn_path + '*.yml') else: @@ -338,12 +348,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Data Needed populated!") + print("[+] Data Needed populated!") def detection_rule(self, dr_path): """Desc""" - print("Populating Detection Rules..") + print("[*] Populating Detection Rules...") if dr_path: dr_list = glob.glob(dr_path + '*.yml') else: @@ -382,12 +392,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Detection Rules populated!") + print("[+] Detection Rules populated!") def enrichment(self, en_path): """Nothing here yet""" - print("Populating Enrichments..") + print("[*] Populating Enrichments...") if en_path: en_list = glob.glob(en_path + '*.yml') else: @@ -419,12 +429,12 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Enrichments populated!") + print("[+] Enrichments populated!") def response_action(self, ra_path): """Nothing here yet""" - print("Populating Response Actions..") + print("[*] Populating Response Actions...") if ra_path: ra_list = glob.glob(ra_path + '*.yml') else: @@ -456,12 +466,12 @@ class PopulateConfluence: traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Response Actions populated!") + print("[+] Response Actions populated!") def response_playbook(self, rp_path): """Nothing here yet""" - print("Populating Response Playbooks..") + print("[*] Populating Response Playbooks...") if rp_path: rp_list = glob.glob(rp_path + '*.yml') else: @@ -496,12 +506,52 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Response Playbooks populated!") + print("[+] Response Playbooks populated!") + + def response_stage(self, rs_path): + """Nothing here yet""" + + print("[*] Populating Response Stages...") + if rs_path: + rs_list = glob.glob(rs_path + '*.yml') + else: + rs_dir = ATCconfig.get('response_stages_dir') + rs_list = glob.glob(rs_dir + '/*.yml') + + for rs_file in rs_list: + try: + rs = ResponseStage(rs_file, apipath=self.apipath, + auth=self.auth, space=self.space) + rs.render_template("confluence") + + base = os.path.basename(rs_file) + + confluence_data = { + "title": rs.rs_parsed_file['title'], + "spacekey": self.space, + "parentid": str(ATCutils.confluence_get_page_id( + self.apipath, self.auth, self.space, + "Response Stages")), + "confluencecontent": rs.content, + } + + res = ATCutils.push_to_confluence(confluence_data, self.apipath, + self.auth) + if res == 'Page updated': + print("==> updated page: RS '" + base + "'") + # print("Done: ", rp.rp_parsed_file['title']) + except Exception as err: + print(rs_file + " failed") + print("Err message: %s" % err) + print('-' * 60) + traceback.print_exc(file=sys.stdout) + print('-' * 60) + print("[+] Response Stages populated!") def customer(self, cu_path): """Nothing here yet""" - print("Populating Customers..") + print("[+] Populating Customers...") if cu_path: cu_list = glob.glob(cu_path + '*.yml') else: @@ -534,4 +584,4 @@ class PopulateConfluence: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) - print("Customers populated!") + print("[+] Customers populated!") diff --git a/scripts/responseaction.py b/scripts/responseaction.py index f247f8d..86d3043 100755 --- a/scripts/responseaction.py +++ b/scripts/responseaction.py @@ -7,11 +7,13 @@ from react_scripts.react_mapping import rs_mapping import os + # ########################################################################### # # ########################### Response Action ############################### # # ########################################################################### # ATCconfig = ATCutils.load_config("config.yml") +env = Environment(loader=FileSystemLoader('templates')) class ResponseAction: @@ -22,9 +24,6 @@ class ResponseAction: # Init vars self.yaml_file = yaml_file - # The name of the directory containing future markdown LogginPolicy - self.parent_title = "Response_Actions" - self.apipath = apipath self.auth = auth self.space = space @@ -40,85 +39,66 @@ class ResponseAction: def render_template(self, template_type): """Description template_type: - - "markdown" - "confluence" """ - if template_type not in ["markdown", "confluence"]: + if template_type not in ["confluence"]: raise Exception( - "Bad template_type. Available values:" + - " [\"markdown\", \"confluence\"]") - - # Point to the templates directory - env = Environment(loader=FileSystemLoader('templates')) + "Bad template_type. Available value:" + + " \"confluence\"]") # Get proper template - if template_type == "markdown": - template = env.get_template( - 'markdown_responseaction_template.md.j2' + + template = env.get_template( + 'confluence_responseaction_template.html.j2') + + new_title = self.ra_parsed_file.get('id')\ + + ": "\ + + ATCutils.normalize_react_title(self.ra_parsed_file.get('title')) + + self.ra_parsed_file.update( + {'title': new_title} + ) + + self.ra_parsed_file.update( + {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) + + ## + ## Add link to a stage + ## + + stage = self.ra_parsed_file.get('stage') + rs_list = [] + for rs_id, rs_name in rs_mapping.items(): + if ATCutils.normalize_rs_name(stage) == rs_name: + if self.apipath and self.auth and self.space: + rs_confluence_page_id = str(ATCutils.confluence_get_page_id( + self.apipath, self.auth, self.space, rs_name) + ) + rs_list.append((rs_id, rs_name, rs_confluence_page_id)) + else: + rs_confluence_page_id = "" + rs_list.append((rs_id, rs_name, rs_confluence_page_id)) + break + + + + self.ra_parsed_file.update( + {'stage': rs_list} ) - self.ra_parsed_file.update( - {'title': ATCutils.normalize_react_title(self.ra_parsed_file - .get('title'))} - ) + # Category + self.ra_parsed_file.update( + {'category': ATCutils.get_ra_category(self.ra_parsed_file + .get('id'))} + ) - self.ra_parsed_file.update( - {'description': self.ra_parsed_file - .get('description').strip()} - ) + self.ra_parsed_file.update( + {'description': self.ra_parsed_file.get('description').strip()} + ) - elif template_type == "confluence": - template = env.get_template( - 'confluence_responseaction_template.html.j2' - ) - - new_title = self.ra_parsed_file.get('id')\ - + ": "\ - + ATCutils.normalize_react_title(self.ra_parsed_file.get('title')) - - self.ra_parsed_file.update( - {'title': new_title } - ) - - self.ra_parsed_file.update( - {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) - - linked_ra = self.ra_parsed_file.get("linked_ra") - - if linked_ra: - linked_ra_with_id = [] - for ra in linked_ra: - if self.apipath and self.auth and self.space: - linked_ra_id = str(ATCutils.confluence_get_page_id( - self.apipath, self.auth, self.space, ra) - ) - else: - linked_ra_id = "" - ra = (ra, linked_ra_id) - linked_ra_with_id.append(ra) - - self.ra_parsed_file.update( - {'linkedra': linked_ra_with_id} - ) - - self.ra_parsed_file.update( - {'description': self.ra_parsed_file.get('description').strip()} - ) - - self.ra_parsed_file.update( - {'workflow': self.ra_parsed_file.get('workflow')} - ) + self.ra_parsed_file.update( + {'workflow': self.ra_parsed_file.get('workflow')} + ) self.content = template.render(self.ra_parsed_file) - - def save_markdown_file(self, atc_dir=ATCconfig.get('md_name_of_root_directory')): - """Write content (md template filled with data) to a file""" - - base = os.path.basename(self.yaml_file) - title = os.path.splitext(base)[0] - - file_path = atc_dir + self.parent_title + "/" + \ - title + ".md" - - return ATCutils.write_file(file_path, self.content) diff --git a/scripts/responseplaybook.py b/scripts/responseplaybook.py index 66e3372..1fd3b62 100755 --- a/scripts/responseplaybook.py +++ b/scripts/responseplaybook.py @@ -24,8 +24,6 @@ class ResponsePlaybook: # Init vars self.yaml_file = yaml_file - # The name of the directory containing future markdown LogginPolicy - self.parent_title = "Response_Playbooks" self.apipath = apipath self.auth = auth @@ -46,288 +44,177 @@ class ResponsePlaybook: - "confluence" """ - if template_type not in ["markdown", "confluence"]: + if template_type not in ["confluence"]: raise Exception( "Bad template_type. Available values:" + - " [\"markdown\", \"confluence\"]") + " \"confluence\"]") # Point to the templates directory env = Environment(loader=FileSystemLoader('templates')) - # Get proper template - if template_type == "markdown": - template = env.get_template( - 'markdown_responseplaybook_template.md.j2' - ) - self.rp_parsed_file.update( - {'title': ATCutils.normalize_react_title(self.rp_parsed_file - .get('title'))} - ) + template = env.get_template( + 'confluence_responseplaybook_template.html.j2' + ) - # MITRE ATT&CK Tactics and Techniques - tactic = [] - tactic_re = re.compile(r'attack\.\w\D+$') - technique = [] - technique_re = re.compile(r'attack\.t\d{1,5}$') - # AM!TT Tactics and Techniques - amitt_tactic = [] - amitt_tactic_re = re.compile(r'amitt\.\w\D+$') - amitt_technique = [] - amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') + new_title = self.rp_parsed_file.get('id')\ + + ": "\ + + ATCutils.normalize_react_title(self.rp_parsed_file.get('title')) + + self.rp_parsed_file.update( + {'title': new_title } + ) - other_tags = [] + self.rp_parsed_file.update( + {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) - for tag in self.rp_parsed_file.get('tags'): - if tactic_re.match(tag): - tactic.append(ta_mapping.get(tag)) - elif technique_re.match(tag): - te = tag.upper()[7:] - technique.append((te_mapping.get(te), te)) - elif amitt_tactic_re.match(tag): - amitt_tactic.append(amitt_tactic_mapping.get(tag)) - elif amitt_technique_re.match(tag): - te = tag.upper()[6:] - amitt_technique.append((amitt_technique_mapping.get(te), te)) - else: - other_tags.append(tag) + # MITRE ATT&CK Tactics and Techniques + tactic = [] + tactic_re = re.compile(r'attack\.\w\D+$') + technique = [] + technique_re = re.compile(r'attack\.t\d{1,5}$') + # AM!TT Tactics and Techniques + amitt_tactic = [] + amitt_tactic_re = re.compile(r'amitt\.\w\D+$') + amitt_technique = [] + amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') - # Add MITRE ATT&CK Tactics and Techniques to J2 - self.rp_parsed_file.update({'tactics': tactic}) - self.rp_parsed_file.update({'techniques': technique}) - # Add AM!TT Tactics and Techniques to J2 - self.rp_parsed_file.update({'amitt_tactics': amitt_tactic}) - self.rp_parsed_file.update({'amitt_techniques': amitt_technique}) - self.rp_parsed_file.update({'other_tags': other_tags}) + other_tags = [] - preparation = [] - identification = [] - containment = [] - eradication = [] - recovery = [] - lessons_learned = [] - detect = [] - deny = [] - disrupt = [] - degrade = [] - deceive = [] - destroy = [] - deter = [] + for tag in self.rp_parsed_file.get('tags'): + if tactic_re.match(tag): + tactic.append(ta_mapping.get(tag)) + elif technique_re.match(tag): + te = tag.upper()[7:] + technique.append((te_mapping.get(te), te)) + elif amitt_tactic_re.match(tag): + amitt_tactic.append(amitt_tactic_mapping.get(tag)) + elif amitt_technique_re.match(tag): + te = tag.upper()[6:] + amitt_technique.append((amitt_technique_mapping.get(te), te)) + else: + other_tags.append(tag) - stages = [ - ('preparation', preparation), ('identification', identification), - ('containment', containment), ('eradication', eradication), - ('recovery', recovery), ('lessons_learned', lessons_learned), - ('detect', detect), ('deny', deny), ('disrupt', disrupt), - ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), - ('deter', deter) - ] + # Add MITRE ATT&CK Tactics and Techniques to J2 + self.rp_parsed_file.update({'tactics': tactic}) + self.rp_parsed_file.update({'techniques': technique}) + # Add AM!TT Tactics and Techniques to J2 + self.rp_parsed_file.update({'amitt_tactics': amitt_tactic}) + self.rp_parsed_file.update({'amitt_techniques': amitt_technique}) + self.rp_parsed_file.update({'other_tags': other_tags}) - # grab workflow per action in each IR stages - # error handling for playbooks with empty stages - for stage_name, stage_list in stages: - try: - for task in self.rp_parsed_file.get(stage_name): - action = ATCutils.read_yaml_file( - ATCconfig.get('response_actions_dir') - + '/' + task + '.yml' - ) + # get links to response action - action_title = action.get('id')\ - + ":"\ - + ATCutils.normalize_react_title(action.get('title')) - + preparation = [] + identification = [] + containment = [] + eradication = [] + recovery = [] + lessons_learned = [] + detect = [] + deny = [] + disrupt = [] + degrade = [] + deceive = [] + destroy = [] + deter = [] + + stages = [ + ('preparation', preparation), ('identification', identification), + ('containment', containment), ('eradication', eradication), + ('recovery', recovery), ('lessons_learned', lessons_learned), + ('detect', detect), ('deny', deny), ('disrupt', disrupt), + ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), + ('deter', deter) + ] + + for stage_name, stage_list in stages: + try: + for task in self.rp_parsed_file.get(stage_name): + action = ATCutils.read_yaml_file( + ATCconfig.get('response_actions_dir') + + '/' + task + '.yml' + ) + + action_title = action.get('id')\ + + ": "\ + + ATCutils.normalize_react_title(action.get('title')) + + if self.apipath and self.auth and self.space: stage_list.append( - (action_title, task, action.get('description'), action.get('workflow')) - ) - except TypeError: - pass - - # change stages name to more pretty format - stages = [(stage_name.replace('_', ' ').capitalize(), - stage_list) for stage_name, stage_list in stages] - - self.rp_parsed_file.update({'stages': stages}) - - self.rp_parsed_file.update( - {'description': self.rp_parsed_file - .get('description').strip()} - ) - - elif template_type == "confluence": - template = env.get_template( - 'confluence_responseplaybook_template.html.j2' - ) - - new_title = self.rp_parsed_file.get('id')\ - + ": "\ - + ATCutils.normalize_react_title(self.rp_parsed_file.get('title')) - - self.rp_parsed_file.update( - {'title': new_title } - ) - - self.rp_parsed_file.update( - {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) - - # MITRE ATT&CK Tactics and Techniques - tactic = [] - tactic_re = re.compile(r'attack\.\w\D+$') - technique = [] - technique_re = re.compile(r'attack\.t\d{1,5}$') - # AM!TT Tactics and Techniques - amitt_tactic = [] - amitt_tactic_re = re.compile(r'amitt\.\w\D+$') - amitt_technique = [] - amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') - - other_tags = [] - - for tag in self.rp_parsed_file.get('tags'): - if tactic_re.match(tag): - tactic.append(ta_mapping.get(tag)) - elif technique_re.match(tag): - te = tag.upper()[7:] - technique.append((te_mapping.get(te), te)) - elif amitt_tactic_re.match(tag): - amitt_tactic.append(amitt_tactic_mapping.get(tag)) - elif amitt_technique_re.match(tag): - te = tag.upper()[6:] - amitt_technique.append((amitt_technique_mapping.get(te), te)) - else: - other_tags.append(tag) - - # Add MITRE ATT&CK Tactics and Techniques to J2 - self.rp_parsed_file.update({'tactics': tactic}) - self.rp_parsed_file.update({'techniques': technique}) - # Add AM!TT Tactics and Techniques to J2 - self.rp_parsed_file.update({'amitt_tactics': amitt_tactic}) - self.rp_parsed_file.update({'amitt_techniques': amitt_technique}) - self.rp_parsed_file.update({'other_tags': other_tags}) - - # get links to response action - - preparation = [] - identification = [] - containment = [] - eradication = [] - recovery = [] - lessons_learned = [] - detect = [] - deny = [] - disrupt = [] - degrade = [] - deceive = [] - destroy = [] - deter = [] - - stages = [ - ('preparation', preparation), ('identification', identification), - ('containment', containment), ('eradication', eradication), - ('recovery', recovery), ('lessons_learned', lessons_learned), - ('detect', detect), ('deny', deny), ('disrupt', disrupt), - ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), - ('deter', deter) - ] - - for stage_name, stage_list in stages: - try: - for task in self.rp_parsed_file.get(stage_name): - action = ATCutils.read_yaml_file( - ATCconfig.get('response_actions_dir') - + '/' + task + '.yml' - ) - - action_title = action.get('id')\ - + ": "\ - + ATCutils.normalize_react_title(action.get('title')) - - if self.apipath and self.auth and self.space: - stage_list.append( - (action_title, - str(ATCutils.confluence_get_page_id( - self.apipath, self.auth, - self.space, action_title) - ) + (action_title, + str(ATCutils.confluence_get_page_id( + self.apipath, self.auth, + self.space, action_title) ) - ) - else: - stage_list.append((action_title, "")) - - except TypeError: - pass - - # change stages name to more pretty format - stages = [(stage_name.replace('_', ' ').capitalize(), stage_list) - for stage_name, stage_list in stages] - - self.rp_parsed_file.update({'stages_with_id': stages}) - - # get descriptions for response actions - - preparation = [] - identification = [] - containment = [] - eradication = [] - recovery = [] - lessons_learned = [] - detect = [] - deny = [] - disrupt = [] - degrade = [] - deceive = [] - destroy = [] - deter = [] - - stages = [ - ('preparation', preparation), ('identification', identification), - ('containment', containment), ('eradication', eradication), - ('recovery', recovery), ('lessons_learned', lessons_learned), - ('detect', detect), ('deny', deny), ('disrupt', disrupt), - ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), - ('deter', deter) - ] - - # grab workflow per action in each IR stages - # error handling for playbooks with empty stages - for stage_name, stage_list in stages: - try: - for task in self.rp_parsed_file.get(stage_name): - action = ATCutils.read_yaml_file( - ATCconfig.get('response_actions_dir') - + '/' + task + '.yml') - stage_list.append( - (action.get('description'), - action.get('workflow')) + ) ) - except TypeError: - pass + else: + stage_list.append((action_title, "")) - # change stages name to more pretty format - stages = [(stage_name.replace('_', ' ').capitalize(), stage_list) - for stage_name, stage_list in stages] + except TypeError: + pass - self.rp_parsed_file.update({'stages': stages}) - self.rp_parsed_file.update( - {'workflow': - self.rp_parsed_file.get('workflow') - } - ) - self.rp_parsed_file.update( - {'description': self.rp_parsed_file - .get('description').strip()} - ) + # change stages name to more pretty format + stages = [(stage_name.replace('_', ' ').capitalize(), stage_list) + for stage_name, stage_list in stages] + + self.rp_parsed_file.update({'stages_with_id': stages}) + + # get descriptions for response actions + + preparation = [] + identification = [] + containment = [] + eradication = [] + recovery = [] + lessons_learned = [] + detect = [] + deny = [] + disrupt = [] + degrade = [] + deceive = [] + destroy = [] + deter = [] + + stages = [ + ('preparation', preparation), ('identification', identification), + ('containment', containment), ('eradication', eradication), + ('recovery', recovery), ('lessons_learned', lessons_learned), + ('detect', detect), ('deny', deny), ('disrupt', disrupt), + ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), + ('deter', deter) + ] + + # grab workflow per action in each IR stages + # error handling for playbooks with empty stages + for stage_name, stage_list in stages: + try: + for task in self.rp_parsed_file.get(stage_name): + action = ATCutils.read_yaml_file( + ATCconfig.get('response_actions_dir') + + '/' + task + '.yml') + stage_list.append( + (action.get('description'), + action.get('workflow')) + ) + except TypeError: + pass + + # change stages name to more pretty format + stages = [(stage_name.replace('_', ' ').capitalize(), stage_list) + for stage_name, stage_list in stages] + + self.rp_parsed_file.update({'stages': stages}) + self.rp_parsed_file.update( + {'workflow': + self.rp_parsed_file.get('workflow') + } + ) + self.rp_parsed_file.update( + {'description': self.rp_parsed_file + .get('description').strip()} + ) # Render self.content = template.render(self.rp_parsed_file) - - def save_markdown_file(self, atc_dir=ATCconfig.get('md_name_of_root_directory')): - """Write content (md template filled with data) to a file""" - - base = os.path.basename(self.yaml_file) - title = os.path.splitext(base)[0] - - file_path = atc_dir + self.parent_title + "/" + \ - title + ".md" - - return ATCutils.write_file(file_path, self.content) diff --git a/scripts/responsestage.py b/scripts/responsestage.py index e5c2bcb..9613e79 100755 --- a/scripts/responsestage.py +++ b/scripts/responsestage.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -from scripts.atcutils import ATCutils +from atcutils import ATCutils from jinja2 import Environment, FileSystemLoader -from scripts.react_mapping import rs_mapping +from react_scripts.react_mapping import rs_mapping import os -ATCconfig = ATCutils.load_config("scripts/config.yml") +ATCconfig = ATCutils.load_config("config.yml") +env = Environment(loader=FileSystemLoader('templates')) class ResponseStage: """Class for the Playbook Stage entity""" - def __init__(self, yaml_file): + def __init__(self, yaml_file, apipath=None, auth=None, space=None): """Init method""" # Init vars + self.apipath = apipath + self.auth = auth + self.space = space self.yaml_file = yaml_file # The name of the directory containing future markdown Response_Stages - self.parent_title = "Response_Stages" # Init methods self.parse_into_fields(self.yaml_file) @@ -26,7 +29,7 @@ class ResponseStage: def parse_into_fields(self, yaml_file): """Description""" - self.ra_parsed_file = ATCutils.read_yaml_file(yaml_file) + self.rs_parsed_file = ATCutils.read_yaml_file(yaml_file) def render_template(self, template_type): """Description @@ -34,28 +37,29 @@ class ResponseStage: - "markdown" """ - if template_type not in ["markdown"]: + if template_type not in ["confluence"]: raise Exception( "Bad template_type. Available values:" + - " [\"markdown\"]") - - # Point to the templates directory - env = Environment(loader=FileSystemLoader('scripts/templates')) + " \"confluence\"]") template = env.get_template( - 'markdown_responsestage_template.md.j2' + 'confluence_responsestage_template.html.j2' ) - self.ra_parsed_file.update( - {'description': self.ra_parsed_file + self.rs_parsed_file.update( + {'description': self.rs_parsed_file .get('description').strip()} ) - ras, ra_paths = ATCutils.load_yamls_with_paths(ATCconfig.get('response_actions_dir')) - ra_filenames = [ra_path.split('/')[-1].replace('.yml', '') for ra_path in ra_paths] + ras, ra_paths = ATCutils.load_yamls_with_paths( + ATCconfig.get('response_actions_dir')) + ra_filenames = [ra_path.split('/')[-1].replace('.yml', '') + for ra_path in ra_paths] + rs_id = self.rs_parsed_file.get('id') - rs_id = self.ra_parsed_file.get('id') + self.rs_parsed_file.update( + {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) stage_list = [] @@ -65,20 +69,28 @@ class ResponseStage: ra_filename = ra_filenames[i] ra_title = ATCutils.normalize_react_title(ras[i].get('title')) ra_description = ras[i].get('description').strip() - stage_list.append((ra_id, ra_filename, ra_title, ra_description)) + ra_confluence_page_name = ra_id + ": " + ra_title + print(ra_confluence_page_name) + + if self.apipath and self.auth and self.space: + ra_confluence_page_id = str(ATCutils.confluence_get_page_id( + self.apipath, self.auth, self.space, ra_confluence_page_name) + ) + else: + ra_confluence_page_id = "" - self.ra_parsed_file.update({'stage_list': sorted(stage_list)}) + print(ra_confluence_page_id) + stage_list.append( + (ra_id, ra_filename, ra_title, ra_description, ra_confluence_page_id)) - self.content = template.render(self.ra_parsed_file) + new_title = self.rs_parsed_file.get('id')\ + + ": "\ + + ATCutils.normalize_react_title(self.rs_parsed_file.get('title')) - def save_markdown_file(self, - atc_dir=ATCconfig.get('md_name_of_root_directory')): - """Write content (md template filled with data) to a file""" + self.rs_parsed_file.update( + {'title': new_title} + ) - base = os.path.basename(self.yaml_file) - title = os.path.splitext(base)[0] + self.rs_parsed_file.update({'stage_list': sorted(stage_list)}) - file_path = atc_dir + self.parent_title + "/" + \ - title + ".md" - - return ATCutils.write_file(file_path, self.content) + self.content = template.render(self.rs_parsed_file) diff --git a/scripts/templates/confluence_responseaction_template.html.j2 b/scripts/templates/confluence_responseaction_template.html.j2 index ee0673c..4fd27cb 100755 --- a/scripts/templates/confluence_responseaction_template.html.j2 +++ b/scripts/templates/confluence_responseaction_template.html.j2 @@ -26,9 +26,15 @@
+{{ description }} +{%- if stage_list is not none and stage_list|length -%} +
+ ID |
+
+ Name |
+
+ Description + |
+
---|---|---|
+ | ++ | +
+ {{ ra_description }} |
+