#!/usr/bin/env python3
# filename: generate_scap.py
# description: Input a keyword for the baseline, output the scap/oval/xccdf
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
import argparse
from xml.sax.saxutils import escape
warnings.filterwarnings("ignore", category=DeprecationWarning)
def format_mobileconfig_fix(mobileconfig):
"""Takes a list of domains and setting from a mobileconfig, and reformats it for the output of the fix section of the guide.
"""
rulefix = ""
for domain, settings in mobileconfig.items():
if domain == "com.apple.ManagedClient.preferences":
rulefix = rulefix + \
(f"NOTE: The following settings are in the ({domain}) payload. This payload requires the additional settings to be sub-payloads within, containing their defined payload types.\n\n")
rulefix = rulefix + format_mobileconfig_fix(settings)
else:
rulefix = rulefix + (
f"Create a configuration profile containing the following keys in the ({domain}) payload type:\n\n")
rulefix = rulefix + "[source,xml]\n----\n"
for item in settings.items():
rulefix = rulefix + (f"{item[0]}\n")
if type(item[1]) == bool:
rulefix = rulefix + \
(f"<{str(item[1]).lower()}/>\n")
elif type(item[1]) == list:
rulefix = rulefix + "\n"
for setting in item[1]:
rulefix = rulefix + \
(f" {setting}\n")
rulefix = rulefix + "\n"
elif type(item[1]) == int:
rulefix = rulefix + \
(f"{item[1]}\n")
elif type(item[1]) == str:
rulefix = rulefix + \
(f"{item[1]}\n")
elif type(item[1]) == dict:
rulefix = rulefix + "\n"
for k,v in item[1].items():
if type(v) == dict:
rulefix = rulefix + \
(f" {k}\n")
rulefix = rulefix + \
(f" \n")
for x,y in v.items():
rulefix = rulefix + \
(f" {x}\n")
rulefix = rulefix + \
(f" {y}\n")
rulefix = rulefix + \
(f" \n")
break
if isinstance(v, list):
rulefix = rulefix + " \n"
for setting in v:
rulefix = rulefix + \
(f" {setting}\n")
rulefix = rulefix + " \n"
else:
rulefix = rulefix + \
(f" {k}\n")
rulefix = rulefix + \
(f" {v}\n")
rulefix = rulefix + "\n"
rulefix = rulefix + "----\n\n"
return rulefix
def replace_ocil(xccdf, x):
regex = r'''([\r\n].*?)(?:=?\r|\n)(.*?(?:def:{}\").*)'''.format(x)
substr = ''''''
result = re.sub(regex, substr, xccdf, 0, re.MULTILINE)
return result
def create_args():
parser = argparse.ArgumentParser(
description="Easily generate xccdf, oval, or scap datastream. If no option is defined, it will generate an scap datastream file.")
parser.add_argument("-x", "--xccdf", default=None,
help="Generate an xccdf file.", action="store_true")
parser.add_argument("-o", "--oval", default=None,
help="Generate an oval file of the checks.", action="store_true")
parser.add_argument("-l", "--list_tags", default=None,
help="List the available keyword tags to search for.", action="store_true")
parser.add_argument("-b", "--baseline", default="None",
help="Choose a baseline to generate an xml file for, if none is specified it will generate for every rule found.", action="store")
return parser.parse_args()
def generate_scap(all_rules, all_baselines, args):
export_as = ""
version_file = "../VERSION.yaml"
with open(version_file) as r:
version_yaml = yaml.load(r, Loader=yaml.SafeLoader)
if args.xccdf:
export_as = "xccdf"
if args.oval:
export_as = "oval"
if "ios" in version_yaml['cpe']:
print("OVAL generation is not available on iOS")
exit()
if args.oval == None and args.xccdf == None:
export_as = "scap"
if "ios" in version_yaml['cpe']:
print("iOS will only export as XCCDF")
export_as = "xccdf"
now = datetime.now()
date_time_string = now.strftime("%Y-%m-%dT%H:%M:%S")
filenameversion = version_yaml['version'].split(",")[1].replace(" ", "_")[1:]
output = "../build/macOS_{0}_Security_Compliance_Benchmark-{1}".format(version_yaml['os'],filenameversion)
if "ios" in version_yaml['cpe']:
output = "../build/iOS_{0}_Security_Compliance_Benchmark-{1}".format(version_yaml['os'],filenameversion)
if export_as == "xccdf":
output = output + "_xccdf.xml"
if export_as == "oval":
output = output + "_oval.xml"
if export_as == "scap":
output = output + ".xml"
oval_definition = str()
oval_test = str()
oval_object = str()
oval_state = str()
oval_variable = str()
xccdf_profiles = str()
total_scap = str()
scap_groups = str()
xccdf_rules = str()
x = 1
d = 1
ovalPrefix = '''
5.11.2
{0}
Copyright (c) 2020, NIST.
macOS Security Compliance Project
'''.format(date_time_string)
ostype = "macOS"
if "ios" in version_yaml['cpe']:
ostype = "iOS/iPadOS"
xccdfPrefix = '''
draft
{4} {1}: Security Configuration
{4} {1}: Security Configuration
Security Content Automation Protocol
National Institute of Standards and Technology
{2}
National Institute of Standards and Technology
National Institute of Standards and Technology
https://github.com/usnistgov/macos_security/releases/latest
Bob Gendler - National Institute of Standards and Technology
Dan Brodjieski - National Aeronautics and Space Administration
Allen Golbig - Jamf
'''.format(date_time_string, version_yaml['os'], version_yaml['version'],date_time_string.split("T")[0] + "Z", ostype)
scapPrefix = '''
draft
macOS {1}: Security Configuration
macOS {1}: Security Configuration
Security Content Automation Protocol
National Institute of Standards and Technology
platform-cpe-dictionary
platform-cpe-oval
{3}
National Institute of Standards and Technology
National Institute of Standards and Technology
https://github.com/usnistgov/macos_security/releases/latest
Bob Gendler - National Institute of Standards and Technology
Dan Brodjieski - National Aeronautics and Space Administration
Allen Golbig - Jamf
'''.format(date_time_string, version_yaml['os'], version_yaml['cpe'], version_yaml['version'],date_time_string.split("T")[0] + "Z")
generated_baselines = {}
for rule in all_rules:
if glob.glob('../custom/rules/**/{}.yaml'.format(rule),recursive=True):
rule_file = glob.glob('../custom/rules/**/{}.yaml'.format(rule),recursive=True)[0]
custom=True
elif glob.glob('../rules/*/{}.yaml'.format(rule)):
rule_file = glob.glob('../rules/*/{}.yaml'.format(rule))[0]
custom=False
odv_label = str()
og_rule_yaml = get_rule_yaml(rule_file, custom)
loop = 1
if "odv" in og_rule_yaml:
loop = len(og_rule_yaml['odv'])
if args.baseline != "None":
loop = 1
for a in range(0, loop):
rule_yaml = get_rule_yaml(rule_file, custom)
try:
# # odv_label = list(rule_yaml['odv'].keys())[a]
# # odv_label.remove('hint')
if args.baseline != "None":
odv_label = args.baseline
if args.baseline not in list(rule_yaml['odv'].keys())[a]:
odv_label = "recommended"
# if args.baseline not in list(rule_yaml['odv'].keys())[a]:
# odv_label = "recommended"
else:
odv_label = list(rule_yaml['odv'].keys())[a]
# if odv_label == "hint":
# continue
odv_value = str(rule_yaml['odv'][odv_label])
rule_yaml['title'] = rule_yaml['title'].replace("$ODV",str(odv_value))
rule_yaml['discussion'] = rule_yaml['discussion'].replace("$ODV",odv_value)
rule_yaml['check'] = rule_yaml['check'].replace("$ODV",odv_value)
rule_yaml['fix'] = rule_yaml['fix'].replace("$ODV",odv_value)
if "result" in rule_yaml:
for result_value in rule_yaml['result']:
if "$ODV" == rule_yaml['result'][result_value]:
rule_yaml['result'][result_value] = rule_yaml['result'][result_value].replace("$ODV",odv_value)
if rule_yaml['mobileconfig_info']:
for mobileconfig_type in rule_yaml['mobileconfig_info']:
if isinstance(rule_yaml['mobileconfig_info'][mobileconfig_type], dict):
for mobileconfig_value in rule_yaml['mobileconfig_info'][mobileconfig_type]:
if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]):
if type(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]) == dict:
for k,v in resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value].items():
if v == "$ODV":
resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value][k] = odv_value
else:
resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv_value
except:
odv_label = "recommended"
for baseline in all_baselines:
found_rules = []
for tag in rule_yaml['tags']:
if tag == baseline:
if odv_label != "recommended" and odv_label == tag or odv_label == "custom":
if baseline in generated_baselines:
generated_baselines[baseline].append(rule_yaml['id'] + "_" + odv_label)
else:
generated_baselines[baseline] = [rule_yaml['id'] + "_" + odv_label]
continue
elif odv_label == "recommended" or odv_label == "custom":
if "odv" in rule_yaml:
if baseline not in rule_yaml['odv']:
if baseline in generated_baselines:
generated_baselines[baseline].append(rule_yaml['id'] + "_" + odv_label)
else:
generated_baselines[baseline] = [rule_yaml['id'] + "_" + odv_label]
else:
if baseline in generated_baselines:
generated_baselines[baseline].append(rule_yaml['id'] + "_" + odv_label)
else:
generated_baselines[baseline] = [rule_yaml['id'] + "_" + odv_label]
if odv_label == "hint":
continue
result = str()
if "result" in rule_yaml:
result = "\nResult: {}".format(rule_yaml['result'])
else:
result = ""
severity = str()
if severity in rule_yaml:
if isinstance(rule_yaml["severity"], str):
severity = f'{rule_yaml["severity"]}'
if isinstance(rule_yaml["severity"], dict):
try:
severity = f'{rule_yaml["severity"][args.baseline]}'
except KeyError:
severity = "unknown"
else:
severity = "unknown"
check_rule = str()
if "inherent" in rule_yaml['tags'] or "n_a" in rule_yaml['tags'] or "permanent" in rule_yaml['tags']:
check_rule = '''
'''
else:
check_rule = '''
'''.format(x)
references = str()
if "800-53r5" in rule_yaml['references'] and rule_yaml['references']['800-53r5'][0] != "N/A":
references = references + "NIST SP 800-53r5: "
for nist80053 in rule_yaml['references']['800-53r5']:
references = references + nist80053 + ", "
references = references[:-2] + ""
if "800-53r4" in rule_yaml['references'] and rule_yaml['references']['800-53r4'][0] != "N/A":
references = references + "NIST SP 800-53r4: "
for nist80053 in rule_yaml['references']['800-53r4']:
references = references + nist80053 + ", "
references = references[:-2] + ""
if "800-171r3" in rule_yaml['references'] and rule_yaml['references']['800-171r3'][0] != "N/A":
references = references + "NIST SP 800-171r3: "
for nist800171 in rule_yaml['references']['800-171r3']:
references = references + nist800171 + ", "
references = references[:-2] + ""
if "disa_stig" in rule_yaml['references'] and rule_yaml['references']['disa_stig'][0] != "N/A":
references = references + "DISA STIG(s): "
for disa_stig in rule_yaml['references']['disa_stig']:
references = references + disa_stig + ", "
references = references[:-2] + ""
if "cis" in rule_yaml['references']:
if "benchmark" in rule_yaml['references']['cis'] and rule_yaml['references']['cis']['benchmark'][0] != "N/A":
references = references + "CIS Benchmark: "
for benchmark in rule_yaml['references']['cis']['benchmark']:
references = references + benchmark + ", "
references = references[:-2] + ""
if "controls v8" in rule_yaml['references']['cis'] and rule_yaml['references']['cis']['controls v8'][0] != "N/A":
references = references + "CIS Controls V8: "
for v8controls in rule_yaml['references']['cis']['controls v8']:
references = references + str(v8controls) + ", "
references = references[:-2] + ""
for k,v in rule_yaml['references'].items():
if k == "cci" or k == "srg":
continue
if k == "custom":
for i,u in rule_yaml['references']['custom'].items():
references = references + '{0}: '.format(i)
for refs in rule_yaml['references']['custom'][i]:
references = references + '{0}, '.format(str(refs))
references = references[:-2] + ""
cce = str()
if "cce" not in rule_yaml['references'] or rule_yaml['references']['cce'] == "N/A":
cce = "CCE-11111-1"
else:
cce = rule_yaml['references']['cce'][0]
if export_as == "scap":
mobileconfig_info = ""
if rule_yaml['mobileconfig']:
mobileconfig_info = escape(format_mobileconfig_fix(rule_yaml['mobileconfig_info']))
xccdf_rules = xccdf_rules + '''
{2}
{3}
{4}
{5}{9}
{6}
{7}
{8}
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&") + "\n" + mobileconfig_info, check_rule, references)
if export_as == "xccdf":
mobileconfig_info = ""
if rule_yaml['mobileconfig']:
mobileconfig_info = escape(format_mobileconfig_fix(rule_yaml['mobileconfig_info']))
xccdf_rules = xccdf_rules + '''
{2}
{3}
{4}
{5}{8}
{6}
{7}
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&") + "\n" + mobileconfig_info, references)
continue
if "inherent" in rule_yaml['tags'] or "n_a" in rule_yaml['tags'] or "permanent" in rule_yaml['tags']:
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "time_machine" in rule_yaml['id'] and "encrypted" in rule_yaml['id']:
print(rule_yaml['id'] + " - Manual Check Required")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "bluetooth" in rule_yaml['id'] and "unpaired" in rule_yaml['id']:
print(rule_yaml['id'] + " - Manual Check Required")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if rule_yaml['check'][0] != "/" and "[source,bash]" not in rule_yaml['fix']:
print(rule_yaml['id'] + " - Manual Check")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "hint" in rule_yaml['check'] and "dscl" in rule_yaml['check']:
print(rule_yaml['id'] + " - no relevant oval")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "manual" in rule_yaml['tags']:
print(rule_yaml['id'] + " - Manual Check")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "eficheck" in rule_yaml['check']:
print(rule_yaml['id'] + " - eficheck - no relevant oval")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
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")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "/usr/bin/pwpolicy getaccountpolicies" in rule_yaml['check']:
print(rule_yaml['id'] + " - pwpolicy getaccountpolicies - no relevant oval")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "find" in rule_yaml['check'].split(" ")[0] and rule_yaml['id'] != "os_home_folders_secure":
print(rule_yaml['id'] + " - no relevant oval")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "/usr/sbin/firmwarepasswd" in rule_yaml['check']:
print(rule_yaml['id'] + " - no relevant oval")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "os_home_folders_secure" in rule_yaml['id']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label, rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
.*
oval:mscp:ste:{}
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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']:
xccdf_rules = replace_ocil(xccdf_rules,x)
x = x + 1
continue
else:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
'''.format(x,rule_yaml['id'])
oval_state = oval_state + '''
true
'''.format(rule_yaml['id'] + "_" + odv_label,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():
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip())
if len(settings) > 1:
oval_definition = oval_definition + ''''''
else:
oval_definition = oval_definition + ''''''
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"
dz = d + 5000
oval_definition = oval_definition + ''''''.format(rule_yaml['id'] + '_' + odv_label + "_" + str(d), dz)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label + "_" + str(d),dz,dz,dz)
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,dz,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'] + "_" + odv_label,dz,payload_domain,key)
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,dz,state_kind,value)
d += 1
x += 1
oval_definition = oval_definition + ''' '''
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x,str(xpath_search).replace('[',"").replace(']',"").replace("'",""))
oval_state = oval_state + '''
true
'''.format(rule_yaml['id'] + "_" + odv_label,x)
x = x + 1
continue
else:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
/Library/Managed Preferences/{}.plist'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x,state_kind,value)
x = x + 1
continue
if payload_type == "com.apple.finder":
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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" or payload_type == "com.apple.systempreferences" and key == "DisabledSystemSettings":
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x,x,key,str(value).strip('[]').strip("'"))
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,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"
try:
int(value)
state_kind = "int"
except:
pass
elif type(value) == dict:
state_kind = "string"
else:
continue
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
/Library/Managed Preferences/{}.plist'''.format(rule_yaml['id'] + "_" + odv_label,x,payload_type)
if state_kind == "boolean":
oval_object = oval_object + '''
name(//*[contains(text(), "{}")]/following-sibling::*[1])
'''.format(key)
else:
if payload_type == "com.apple.mobiledevice.passwordpolicy" and "customRegex" in info:
oval_object = oval_object + '''
//*[contains(text(), "{}")]/following-sibling::*[1]/text()
'''.format("passwordContentRegex")
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,state_kind,value['passwordContentRegex'])
x += 1
continue
else:
oval_object = oval_object + '''
//*[contains(text(), "{}")]/following-sibling::*[1]/text()
'''.format(key)
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,state_kind,value)
x += 1
continue
else:
command = rule_yaml['check'].split("/")
if "sntp" in rule_yaml['check']:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "SPStorageDataType" in rule_yaml['check']:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
try:
if "fdesetup" in command[3]:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
except:
pass
try:
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'],cce,rule_yaml['id'] + "_" + odv_label,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
except:
pass
try:
if "csrutil" in command[3]:
if "authenticated-root" in command[3]:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
SPSoftwareDataType
//*[contains(text(), "system_integrity")]/following-sibling::string[position()=1]/text()
'''.format(rule_yaml['id'] + "_" + odv_label,x)
oval_state = oval_state + '''
SPSoftwareDataType
//*[contains(text(), "system_integrity")]/following-sibling::string[position()=1]/text()
integrity_enabled
'''.format(rule_yaml['id'] + "_" + odv_label,x)
x += 1
continue
except:
pass
if "pfctl" in rule_yaml['check']:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
if "dump-keychain" in rule_yaml['check']:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
try:
if "mdmclient" in command[3]:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
except:
pass
try:
if "nvram" in command[3]:
print(rule_yaml['id'] + " - No relevant oval test")
xccdf_rules = replace_ocil(xccdf_rules,x)
x += 1
continue
except:
pass
try:
if "pmset" in command[3] and "standby" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,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
except:
pass
if "sudo -V" in rule_yaml['check']:
if "grep" in rule_yaml['check'].split("|")[1]:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x, rule_yaml['id'] + "_" + odv_label,x+5051)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+5051, rule_yaml['id'] + "_" + odv_label, x+5051)
check_string = rule_yaml['fix'].split("echo")[1].split('"')[1]
oval_object = oval_object + '''
/etc/sudoers
{}
1
'''.format(x, rule_yaml['id'] + "_" + odv_label, check_string)
oval_object = oval_object + '''
/etc/sudoers.d/
*
{}
1
'''.format(x+5051, rule_yaml['id'] + "_" + odv_label, check_string)
x = x + 1
continue
if "awk" in rule_yaml['check'].split("|")[1]:
if "timestamp_type" in rule_yaml['fix'] and rule_yaml['result']['string'] == "tty":
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x+8000, rule_yaml['id'] + "_" + odv_label,x+8001, rule_yaml['id'] + "_" + odv_label,x+8002,rule_yaml['id'] + "_" + odv_label,x+8003)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+8000, rule_yaml['id'] + "_" + odv_label, x+8000)
oval_test = oval_test + '''
'''.format(x+8001, rule_yaml['id'] + "_" + odv_label, x+8001)
oval_test = oval_test + '''
'''.format(x+8002, rule_yaml['id'] + "_" + odv_label, x+8002)
oval_object = oval_object + '''
/etc/sudoers
timestamp_type
1
'''.format(x, rule_yaml['id'] + "_" + odv_label)
oval_object = oval_object + '''
/etc/sudoers.d/
*
timestamp_type
1
'''.format(x+8000, rule_yaml['id'] + "_" + odv_label)
oval_object = oval_object + '''
/etc/sudoers.d/
*
!tty_tickets
1
'''.format(x+8001, rule_yaml['id'] + "_" + odv_label)
oval_object = oval_object + '''
/etc/sudoers.d/
*
!tty_tickets
1
'''.format(x+8002, rule_yaml['id'] + "_" + odv_label)
x = x + 1
continue
else:
check_string = "Defaults.*.timestamp_type={}".format(rule_yaml['result']['string'])
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x+8000, rule_yaml['id'] + "_" + odv_label,x+8001, rule_yaml['id'] + "_" + odv_label,x+8002,rule_yaml['id'] + "_" + odv_label,x+8003)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+5000, rule_yaml['id'] + "_" + odv_label, x+7000)
oval_object = oval_object + '''
/etc/sudoers
{}
1
'''.format(x, rule_yaml['id'] + "_" + odv_label, check_string)
oval_object = oval_object + '''
/etc/sudoers.d/
*
{}
1
'''.format(x+7000, rule_yaml['id'] + "_" + odv_label, check_string)
x = x + 1
continue
if "ssh_config" in rule_yaml['discussion'] and "dscl" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x+5010, rule_yaml['id'] + "_" + odv_label,x+5025)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+5010, rule_yaml['id'] + "_" + odv_label, x+5010)
oval_test = oval_test + '''
'''.format(x+5025, rule_yaml['id'] + "_" + odv_label, x+5025)
regex = r"(?<=grep).*$"
matches = re.finditer(regex, rule_yaml['check'], re.MULTILINE)
matchy_match = ""
for matchNum, match in enumerate(matches, start=1):
matchy_match = match.group()
ssh_config_pattern = matchy_match.split('"')[1]
oval_object = oval_object + '''
/etc/ssh/ssh_config
{}
1
'''.format(x, rule_yaml['id'] + "_" + odv_label, ssh_config_pattern)
oval_object = oval_object + '''
/etc/ssh/ssh_config.d/
*
{}
1
'''.format(x+5010, rule_yaml['id'] + "_" + odv_label, ssh_config_pattern)
oval_object = oval_object + '''
{}
1
.*
oval:mscp:ste:{}
'''.format(x+5025,rule_yaml['id'] + "_" + odv_label,x,ssh_config_pattern,x+999,x+999)
oval_state = oval_state + '''
^[^_\s].*
0
0
/usr/bin/false
'''.format(x+999)
oval_variable = oval_variable + '''
/.ssh/config
'''.format(x,x+999)
x = x + 1
continue
if "sshd -T" in rule_yaml['check'] and "fips" in rule_yaml['check'] or "sshd -G" in rule_yaml['check'] and "fips" in rule_yaml['check']:
fipslist = rule_yaml['check'].split("\n")[0].split("(")[1].replace(")","").replace('" "',"\n").replace('"',"")
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x+6000, rule_yaml['id'] + "_" + odv_label,x+6001)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+6000, rule_yaml['id'] + "_" + odv_label, x+6000)
oval_object = oval_object + '''
/etc/ssh/sshd_config
{}
1
'''.format(x, rule_yaml['id'] + "_" + odv_label, fipslist)
oval_object = oval_object + '''
/etc/ssh/sshd_config.d/
*
{}
1
'''.format(x+6000, rule_yaml['id'] + "_" + odv_label, fipslist)
x = x + 1
continue
if "sshd -T" in rule_yaml['check'] or "sshd -G" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label, x+6000, rule_yaml['id'] + "_" + odv_label,x+6001)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_test = oval_test + '''
'''.format(x+6000, rule_yaml['id'] + "_" + odv_label, x+6000)
sshd_config_pattern = ""
if "grep" in rule_yaml['check']:
regex = r"(?<=grep).*$"
matches = re.finditer(regex, rule_yaml['check'], re.MULTILINE)
matchy_match = ""
for matchNum, match in enumerate(matches, start=1):
matchy_match = match.group()
sshd_config_pattern = ""
if '"' in matchy_match:
sshd_config_pattern = matchy_match.split('"')[1]
elif "'" in matchy_match:
sshd_config_pattern = matchy_match.split("'")[1]
if "awk" in rule_yaml['check']:
matchy_match = rule_yaml['check'].split("'")[1].split("/")[1]
for item in rule_yaml['result']:
sshd_config_pattern = matchy_match + " " + str(rule_yaml['result'][item])
oval_object = oval_object + '''
/etc/ssh/sshd_config
{}
1
'''.format(x, rule_yaml['id'] + "_" + odv_label, sshd_config_pattern)
oval_object = oval_object + '''
/etc/ssh/sshd_config.d/
*
{}
1
'''.format(x+6000, rule_yaml['id'] + "_" + odv_label, sshd_config_pattern)
x = x + 1
continue
try:
if "pmset" in command[3]:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
/Library/Preferences/com.apple.PowerManagement.plist'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x)
x += 1
continue
except:
pass
if "socketfilterfw" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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()
'''.format(rule_yaml['id'] + "_" + odv_label,x,firewall_variable)
oval_state = oval_state + '''
1
'''.format(rule_yaml['id'] + "_" + odv_label,x)
x += 1
continue
try:
if "systemsetup" in command[3]:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x,state_test)
except:
pass
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,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'] == "system_settings_hot_corners_secure" or rule_yaml['id'] == "sysprefs_hot_corners_secure":
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label,x+5000,rule_yaml['id'] + "_" + odv_label,x+5001,rule_yaml['id'] + "_" + odv_label,x+5002)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x+5000,x+5000,x+5000)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x+5001,x+5001,x+5001)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,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'] + "_" + odv_label,x,oval_datatype,value)
oval_definition = re.sub('(?=\n\[NOTE\])(?s)(.*)\=\n<', '<', oval_definition)
x = x+1
continue
try:
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
{}
boolean(//key[text()="{}"]/following-sibling::{})
'''.format(rule_yaml['id'] + "_" + odv_label,x,authdb,key,last_string)
oval_state = oval_state + '''
true
'''.format(rule_yaml['id'] + "_" + odv_label,x)
else:
key = (check[1].split()[2].replace("'",""))
key = key.split('>')[1].split('<')[0]
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
{}
//*[contains(text(), "{}")]/text()
'''.format(rule_yaml['id'] + "_" + odv_label,x,authdb,key)
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,key)
else:
if "authorizationdb" in rule_yaml['check']:
regex = r"=\(.*.\)"
matchy_match = []
matches = re.finditer(regex, rule_yaml['check'], re.MULTILINE)
for matchNum, match in enumerate(matches, start=1):
matchy_match = match.group().replace('=(',"").replace(")","").replace('"','').split()
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'])
for match in matchy_match:
oval_definition = oval_definition + '''
'''.format(rule_yaml['id'] + "+" + match, x)
oval_test = oval_test + '''
'''.format(match,x,x,x)
key="shared"
value=""
if "false" in rule_yaml["check"]:
value="false"
else:
value="true"
oval_object = oval_object + '''
{}
boolean(//key[text()="{}"]/following-sibling::{})
'''.format(match,x,match,key,value)
oval_state = oval_state + '''
true
'''.format(match,x)
x += 1
oval_definition = oval_definition + ""
x += 1
continue
except:
pass
if "/bin/rm" in rule_yaml['fix'] and "/bin/ls" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,x)
path = rule_yaml['fix'].split("----")[1].split(" ")[-1]
oval_object = oval_object + '''
{}
'''.format(x,rule_yaml['id'] + "_" + odv_label,path.rstrip())
x += 1
continue
try:
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label,x+2999)
oval_test = oval_test + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,x,x+2999,rule_yaml['id'] + "_" + odv_label,x+2999)
oval_object = oval_object + '''
/Library/Security/PolicyBanner.rtf
/Library/Security/PolicyBanner.rtfd
'''.format(x,rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label, x+999, config_file, regex)
oval_variable = oval_variable + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,x,x)
if "-" in fix_command and "R" in fix_command or rule_yaml['fix'].split("\n")[2][-1] == "*":
behavior = ''
if "audit" in rule_yaml['id']:
filename = 'current'
else:
behavior = ""
filename = ''
if oval_variable_need == True:
oval_object = oval_object + '''
{}
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,behavior,x,filename)
else:
oval_object = oval_object + '''
{}
{}
'''.format(rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x) + state_test + '''
'''
x += 1
continue
except:
pass
try:
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
oval_object = oval_object + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,command[5].split()[0])
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,shell)
x += 1
continue
except:
pass
try:
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]
try:
field_sep = rule_yaml['check'].split("-F")[1].split(" ")[0].replace('\"',"")
except:
field_sep = " "
try:
awk_result = rule_yaml['result']['string']
except:
awk_result = str(rule_yaml['result']['integer'])
if awk_search[0] != "^":
awk_search = "^" + awk_search + field_sep + awk_result
else:
awk_search = awk_search + field_sep + awk_result
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_object = oval_object + '''
{}
{}
1
'''.format(x,rule_yaml['id'] + "_" + odv_label,awk_file.rstrip(), awk_search)
x += 1
continue
except:
pass
try:
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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
file_path = rule_yaml["check"].split(" ")[-1].rstrip()
oval_object = oval_object + '''
{}
{}
1
'''.format(x,rule_yaml['id'] + "_" + odv_label,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'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x, rule_yaml['id'] + "_" + odv_label, x)
oval_object = oval_object + '''
{}
{}
1
'''.format(x,rule_yaml['id'] + "_" + odv_label,grep_file.rstrip(),grep_search)
x += 1
continue
except:
pass
try:
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'] or "disable" in command[2] and "=> disabled" in rule_yaml['check']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label,x+999)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x,x+999,rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,x,domain,x+999,rule_yaml['id'] + "_" + odv_label,domain.replace('(','').replace(')',''))
status = ""
if "enable" in rule_yaml["fix"]:
status = "false"
else:
status = "true"
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,status)
elif "launchctl unload" in rule_yaml['fix']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x,rule_yaml['id'] + "_" + odv_label,x+999)
oval_test = oval_test + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,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'] + "_" + odv_label,domain.replace('(','').replace(')',''))
elif "defaults write" in rule_yaml['fix']:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'],rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x,x)
plist = rule_yaml['fix'].split(" ")[2].replace(".plist","")
# 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']:
key = rule_yaml['fix'].split("defaults")[1].split(" ")[3]
oval_object = oval_object + '''
.*
oval:mscp:ste:{}
'''.format(x+1999,x+1999,rule_yaml['id'] + "_" + odv_label,x,x)
if rule_yaml['fix'].split("defaults")[1].split(" ")[4] == "-bool":
rule_yaml['result']['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 + '''
^[^_\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:
key = rule_yaml['fix'].split("defaults")[1].split(" ")[3]
oval_object = oval_object + '''
'''.format(rule_yaml['id'] + "_" + odv_label,x,x)
if rule_yaml['fix'].split("defaults")[1].split(" ")[4] == "-bool":
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_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['fix'].split("defaults")[1].split(" ")[3]
oval_object = oval_object + '''
.*
oval:mscp:ste:{}
'''.format(x+1999,x+1999,rule_yaml['id'] + "_" + odv_label,x,x)
if rule_yaml['fix'].split("defaults")[1].split(" ")[4] == "-bool":
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 + '''
^[^_\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['fix'].split("defaults")[1].split(" ")[3]
oval_object = oval_object + '''
{}'''.format(rule_yaml['id'] + "_" + odv_label,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['fix'].split("defaults")[1].split(" ")[3]
oval_datatype = rule_yaml['fix'].split("defaults")[1].split(" ")[4].replace("-","")
if oval_datatype == "integer":
oval_datatype = "int"
if oval_datatype == "bool":
oval_datatype = "boolean"
value = rule_yaml['fix'].split("defaults")[1].split(" ")[5].replace(";","")
oval_state = oval_state + '''
{}
'''.format(rule_yaml['id'] + "_" + odv_label,x,oval_datatype,value)
oval_definition = re.sub('(?=\n\[NOTE\])(?s)(.*)\=\n<', '<', oval_definition)
x = x+1
continue
else:
oval_definition = oval_definition + '''
{}
{}
'''.format(x,rule_yaml['title'],cce,rule_yaml['id'] + "_" + odv_label,rule_yaml['discussion'].rstrip(),rule_yaml['id'] + "_" + odv_label,x)
oval_test = oval_test + '''
'''.format(x,rule_yaml['id'] + "_" + odv_label,x)
domain = command[5].split()[2]
domain = domain.replace('"','').replace("'",'')
###########
label_obj = ''''
total_scap = scapPrefix + xccdf_profiles + '''
All rules
All the rules
The check/fix commands outlined in this section must be run with elevated privileges.
''' + xccdf_rules + '''
5.11.2
{0}
Copyright (c) 2020, NIST.
macOS Security Compliance Project
'''.format(date_time_string,version_yaml['os'])
total_oval = "\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)
total_scap = total_scap + final_oval + '''
Manual Labor
1
2.0
{0}
Obtain a pass or a fail
ocil:gov.nist.mscp.content:testaction:1
PASS
FAIL
Do you wish this checklist item to be considered to have passed?
macOS Security Compliance Project
2.3
{0}
Apple macOS {2}
This CPE Name represents macOS {2}
oval:gov.nist.mscp.content.cpe.oval:def:1
macOS Security Compliance Project
5.11.2
{0}
Apple macOS {2} is installed
macOS
The operating system installed on the system is Apple macOS ({2}).
/System/Library/CoreServices/SystemVersion.plist
//*[contains(text(), "ProductVersion")]/following-sibling::*[1]/text()
macos
{2}
'''.format(date_time_string,version_yaml['cpe'],version_yaml['os'])
scap_file = output
with open(scap_file + "temp",'w') as rite:
if export_as == "scap":
rite.write(total_scap)
elif export_as == "xccdf":
rite.write(total_xccdf)
elif export_as == "oval":
total_oval = ovalPrefix + total_oval
rite.write(total_oval)
cmd = shutil.which('xmllint')
rite.close()
if cmd == None:
try:
os.rename(scap_file + "temp", scap_file)
except:
print("Error writing Oval file.")
else:
cmd = cmd + " " + scap_file + "temp --format --output " + scap_file
os.popen(cmd).read()
if os.path.exists(scap_file):
os.remove(scap_file + "temp")
def get_rule_yaml(rule_file, custom=False, baseline_name=""):
""" Takes a rule file, checks for a custom version, and returns the yaml for the rule
"""
global resulting_yaml
resulting_yaml = {}
names = [os.path.basename(x) for x in glob.glob('../custom/rules/**/*.yaml', recursive=True)]
file_name = os.path.basename(rule_file)
if custom:
print(f"Custom settings found for rule: {rule_file}")
try:
override_path = glob.glob('../custom/rules/**/{}'.format(file_name), recursive=True)[0]
except IndexError:
override_path = glob.glob('../custom/rules/{}'.format(file_name), recursive=True)[0]
with open(override_path) as r:
rule_yaml = yaml.load(r, Loader=yaml.SafeLoader)
else:
with open(rule_file) as r:
rule_yaml = yaml.load(r, Loader=yaml.SafeLoader)
try:
og_rule_path = glob.glob('../rules/**/{}'.format(file_name), recursive=True)[0]
except IndexError:
og_rule_path = glob.glob('../custom/rules/**/{}'.format(file_name), recursive=True)[0]
resulting_yaml['customized'] = ["customized rule"]
with open(og_rule_path) as og:
og_rule_yaml = yaml.load(og, Loader=yaml.SafeLoader)
for yaml_field in og_rule_yaml:
if yaml_field == "references":
if not 'references' in resulting_yaml:
resulting_yaml['references'] = {}
for ref in og_rule_yaml['references']:
try:
if og_rule_yaml['references'][ref] == rule_yaml['references'][ref]:
resulting_yaml['references'][ref] = og_rule_yaml['references'][ref]
else:
resulting_yaml['references'][ref] = rule_yaml['references'][ref]
except KeyError:
try:
resulting_yaml['references'][ref] = rule_yaml['references'][ref]
except KeyError:
resulting_yaml['references'][ref] = og_rule_yaml['references'][ref]
try:
if "custom" in rule_yaml['references']:
resulting_yaml['references']['custom'] = rule_yaml['references']['custom']
if 'customized' in resulting_yaml:
if 'customized references' not in resulting_yaml['customized']:
resulting_yaml['customized'].append("customized references")
else:
resulting_yaml['customized'] = ["customized references"]
except:
pass
elif yaml_field == "tags":
# try to concatenate tags from both original yaml and custom yaml
try:
if og_rule_yaml["tags"] == rule_yaml["tags"]:
#print("using default data in yaml field {}".format("tags"))
resulting_yaml['tags'] = og_rule_yaml['tags']
else:
#print("Found custom tags... concatenating them")
resulting_yaml['tags'] = og_rule_yaml['tags'] + rule_yaml['tags']
except KeyError:
resulting_yaml['tags'] = og_rule_yaml['tags']
else:
try:
if og_rule_yaml[yaml_field] == rule_yaml[yaml_field]:
resulting_yaml[yaml_field] = og_rule_yaml[yaml_field]
else:
resulting_yaml[yaml_field] = rule_yaml[yaml_field]
if 'customized' in resulting_yaml:
resulting_yaml['customized'].append("customized {}".format(yaml_field))
else:
resulting_yaml['customized'] = ["customized {}".format(yaml_field)]
except KeyError:
resulting_yaml[yaml_field] = og_rule_yaml[yaml_field]
return resulting_yaml
class MacSecurityRule():
def __init__(self, title, rule_id, severity, discussion, check, fix, cci, cce, nist_controls, disa_stig, srg, 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_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 collect_rules():
"""Takes a baseline yaml file and parses the rules, returns a list of containing rules
"""
all_rules = []
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) + glob.glob('../custom/rules/**/*.yaml',recursive=True):
if "supplemental" in rule:
continue
rule_yaml = get_rule_yaml(rule, custom=False)
for key in keys:
try:
rule_yaml[key]
except:
rule_yaml.update({key: "missing"})
if key == "references":
for reference in references:
try:
rule_yaml[key][reference]
except:
rule_yaml[key].update({reference: ["None"]})
if "n_a" in rule_yaml['tags']:
rule_yaml['tags'].remove("n_a")
if "inherent" in rule_yaml['tags']:
rule_yaml['tags'].remove("inherent")
if "manual" in rule_yaml['tags']:
rule_yaml['tags'].remove("manual")
if "none" in rule_yaml['tags']:
rule_yaml['tags'].remove("none")
if "permanent" in rule_yaml['tags']:
rule_yaml['tags'].remove("permanent")
if "supplemental" in rule_yaml['tags']:
rule_yaml['tags'].remove("supplemental")
if "i386" in rule_yaml['tags']:
rule_yaml['tags'].remove("i386")
if "arm64" in rule_yaml['tags']:
rule_yaml['tags'].remove("arm64")
all_rules.append(MacSecurityRule(rule_yaml['title'].replace('|', '\|'),
rule_yaml['id'].replace('|', '\|'),
rule_yaml['severity'],
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['odv'],
rule_yaml['tags'],
rule_yaml['result'],
rule_yaml['mobileconfig'],
rule_yaml['mobileconfig_info']
))
return all_rules
def available_tags(all_rules):
all_tags = []
for rule in all_rules:
for tag in rule.rule_tags:
all_tags.append(tag)
available_tags = []
for tag in all_tags:
if tag not in available_tags:
available_tags.append(tag)
available_tags.sort()
return available_tags
def get_controls(all_rules):
all_controls = []
for rule in all_rules:
for control in rule.rule_80053r4:
if control not in all_controls:
all_controls.append(control)
all_controls.sort()
return all_controls
def main():
args = create_args()
file_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(file_dir)
original_working_directory = os.getcwd()
os.chdir(file_dir)
all_rules = collect_rules()
all_rules_pruned = []
# for rule in all_rules:
# if rule.rule_id not in all_rules_pruned:
# all_rules_pruned.append(rule.rule_id)
if args.list_tags:
for tag in available_tags(all_rules):
print(tag)
exit(0)
all_baselines = []
if args.baseline:
all_baselines = [args.baseline]
for rule in all_rules:
if rule.rule_id not in all_rules_pruned and args.baseline in rule.rule_tags:
# if args.baseline in rule.rule_tags:
all_rules_pruned.append(rule.rule_id)
if all_baselines == ['None']:
all_baselines = available_tags(all_rules)
for rule in all_rules:
if rule.rule_id not in all_rules_pruned:
all_rules_pruned.append(rule.rule_id)
generate_scap(all_rules_pruned, all_baselines, args)
os.chdir(original_working_directory)
if __name__ == "__main__":
main()