Files
macos_security/scripts/modify_rule.py
2022-05-05 07:13:36 -04:00

249 lines
8.2 KiB
Python

#!/usr/bin/env python3
# filename: generate_guidance.py
# description: Process a given keyword, and output a baseline file
from operator import truediv
import os.path
import glob
import os
import yaml
from yaml.representer import SafeRepresenter
import argparse
class LiteralString(str):
pass
def change_style(style, representer):
def new_representer(dumper, data):
scalar = representer(dumper, data)
scalar.style = style
return scalar
return new_representer
def represent_none(self, _):
return self.represent_scalar('tag:yaml.org,2002:null', '')
represent_literal_str = change_style('|', SafeRepresenter.represent_str)
yaml.add_representer(LiteralString, represent_literal_str)
yaml.add_representer(type(None), represent_none)
class MacSecurityRule():
def __init__(self, title, rule_id, severity, discussion, check, fix, cci, cce, nist_controls, disa_stig, srg, macos, odv, tags, result_value, mobileconfig, mobileconfig_info):
self.rule_title = title
self.rule_id = rule_id
self.rule_severity = severity
self.rule_discussion = discussion
self.rule_check = check
self.rule_fix = fix
self.rule_cci = cci
self.rule_cce = cce
self.rule_80053r4 = nist_controls
self.rule_disa_stig = disa_stig
self.rule_srg = srg
self.rule_macOS = macos
self.rule_odv = odv
self.rule_result_value = result_value
self.rule_tags = tags
self.rule_mobileconfig = mobileconfig
self.rule_mobileconfig_info = mobileconfig_info
def create_asciidoc(self, adoc_rule_template):
"""Pass an AsciiDoc template as file object to return formatted AsciiDOC"""
rule_adoc = ""
rule_adoc = adoc_rule_template.substitute(
rule_title=self.rule_title,
rule_id=self.rule_id,
rule_severity=self.rule_severity,
rule_discussion=self.rule_discussion,
rule_check=self.rule_check,
rule_fix=self.rule_fix,
rule_cci=self.rule_cci,
rule_80053r4=self.rule_80053r4,
rule_disa_stig=self.rule_disa_stig,
rule_srg=self.rule_srg,
rule_result=self.rule_result_value
)
return rule_adoc
def get_rule_yaml(rule_file, custom=False):
""" Takes a rule file, checks for a custom version, and returns the yaml for the rule
"""
with open(rule_file) as r:
rule_yaml = yaml.load(r, Loader=yaml.SafeLoader)
return rule_yaml
def collect_rules():
"""Takes a baseline yaml file and parses the rules, returns a list of containing rules
"""
all_rules = []
#expected keys and references
keys = ['mobileconfig',
'macOS',
'severity',
'title',
'check',
'fix',
'odv',
'tags',
'id',
'references',
'result',
'discussion']
references = ['disa_stig',
'cci',
'cce',
'800-53r4',
'srg']
for rule in glob.glob('../rules/**/*.yaml',recursive=True):
rule_yaml = get_rule_yaml(rule, custom=False)
for key in keys:
try:
rule_yaml[key]
except:
#print "{} key missing ..for {}".format(key, rule)
rule_yaml.update({key: "missing"})
if key == "references":
for reference in references:
try:
rule_yaml[key][reference]
except:
#print("expected reference '{}' is missing in key '{}' for rule{}".format(reference, key, rule))
rule_yaml[key].update({reference: ["None"]})
all_rules.append(MacSecurityRule(rule_yaml['title'].replace('|', '\|'),
rule_yaml['id'].replace('|', '\|'),
rule_yaml['severity'].replace('|', '\|'),
rule_yaml['discussion'].replace('|', '\|'),
rule_yaml['check'].replace('|', '\|'),
rule_yaml['fix'].replace('|', '\|'),
rule_yaml['references']['cci'],
rule_yaml['references']['cce'],
rule_yaml['references']['800-53r4'],
rule_yaml['references']['disa_stig'],
rule_yaml['references']['srg'],
rule_yaml['macOS'],
rule_yaml['odv'],
rule_yaml['tags'],
rule_yaml['result'],
rule_yaml['mobileconfig'],
rule_yaml['mobileconfig_info']
))
return all_rules
def create_args():
"""configure the arguments used in the script, returns the parsed arguments
"""
parser = argparse.ArgumentParser(
description='Given a keyword tag, generate a generic baseline.yaml file containing rules with the tag.')
parser.add_argument("macOS", default=None,
help="Version of macOS you are building a baseline for.", action="store")
return parser.parse_args()
def getCCE(rule):
try:
return rule["references"]["cce"]
except KeyError:
print(f"no CCE for {rule['id']}")
return ["N/A"]
def getSTIG(rule):
try:
return rule["references"]["disa_stig"]
except KeyError:
print(f"no disa_stig for {rule['id']}")
return ["N/A"]
def getDiscussion(rule):
try:
return rule["discussion"]
except KeyError:
print(f"no discussion for {rule['id']}")
return "N/A"
def getCheck(rule):
try:
return rule["check"]
except KeyError:
print(f"no check for {rule['id']}")
return "N/A"
def getFix(rule):
try:
return rule["fix"]
except KeyError:
print(f"no fix for {rule['id']}")
return "N/A"
def write_odv_custom_rule(rule, odv):
print(f"Writing custom rule for {rule.rule_id} to include value {odv}")
odv_yaml = f'odv: {odv}'
odv_output_file = open(f"../custom/rules/{rule.rule_id}.yaml", 'w')
odv_output_file.write(odv_yaml)
return
def main():
args = create_args()
try:
file_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(file_dir)
# stash current working directory
original_working_directory = os.getcwd()
# switch to the scripts directory
os.chdir(file_dir)
all_rules = collect_rules()
build_path = os.path.join(parent_dir, 'build', args.macOS)
if not (os.path.isdir(build_path)):
try:
os.makedirs(build_path)
except OSError:
print(f"Creation of the directory {build_path} failed")
except IOError as msg:
parser.error(str(msg))
# found_rules = []
# for rule in all_rules:
# print(rule)
mont_rule_list = []
for rule in glob.glob('../rules/**/*.yaml',recursive=True):
rule_yaml = get_rule_yaml(rule, custom=False)
mont_rule_list.append(rule_yaml)
for rule in mont_rule_list:
cce = getCCE(rule)
stig = getSTIG(rule)
check = getCheck(rule)
discussion = getDiscussion(rule)
fix = getFix(rule)
rule['discussion'] = LiteralString(discussion)
rule['check'] = LiteralString(check)
rule['fix'] = LiteralString(fix)
rule['references']['cce'] = LiteralString("$VALUE")
rule['references']['disa_stig'] = LiteralString("$VALUE")
rule['macOS']={args.macOS : {'references': {'cce' : cce, 'disa_stig' : stig } } }
output_path = os.path.join(build_path, rule['id'] + '.yaml')
with open(output_path, 'w') as yaml_file:
yaml.dump(rule, yaml_file, indent=2, sort_keys=False)
# finally revert back to the prior directory
os.chdir(original_working_directory)
if __name__ == "__main__":
main()