#!/usr/bin/env python3 import argparse import sys import os import os.path import yaml import glob import re import warnings from pathlib import Path from datetime import datetime import shutil from time import sleep warnings.filterwarnings("ignore", category=DeprecationWarning) def main(): now = datetime.now() date_time_string = now.strftime("%Y-%m-%dT%H:%M:%S") output = "" parser = argparse.ArgumentParser(description='Given a profile, create oval checks.') parser.add_argument("baseline", default=None, help="Baseline YAML file used to create the oval.", type=argparse.FileType('rt')) results = parser.parse_args() try: output_basename = os.path.basename(results.baseline.name) output_filename = os.path.splitext(output_basename)[0] baseline_name = os.path.splitext(output_basename)[0] file_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(file_dir) build_path = os.path.join(parent_dir, 'build', f'{baseline_name}') output = build_path + "/" + baseline_name + ".xml" if not (os.path.isdir(build_path)): try: os.makedirs(build_path) except OSError: print(f"Creation of the directory {build_path} failed") print('Profile YAML:', results.baseline.name) print('Output path:', output) except IOError as msg: parser.error(str(msg)) profile_yaml = yaml.load(results.baseline, Loader=yaml.SafeLoader) x = 1 ovalPrefix = ''' 5.11.2 {} Copyright (c) 2020, NIST. macOS Security Compliance Project '''.format(date_time_string) oval_definition = str() oval_test = str() oval_object = str() oval_state = str() oval_variable = str() print() for sections in profile_yaml['profile']: for profile_rule in sections['rules']: for rule_file in glob.glob('../rules/*/{}.yaml'.format(profile_rule)): if "srg" in rule_file or "supplemental" in rule_file: continue with open(rule_file) as r: rule_yaml = yaml.load(r, Loader=yaml.SafeLoader) if "inherent" in rule_yaml['tags'] or "n_a" in rule_yaml['tags'] or "permanent" in rule_yaml['tags']: continue if "time_machine" in rule_yaml['id'] and "encrypted" in rule_yaml['id']: print(rule_yaml['id'] + " - Manual Check Required") continue if "bluetooth" in rule_yaml['id'] and "unpaired" in rule_yaml['id']: print(rule_yaml['id'] + " - Manual Check Required") continue if rule_yaml['check'][0] != "/" and "[source,bash]" not in rule_yaml['fix']: print(rule_yaml['id'] + " - Manual Check") continue if "hint" in rule_yaml['check'] and "dscl" in rule_yaml['check']: print(rule_yaml['id'] + " - no relevant oval") continue if "manual" in rule_yaml['tags']: print(rule_yaml['id'] + " - Manual Check") continue if "eficheck" in rule_yaml['check']: print(rule_yaml['id'] + " - eficheck - no relevant oval") continue if "newsyslog.conf" in rule_yaml['check'] or "asl.conf" in rule_yaml['check'] or "aslmanager" in rule_yaml['check']: print(rule_yaml['id'] + " - Manual Check Required") continue if "/usr/bin/pwpolicy getaccountpolicies" in rule_yaml['check']: print(rule_yaml['id'] + " - pwpolicy getaccountpolicies - no relevant oval") continue if "find" in rule_yaml['check'].split(" ")[0]: print(rule_yaml['id'] + " - no relevant oval") continue if "os_home_folders_secure" in rule_file: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'], rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' .* oval:mscp:ste:{} '''.format(rule_yaml['id'],x,x,x+999,x+999) oval_state = oval_state + ''' true true true false false false false false false ^[^_\s].* 0 0 /usr/bin/false '''.format(rule_yaml['id'],x,x+999) oval_variable = oval_variable + ''' '''.format(x,x+999) x = x + 1 continue if rule_yaml['mobileconfig']: if "spctl" in rule_yaml['check']: if "verbose" in rule_yaml['check']: continue else: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' '''.format(x,rule_yaml['id']) oval_state = oval_state + ''' true '''.format(rule_yaml['id'],x) x += 1 continue for payload_type, info in rule_yaml['mobileconfig_info'].items(): if payload_type == "com.apple.systempolicy.control": continue if payload_type == "com.apple.ManagedClient.preferences": for payload_domain, settings in info.items(): for key, value in settings.items(): state_kind = "" if type(value) == bool: state_kind = "boolean" elif type(value) == int: state_kind = "int" elif type(value) == str: state_kind = "string" oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) if payload_domain == "com.apple.dock": oval_object = oval_object + ''' /Library/Preferences/com.apple.loginwindow.plist /plist/dict/key[string()="lastUserName"]/following-sibling::*[1]/text() //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(x+1999,key,x,x,key) oval_variable = oval_variable + ''' /Library/Managed Preferences/ /com.apple.dock.plist '''.format(x,x+1999) else: oval_object = oval_object + ''' /Library/Managed Preferences/{}.plist //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(rule_yaml['id'],x,payload_domain,key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) x += 1 continue for key, value in info.items(): if key == "familyControlsEnabled": xpath_search = "" if len(info) > 1: xpath_search = info['pathBlackList'] oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Managed Preferences/com.apple.applicationaccess.new.plist boolean(plist/dict/array/string/text() = "{}") '''.format(rule_yaml['id'],x,str(xpath_search).replace('[',"").replace(']',"").replace("'","")) oval_state = oval_state + ''' true '''.format(rule_yaml['id'],x) x = x + 1 continue else: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Managed Preferences/{}.plist'''.format(rule_yaml['id'],x,payload_type) state_kind = "" if type(value) == bool: oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) state_kind = "boolean" elif type(value) == int: state_kind = "int" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) elif type(value) == str: state_kind = "string" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) x = x + 1 continue if payload_type == "com.apple.finder": oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Preferences/com.apple.loginwindow.plist /plist/dict/key[string()="lastUserName"]/following-sibling::*[1]/text() '''.format(x+1999,rule_yaml['id'],x,x) state_kind = "" if type(value) == bool: oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) state_kind = "boolean" elif type(value) == int: state_kind = "int" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) elif type(value) == str: state_kind = "string" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) oval_variable = oval_variable + ''' /Library/Managed Preferences/ /com.apple.finder.plist '''.format(x,x+1999) x += 1 continue if payload_type == "com.apple.DiscRecording": oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Preferences/com.apple.loginwindow.plist /plist/dict/key[string()="lastUserName"]/following-sibling::*[1]/text() '''.format(x+1999,rule_yaml['id'],x,x) state_kind = "" if type(value) == bool: oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) state_kind = "boolean" elif type(value) == int: state_kind = "int" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) elif type(value) == str: state_kind = "string" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) oval_variable = oval_variable + ''' /Library/Managed Preferences/ /com.apple.DiscRecording.plist '''.format(x,x+1999) x += 1 continue if payload_type == "com.apple.Safari" and key == "AutoOpenSafeDownloads": oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Preferences/com.apple.loginwindow.plist /plist/dict/key[string()="lastUserName"]/following-sibling::*[1]/text() '''.format(x+1999,rule_yaml['id'],x,x) state_kind = "" if type(value) == bool: oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) state_kind = "boolean" elif type(value) == int: state_kind = "int" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) elif type(value) == str: state_kind = "string" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) oval_variable = oval_variable + ''' /Library/Managed Preferences/ /com.apple.Safari.plist '''.format(x,x+1999) x += 1 continue if payload_type == "com.apple.systempreferences" and key == "DisabledPreferencePanes" or payload_type == "com.apple.systempreferences" and key == "HiddenPreferencePanes": oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Preferences/com.apple.loginwindow.plist /plist/dict/key[string()="lastUserName"]/following-sibling::*[1]/text() /plist/dict/key[string()="{}"]/following-sibling::*[1]/string[string()="{}"]/text() '''.format(x+1999,rule_yaml['id'],x,x,key,str(value).strip('[]').strip("'")) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,str(value).strip('[]').strip("'")) oval_variable = oval_variable + ''' /Library/Managed Preferences/ /com.apple.systempreferences.plist '''.format(x,x+1999) x += 1 continue state_kind = "" if type(value) == bool: state_kind = "boolean" elif type(value) == int: state_kind = "int" elif type(value) == str: state_kind = "string" else: continue oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Managed Preferences/{}.plist'''.format(rule_yaml['id'],x,payload_type) if state_kind == "boolean": oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) else: oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_kind,value) x += 1 continue else: command = rule_yaml['check'].split("/") if "sntp" in rule_yaml['check']: x += 1 print(rule_yaml['id'] + " - No relevant oval test") continue if "SPStorageDataType" in rule_yaml['check']: x += 1 print(rule_yaml['id'] + " - No relevant oval test") continue if "fdesetup" in command[3]: x += 1 print(rule_yaml['id'] + " - No relevant oval test") continue if "profiles" in command[3]: if "/usr/bin/profiles status -type enrollment" in rule_yaml['check']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],x,x+899,x+799) oval_test = oval_test + ''' '''.format(x,x,x+899,x+899,x+799,x+799) oval_object = oval_object + ''' /Library/Managed Preferences/com.apple.extensiblesso.plist /Library/Managed Preferences/com.apple.syspolicy.kernel-extension-policy.plist /Library/Managed Preferences/com.apple.TCC.configuration-profile-policy.plist '''.format(x,x+899,x+799) x += 1 continue if "csrutil" in command[3]: if "authenticated-root" in command[3]: print(rule_yaml['id'] + " - No relevant oval test") continue oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' SPSoftwareDataType //*[contains(text(), "system_integrity")]/following-sibling::string[position()=1]/text() '''.format(rule_yaml['id'],x) oval_state = oval_state + ''' SPSoftwareDataType //*[contains(text(), "system_integrity")]/following-sibling::string[position()=1]/text() integrity_enabled '''.format(rule_yaml['id'],x) x += 1 continue if "pfctl" in rule_yaml['check']: print(rule_yaml['id'] + " - No relevant oval test") x += 1 continue if "dump-keychain" in rule_yaml['check']: print(rule_yaml['id'] + " - No relevant oval test") x += 1 continue if "mdmclient" in command[3]: print(rule_yaml['id'] + " - No relevant oval test") x += 1 continue if "nvram" in command[3]: print(rule_yaml['id'] + " - No relevant oval test") x += 1 continue if "pmset" in command[3] and "standby" in rule_yaml['check']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'] +"_standbydelayhigh",x, rule_yaml['id'] +"_standbydelaylow",x+877, rule_yaml['id'] +"_highstandbythreshold",x+888) oval_test = oval_test + ''' '''.format(rule_yaml['id'] + "_standbydelayhigh",x,x,x) oval_test = oval_test + ''' '''.format(rule_yaml['id'] + "_standbydelaylow",x+877,x+877,x+877) oval_test = oval_test + ''' '''.format(rule_yaml['id'] + "_highstandbythreshold",x+888,x+888,x+888) standbydelayhigh = str() standbydelaylow = str() highstandbythreshold = str() for line in rule_yaml['fix'].split("----")[1].split("\n"): if line == "": continue if "standbydelayhigh" in line: standbydelayhigh = line.split(" ")[-1].rstrip() if "standbydelaylow" in line: standbydelaylow = line.split(" ")[-1].rstrip() if "highstandbythreshold" in line: highstandbythreshold = line.split(" ")[-1].rstrip() oval_object = oval_object + ''' SPHardwareDataType //*[contains(text(), "platform_UUID")]/following-sibling::string[position()=1]/text() '''.format("hardware UUID",x+999) oval_variable = oval_variable + ''' /Library/Preferences/com.apple.PowerManagement. .plist '''.format(x,x+999) oval_object = oval_object + ''' '''.format(rule_yaml['id'] + "_standbydelayhigh",x,x) oval_object = oval_object + ''' boolean(plist/dict[key="AC Power"]/dict[key="{}"]/integer/text() = "{}") '''.format("High Standby Delay",standbydelayhigh) oval_object = oval_object + ''' '''.format(rule_yaml['id'] + "_standbydelaylow",x+877, x) oval_object = oval_object + ''' boolean(plist/dict[key="AC Power"]/dict[key="{}"]/integer/text() = "{}") '''.format("Standby Delay",standbydelaylow) oval_object = oval_object + ''' '''.format(rule_yaml['id'] + "_highstandbythreshold",x+888, x) oval_object = oval_object + ''' boolean(plist/dict[key="AC Power"]/dict[key="{}"]/integer/text() = "{}") '''.format("Standby Battery Threshold",highstandbythreshold) oval_state = oval_state + ''' true '''.format(rule_yaml['id'] + "_standbydelayhigh",x) oval_state = oval_state + ''' true '''.format(rule_yaml['id'] + "_standbydelaylow",x+877) oval_state = oval_state + ''' true '''.format(rule_yaml['id'] + "_highstandbythreshold",x+888) x += 1 continue if "pmset" in command[3]: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' /Library/Preferences/com.apple.PowerManagement.plist'''.format(rule_yaml['id'],x) pmset_key = str() if "powernap" in rule_yaml['check']: pmset_key = "DarkWakeBackgroundTasks" if "womp" in rule_yaml['check']: pmset_key = "Wake On LAN" oval_object = oval_object + ''' boolean(plist/dict[key="AC Power"]/dict[key="{}"]/integer/text() = "{}") '''.format(pmset_key,rule_yaml['fix'].split("----")[1].replace("\n","")[-1]) oval_state = oval_state + ''' true '''.format(rule_yaml['id'],x) x += 1 continue if "socketfilterfw" in rule_yaml['check']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) if rule_yaml['check'].split()[1] == "--getloggingmode": firewall_variable = "loggingenabled" elif rule_yaml['check'].split()[1] == "--getstealthmode": firewall_variable = "stealthenabled" elif rule_yaml['check'].split()[1] == "--getglobalstate": firewall_variable = "globalstate" oval_object = oval_object + ''' /Library/Preferences/com.apple.alf.plist //*[contains(text(), "{}")]/following-sibling::*[1]/text() 1 '''.format(rule_yaml['id'],x,firewall_variable) oval_state = oval_state + ''' 1 '''.format(rule_yaml['id'],x) x += 1 continue if "systemsetup" in command[3]: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' '''.format(rule_yaml['id'],x) state_test = "" if "-getnetworktimeserver" in rule_yaml['check']: timeservers = rule_yaml['result']['string'] state_test = ''' {} '''.format(timeservers) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,state_test) abc = 0 if "defaults" in rule_yaml['check'] and "grep" in rule_yaml['check'] and "CURRENT_USER" in rule_yaml['check']: regex = r"(?<=\()(.*?)(?=\))" test_str = rule_yaml['check'].split("grep")[1] matches = re.finditer(regex, test_str, re.MULTILINE) matchy_match = "" for matchNum, match in enumerate(matches, start=1): matchy_match = match.group() oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) for multi_grep in matchy_match.split("|"): oval_definition = oval_definition + ''' '''.format(rule_yaml['id']+"_"+str(abc),x) oval_test = oval_test + ''' '''.format(rule_yaml['id']+"_"+str(abc),x,x,x) key = matchy_match.split("|")[abc].split(" = ")[0].replace("\"","") value = matchy_match.split("|")[abc].split(" = ")[1].replace(";","") if "$CURRENT_USER" in rule_yaml['check']: oval_object = oval_object + ''' .* oval:mscp:ste:{} '''.format(x+1999,x+1999) oval_state = oval_state + ''' ^[^_\s].* 0 0 /usr/bin/false '''.format(x+1999) plist = rule_yaml['check'].split("read")[1].split()[0].replace(".plist","") oval_variable = oval_variable + ''' /Library/Preferences/{}. plist '''.format(x,x+1999,plist) oval_object = oval_object + ''' '''.format(rule_yaml['id']+"_"+str(abc),x,x) oval_datatype = "" try: int(value) oval_datatype = "int" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) except: if value.lower() == "true" or value.lower == "false": oval_datatype = "boolean" oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) else: oval_datatype = "string" oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id']+"_"+str(abc),x,oval_datatype,value) abc =+ 1 x = x+1 oval_definition = oval_definition + ''' ''' oval_definition = re.sub('(?=\n\[NOTE\])(?s)(.*)\=\n<', '<', oval_definition) x = x+1 break if "defaults" in rule_yaml['check']: if rule_yaml['id'] == "sysprefs_hot_corners_secure": oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x,rule_yaml['id'],x+5000,rule_yaml['id'],x+5001,rule_yaml['id'],x+5002) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x+5000,x+5000,x+5000) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x+5001,x+5001,x+5001) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x+5002,x+5002,x+5002) plist = rule_yaml['check'].split("read")[1].split()[0].replace(".plist","") check_length = len(rule_yaml['check'].split()) key = rule_yaml['check'].split("\n")[0].replace(" 2>/dev/null","").split()[-1].replace('"','').replace(")",'') oval_object = oval_object + ''' .* oval:mscp:ste:{} '''.format(x+1999,x+1999,rule_yaml['id'],x,x) oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) key = rule_yaml['check'].split("\n")[1].replace(" 2>/dev/null","").split()[-1].replace('"','').replace(")",'') oval_object = oval_object + ''' '''.format(rule_yaml['id'],x+5000,x) oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) key = rule_yaml['check'].split("\n")[2].replace(" 2>/dev/null","").split()[-1].replace('"','').replace(")",'') oval_object = oval_object + ''' '''.format(rule_yaml['id'],x+5001,x) oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) key = rule_yaml['check'].split("\n")[3].replace(" 2>/dev/null","").split()[-1].replace('"','').replace(")",'') oval_object = oval_object + ''' '''.format(rule_yaml['id'],x+5002,x) oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' ^[^_\s].* 0 0 /usr/bin/false '''.format(x+1999) after_user = plist.split('"')[2] oval_variable = oval_variable + ''' {} .plist '''.format(x,x+1999,after_user,x+999) try: check_if = rule_yaml['check'].split("\n")[5] modifier = 0 for n in check_if.split(): if n.replace('"',"").isdigit(): if modifier >= 4999: modifier = modifier + 1 oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x+modifier,n.replace('"',"")) if modifier == 0: modifier = 4999 x = x + 1 continue except: x = x + 1 continue oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) plist = rule_yaml['check'].split("read")[1].split()[0].replace(".plist","") if "ByHost" in rule_yaml['fix'] or "currentHost" in rule_yaml['fix']: oval_object = oval_object + ''' SPHardwareDataType //*[contains(text(), "platform_UUID")]/following-sibling::string[position()=1]/text() '''.format("hardware UUID",x+999) if "$CURRENT_USER" in rule_yaml['check']: check_length = len(rule_yaml['check'].split()) key = rule_yaml['check'].split()[check_length-1] oval_object = oval_object + ''' .* oval:mscp:ste:{} '''.format(x+1999,x+1999,rule_yaml['id'],x,x) try: rule_yaml['result']['boolean'] oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) except: oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' ^[^_\s].* 0 0 /usr/bin/false '''.format(x+1999) oval_variable = oval_variable + ''' /Library/Preferences/ByHost/{}. .plist '''.format(x,x+1999,plist,x+999) else: check_length = len(rule_yaml['check'].split()) key = rule_yaml['check'].replace(" 2>/dev/null","").split()[check_length-1] oval_object = oval_object + ''' '''.format(rule_yaml['id'],x,x) try: rule_yaml['result']['boolean'] oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) except: oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_variable = oval_variable + ''' {}. .plist '''.format(x,plist,x+999) elif "$CURRENT_USER" in rule_yaml['check']: check_length = len(rule_yaml['check'].split()) key = rule_yaml['check'].replace(" 2>/dev/null","").split()[-1] oval_object = oval_object + ''' .* oval:mscp:ste:{} '''.format(x+1999,x+1999,rule_yaml['id'],x,x) try: rule_yaml['result']['boolean'] oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(key) except: oval_object = oval_object + '''//*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(key) oval_state = oval_state + ''' ^[^_\s].* 0 0 /usr/bin/false '''.format(x+1999) oval_variable = oval_variable + ''' /Library/Preferences/{}. plist '''.format(x,x+1999,plist,x+999) else: if plist[-6:] != ".plist": plist = plist + ".plist" plist_key = rule_yaml['check'].replace(" 2>/dev/null","").split(" ")[3].rstrip() oval_object = oval_object + ''' {}'''.format(rule_yaml['id'],x,plist) try: rule_yaml['result']['boolean'] oval_object = oval_object + ''' name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(plist_key) except: oval_object = oval_object + ''' //*[contains(text(), "{}")]/following-sibling::*[1]/text() '''.format(plist_key) datatype = "" plist_key = rule_yaml['check'].split(" ")[3].rstrip() for key in rule_yaml['result']: datatype = key if datatype == "integer": oval_datatype = "int" else: oval_datatype = datatype if oval_datatype == "boolean" and rule_yaml['result'][datatype] == 0: value = "false" elif oval_datatype == "boolean" and rule_yaml['result'][datatype] == 1: value = "true" else: value = rule_yaml['result'][datatype] oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,oval_datatype,value) oval_definition = re.sub('(?=\n\[NOTE\])(?s)(.*)\=\n<', '<', oval_definition) x = x+1 continue if "security" in command[3]: if rule_yaml['check'].split()[1] == "authorizationdb": check = rule_yaml['check'].split("|") authdb = rule_yaml['check'].split()[3] if len(check) > 2: matches = re.findall(r'(?<=\>)(.*)(?=\<)',check[1]) key = str(matches).replace("[","").replace("]","").replace("'","") length = len(check[2].split()) last_string = check[2].split()[length-1].replace('"',"").replace("<","").replace(">","").replace("/","") oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' {} boolean(//key[text()="{}"]/following-sibling::{}) '''.format(rule_yaml['id'],x,authdb,key,last_string) oval_state = oval_state + ''' true '''.format(rule_yaml['id'],x) else: key = (check[1].split()[2].replace("'","")) oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' {} //*[contains(text(), "{}")]/text() '''.format(rule_yaml['id'],x,authdb,key) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,key) x += 1 continue if "/bin/rm" in rule_yaml['fix'] and "/bin/ls" in rule_yaml['check']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x,rule_yaml['id'],x) path = rule_yaml['fix'].split("----")[1].split(" ")[-1] oval_object = oval_object + ''' {} '''.format(x,rule_yaml['id'],path.rstrip()) x += 1 continue if "ls" in command[2] or "stat" in command[3].split()[0]: if '/Library/Security/PolicyBanner.rtf' in rule_yaml['check']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x,rule_yaml['id'],x+2999) oval_test = oval_test + ''' '''.format(x,rule_yaml['id'],x,x+2999,rule_yaml['id'],x+2999) oval_object = oval_object + ''' /Library/Security/PolicyBanner.rtf /Library/Security/PolicyBanner.rtfd '''.format(x,rule_yaml['id'],x+2999,rule_yaml['id']) x = x + 1 continue s = rule_yaml['check'] config_file = str() oval_variable_need = bool() if "grep" in s.split()[2]: oval_variable_need = True grep_search = re.search('\((.*?)\)', s).group(1) substring = grep_search.split("|")[0] regex = re.search('\'(.*?)\'', substring).group(1) try: regex = re.search('/(.*?)/', regex).group(1) except: regex = regex config_file = substring = grep_search.split("|")[0].split()[-1] oval_object = oval_object + ''' {} {}:\s*(.*)$ 1 '''.format(rule_yaml['id'], x+999, config_file, regex) oval_variable = oval_variable + ''' '''.format(x,rule_yaml['id'],x+999) else: oval_variable_need = False config_file = s.split()[2] s = rule_yaml['fix'] fix_command = re.search('-\n(.*?)\n-', s).group(1).split('$')[0] oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x,rule_yaml['id'],x,x) if "-" in fix_command and "R" in fix_command or rule_yaml['fix'].split("\n")[2][-1] == "*": behavior = '' if "audit" in rule_file: filename = 'current' else: behavior = "" filename = '' if oval_variable_need == True: oval_object = oval_object + ''' {} {} '''.format(rule_yaml['id'],x,behavior,x,filename) else: oval_object = oval_object + ''' {} {} '''.format(rule_yaml['id'],x,behavior,config_file) state_test = "" if "-" in fix_command and "N" in fix_command and "chmod" in fix_command: state_test = ''' false ''' elif "chgrp" in fix_command: state_test = ''' {} '''.format(rule_yaml['result']['integer']) elif "chown" in fix_command: state_test = ''' {} '''.format(rule_yaml['result']['integer']) elif "chmod" in fix_command: perms = fix_command.split()[1] if perms[0] == "0": state_test = ''' false false false''' if perms[0] == "1": state_test = ''' false false true''' elif perms[0] == "2": state_test = ''' false true false''' elif perms[0] == "3": state_test = ''' false true true''' elif perms[0] == "4": state_test = ''' true false false''' elif perms[0] == "5": state_test = ''' true false true''' elif perms[0] == "6": state_test = ''' true true false''' elif perms[0] == "7": state_test = ''' true true true''' if perms[1] == "0": state_test = state_test + ''' false false false''' elif perms[1] == "1": state_test = state_test + ''' false false true''' elif perms[1] == "2": state_test = state_test + ''' false true false''' elif perms[1] == "3": state_test = state_test + ''' false true true''' elif perms[1] == "4": state_test = state_test + ''' true false false''' elif perms[1] == "5": state_test = state_test + ''' true false true''' elif perms[1] == "6": state_test = state_test + ''' true true false''' elif perms[1] == "7": state_test = state_test + ''' true true true''' if perms[2] == "0": state_test = state_test + ''' false false false''' if perms[2] == "1": state_test = state_test + ''' false false true''' elif perms[2] == "1": state_test = state_test + ''' false false true''' elif perms[2] == "2": state_test = state_test + ''' false true false''' elif perms[2] == "3": state_test = state_test + ''' false true true''' elif perms[2] == "4": state_test = state_test + ''' true false false''' elif perms[2] == "5": state_test = state_test + ''' true false true''' elif perms[2] == "6": state_test = state_test + ''' true true false''' elif perms[2] == "7": state_test = state_test + ''' true true true''' oval_state = oval_state + ''' '''.format(rule_yaml['id'],x) + state_test + ''' ''' x += 1 continue if "dscl" in command[3]: if "UserShell" in rule_yaml['check']: shell = rule_yaml['check'].split()[9].replace('"','') oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'],rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x) oval_object = oval_object + ''' {} '''.format(rule_yaml['id'],x,command[5].split()[0]) oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,shell) x += 1 continue if "awk" in command[3]: awk_file = "" awk_search = "" field_sep = "" if "grep -qE" in rule_yaml['fix']: awk_file = rule_yaml['fix'].split(" ")[3].strip(" ") awk_search = rule_yaml['fix'].split(" ")[2].strip("\"") elif "grep" in rule_yaml['check']: awk_file = rule_yaml['check'].split("|")[0].split(" ")[-2] awk_search = rule_yaml['check'].split("|")[-1].split(" ")[-2].strip("\'") else: awk_file = rule_yaml['check'].split("'")[2].strip(" ") awk_search = rule_yaml['check'].split("'")[1].split("/")[1] field_sep = rule_yaml['check'].split("-F")[1].split(" ")[0].replace('\"',"") try: awk_result = rule_yaml['result']['string'] except: awk_result = str(rule_yaml['result']['integer']) awk_search = "^" + awk_search + field_sep + awk_result oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x, rule_yaml['id'], x) oval_object = oval_object + ''' {} {} 1 '''.format(x,rule_yaml['id'],awk_file.rstrip(), awk_search) x += 1 continue if "grep" in command[3] and not "pgrep" in command[3]: if "bannerText" in rule_yaml['check'] or "fips_" in rule_yaml['check']: text_to_find = rule_yaml['check'].split("=")[1].split('"')[1] matches = text_to_find.replace(".","\.").replace(")","\)").replace("(","\(").replace("*","\*") oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x, rule_yaml['id'], x) file_path = rule_yaml["check"].split(" ")[-1].rstrip() oval_object = oval_object + ''' {} {} 1 '''.format(x,rule_yaml['id'],file_path,matches) x += 1 continue else: s = rule_yaml['check'] try: grep_search = re.search('"(.*?)"', s).group(1) except: grep_search = re.search('\'(.*?)\'', s).group(1) grep_file = rule_yaml['check'].split(grep_search,1)[1].split(" ")[1] oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x, rule_yaml['id'], x) oval_object = oval_object + ''' {} {} 1 '''.format(x,rule_yaml['id'],grep_file.rstrip(),grep_search) x += 1 continue if "launchctl" in command[2] or "launchctl" in rule_yaml['fix']: if "disable" in command[2] and "=> true" in rule_yaml['check'] or "unload -w" in rule_yaml['fix']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x,rule_yaml['id'],x+999) oval_test = oval_test + ''' '''.format(rule_yaml['id'],x,x,x,x+999,rule_yaml['id'],x+999) domain = str() if "launchctl" not in rule_yaml['check']: domain = rule_yaml['fix'].split()[4].split('/')[4].replace(".plist","") else: s = command[5].split()[2] domain = re.search('"(.*?)"', s).group(1) oval_object = oval_object + ''' /var/db/com.apple.xpc.launchd/disabled.plist name(//*[contains(text(), "{}")]/following-sibling::*[1]) '''.format(rule_yaml['id'],x,domain,x+999,rule_yaml['id'],domain) status = "" if "enable" in rule_yaml["fix"]: status = "false" else: status = "true" oval_state = oval_state + ''' {} '''.format(rule_yaml['id'],x,status) elif "launchctl unload" in rule_yaml['fix']: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x,rule_yaml['id'],x+999) oval_test = oval_test + ''' '''.format(x,rule_yaml['id'],x) domain = str() if "launchctl" not in rule_yaml['check']: domain = rule_yaml['fix'].split()[4].split('/')[4].replace(".plist","") else: s = command[5].split()[2] domain = re.search('"(.*?)"', s).group(1) oval_object = oval_object + ''' '''.format(x, rule_yaml['id'],domain) else: oval_definition = oval_definition + ''' {} {} '''.format(x,rule_yaml['title'],rule_yaml['references']['cce'][0],rule_yaml['id'],rule_yaml['discussion'].rstrip(),rule_yaml['id'],x) oval_test = oval_test + ''' '''.format(x,rule_yaml['id'],x) domain = command[5].split()[2] domain = domain.replace('"','').replace("'",'') oval_object = oval_object + ''' '''.format(x,rule_yaml['id'],domain) x += 1 continue total_oval = ovalPrefix + "\n\n" + oval_definition + "\n\n\n" + oval_test + "\n\n\n" + oval_object + "\n\n" if oval_state != "": total_oval = total_oval + "\n" + oval_state + "\n\n" if oval_variable != "": total_oval = total_oval + "\n\n" + oval_variable + "\n\n" total_oval = total_oval + "\n" final_oval = re.sub('(?=\n\[NOTE\])(?s)(.*)\=\n$.*', '<', total_oval) oval_file = output with open(oval_file + "temp",'w') as rite: rite.write(final_oval) cmd = shutil.which('xmllint') rite.close() if cmd == None: try: os.rename(oval_file + "temp", oval_file) except: print("Error writing Oval file.") else: cmd = cmd + " " + oval_file + "temp --format --output " + oval_file os.popen(cmd).read() if os.path.exists(oval_file): os.remove(oval_file + "temp") if __name__ == "__main__": main()