From 32d6f3b340dca3e739167f052fbe010008139112 Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Tue, 24 May 2022 21:01:24 -0400 Subject: [PATCH 01/14] tag fixes --- scripts/generate_guidance.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 18dd675f..4b73dab9 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -779,15 +779,15 @@ fi continue arch="" - if "tags" in rule_yaml: + try: if "manual" in rule_yaml['tags']: continue if "arm64" in rule_yaml['tags']: arch="arm64" elif "i386" in rule_yaml['tags']: arch="i386" - else: - arch="" + except: + pass # grab the 800-53 controls try: @@ -1062,7 +1062,8 @@ def fill_in_odv(resulting_yaml, baseline_name): for mobileconfig_type in resulting_yaml['mobileconfig_info']: if isinstance(resulting_yaml['mobileconfig_info'][mobileconfig_type], dict): for mobileconfig_value in resulting_yaml['mobileconfig_info'][mobileconfig_type]: - resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv + if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]): + resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv @@ -1127,13 +1128,15 @@ def get_rule_yaml(rule_file, custom=False, baseline_name=""): pass elif yaml_field == "tags": # try to concatenate tags from both original yaml and custom yaml - if "tags" in rule_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'] + #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: + pass else: try: if og_rule_yaml[yaml_field] == rule_yaml[yaml_field]: From ddd9a6d966f479bd7f4a8093610dc45b72851cd8 Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Tue, 24 May 2022 21:18:59 -0400 Subject: [PATCH 02/14] odv fixes --- scripts/generate_guidance.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 4b73dab9..9f1bba1a 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -777,7 +777,7 @@ fi if rule_yaml['id'].startswith("supplemental"): continue - + arch="" try: if "manual" in rule_yaml['tags']: @@ -841,6 +841,7 @@ fi except KeyError: continue + print(rule_yaml) if "integer" in result: result_value = result['integer'] elif "boolean" in result: @@ -1056,13 +1057,14 @@ def fill_in_odv(resulting_yaml, baseline_name): resulting_yaml[field]=resulting_yaml[field].replace("$ODV", odv) for result_value in resulting_yaml['result']: - resulting_yaml['result'][result_value] = odv + if "$ODV" in result_value: + resulting_yaml['result'][result_value] = odv if resulting_yaml['mobileconfig_info']: for mobileconfig_type in resulting_yaml['mobileconfig_info']: if isinstance(resulting_yaml['mobileconfig_info'][mobileconfig_type], dict): for mobileconfig_value in resulting_yaml['mobileconfig_info'][mobileconfig_type]: - if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]): + if "$ODV" in mobileconfig_value: resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv @@ -1136,7 +1138,7 @@ def get_rule_yaml(rule_file, custom=False, baseline_name=""): #print("Found custom tags... concatenating them") resulting_yaml['tags'] = og_rule_yaml['tags'] + rule_yaml['tags'] except KeyError: - pass + resulting_yaml['tags'] = og_rule_yaml['tags'] else: try: if og_rule_yaml[yaml_field] == rule_yaml[yaml_field]: From 14685b42f0a2c6a556768ed6080e4ba48161dd1c Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Tue, 24 May 2022 21:28:08 -0400 Subject: [PATCH 03/14] more ODV fixes --- scripts/generate_guidance.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 9f1bba1a..6187516b 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -841,7 +841,6 @@ fi except KeyError: continue - print(rule_yaml) if "integer" in result: result_value = result['integer'] elif "boolean" in result: @@ -1064,7 +1063,7 @@ def fill_in_odv(resulting_yaml, baseline_name): for mobileconfig_type in resulting_yaml['mobileconfig_info']: if isinstance(resulting_yaml['mobileconfig_info'][mobileconfig_type], dict): for mobileconfig_value in resulting_yaml['mobileconfig_info'][mobileconfig_type]: - if "$ODV" in mobileconfig_value: + if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]): resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv From debb4b28aff70c996eae81211bd8fc9c1ef4192a Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Tue, 24 May 2022 22:50:14 -0400 Subject: [PATCH 04/14] adjusted ODV replacement for result_value --- scripts/generate_guidance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 6187516b..eb4ebdd7 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -1056,7 +1056,7 @@ def fill_in_odv(resulting_yaml, baseline_name): resulting_yaml[field]=resulting_yaml[field].replace("$ODV", odv) for result_value in resulting_yaml['result']: - if "$ODV" in result_value: + if "$ODV" in str(resulting_yaml['result'][result_value]): resulting_yaml['result'][result_value] = odv if resulting_yaml['mobileconfig_info']: From 65a2cff5ac004cd1deec074808c6b7c7299fd6b1 Mon Sep 17 00:00:00 2001 From: Bob Gendler Date: Wed, 25 May 2022 10:40:02 -0400 Subject: [PATCH 05/14] better handling odv --- scripts/generate_scap.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/scripts/generate_scap.py b/scripts/generate_scap.py index 0fd79df2..22a799ac 100755 --- a/scripts/generate_scap.py +++ b/scripts/generate_scap.py @@ -184,9 +184,9 @@ def generate_scap(all_rules, all_baselines, args): loop = 1 if "odv" in og_rule_yaml: loop = len(og_rule_yaml['odv']) - if args.baseline: - loop = 1 + if args.baseline != "None": + loop = 1 for a in range(0, loop): @@ -196,7 +196,7 @@ def generate_scap(all_rules, all_baselines, args): odv_label = list(rule_yaml['odv'].keys())[a] - if args.baseline: + if args.baseline != "None": odv_label = args.baseline if odv_label == "hint": @@ -219,8 +219,8 @@ def generate_scap(all_rules, all_baselines, args): 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" == rule_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]: - rule_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = rule_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value].replace("$ODV",odv_value) + if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]): + resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = rule_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value].replace("$ODV",odv_value) except: odv_label = "default" @@ -229,14 +229,14 @@ def generate_scap(all_rules, all_baselines, args): found_rules = [] for tag in rule_yaml['tags']: if tag == baseline: - if odv_label != "default" and odv_label == tag: + if odv_label != "default" 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 == "default": + elif odv_label == "default" or odv_label == "custom": if "odv" in rule_yaml: if baseline not in rule_yaml['odv']: @@ -2707,10 +2707,16 @@ def get_rule_yaml(rule_file, custom=False, baseline_name=""): except: pass elif yaml_field == "tags": - if og_rule_yaml["tags"] == rule_yaml["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: - resulting_yaml['tags'] = og_rule_yaml['tags'] + rule_yaml['tags'] else: try: if og_rule_yaml[yaml_field] == rule_yaml[yaml_field]: From f5bfcdb2c1543619221dc59de01ee704ddc39eab Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Thu, 26 May 2022 13:14:51 -0400 Subject: [PATCH 06/14] default ODVs work better --- baselines/800-171.yaml | 1 + baselines/800-53r5_high.yaml | 1 + baselines/800-53r5_low.yaml | 1 + baselines/800-53r5_moderate.yaml | 1 + baselines/DISA-STIG.yaml | 1 + baselines/all_rules.yaml | 1 + baselines/cis_lvl1.yaml | 1 + baselines/cis_lvl2.yaml | 1 + baselines/cisv8.yaml | 1 + baselines/cnssi-1253.yaml | 1 + .../audit_configure_capacity_notify.yaml | 2 +- rules/audit/audit_retention_configure.yaml | 2 +- .../os_install_log_retention_configure.yaml | 2 +- .../os_policy_banner_loginwindow_enforce.yaml | 2 +- rules/os/os_policy_banner_ssh_configure.yaml | 2 +- ..._ssh_server_alive_count_max_configure.yaml | 2 +- ...s_ssh_server_alive_interval_configure.yaml | 2 +- ...sshd_client_alive_count_max_configure.yaml | 2 +- ..._sshd_client_alive_interval_configure.yaml | 2 +- .../os_sshd_login_grace_time_configure.yaml | 2 +- rules/os/os_sudo_timeout_configure.yaml | 2 +- .../pwpolicy_account_inactivity_enforce.yaml | 2 +- .../pwpolicy_account_lockout_enforce.yaml | 2 +- ...olicy_account_lockout_timeout_enforce.yaml | 2 +- rules/pwpolicy/pwpolicy_history_enforce.yaml | 2 +- .../pwpolicy_max_lifetime_enforce.yaml | 2 +- .../pwpolicy_minimum_length_enforce.yaml | 2 +- .../pwpolicy_minimum_lifetime_enforce.yaml | 2 +- ...fs_loginwindow_loginwindowtext_enable.yaml | 2 +- ...nsaver_ask_for_password_delay_enforce.yaml | 2 +- .../sysprefs_screensaver_timeout_enforce.yaml | 3 +- .../sysprefs_time_server_configure.yaml | 2 +- scripts/generate_baseline.py | 40 ++++++++++--------- scripts/generate_guidance.py | 15 +++---- 34 files changed, 61 insertions(+), 49 deletions(-) diff --git a/baselines/800-171.yaml b/baselines/800-171.yaml index c5366185..56912b8a 100644 --- a/baselines/800-171.yaml +++ b/baselines/800-171.yaml @@ -7,6 +7,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/800-53r5_high.yaml b/baselines/800-53r5_high.yaml index c6316eae..9872d422 100644 --- a/baselines/800-53r5_high.yaml +++ b/baselines/800-53r5_high.yaml @@ -7,6 +7,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/800-53r5_low.yaml b/baselines/800-53r5_low.yaml index 7b640722..428fe889 100644 --- a/baselines/800-53r5_low.yaml +++ b/baselines/800-53r5_low.yaml @@ -7,6 +7,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/800-53r5_moderate.yaml b/baselines/800-53r5_moderate.yaml index 2c71d231..8de1ccd0 100644 --- a/baselines/800-53r5_moderate.yaml +++ b/baselines/800-53r5_moderate.yaml @@ -7,6 +7,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/DISA-STIG.yaml b/baselines/DISA-STIG.yaml index 2462c5d7..d3fc9b9c 100644 --- a/baselines/DISA-STIG.yaml +++ b/baselines/DISA-STIG.yaml @@ -7,6 +7,7 @@ authors: | |Allen Golbig|Jamf |Bob Gendler|National Institute of Standards and Technology |=== +parent_values: stig profile: - section: "authentication" rules: diff --git a/baselines/all_rules.yaml b/baselines/all_rules.yaml index 03109b65..860cf282 100644 --- a/baselines/all_rules.yaml +++ b/baselines/all_rules.yaml @@ -7,6 +7,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/cis_lvl1.yaml b/baselines/cis_lvl1.yaml index eeba04af..4652b000 100644 --- a/baselines/cis_lvl1.yaml +++ b/baselines/cis_lvl1.yaml @@ -8,6 +8,7 @@ authors: | |Ron Colvin|Center for Internet Security |Allen Golbig|Jamf |=== +parent_values: cis_lvl1 profile: - section: "auditing" rules: diff --git a/baselines/cis_lvl2.yaml b/baselines/cis_lvl2.yaml index 0c64fb75..c4581013 100644 --- a/baselines/cis_lvl2.yaml +++ b/baselines/cis_lvl2.yaml @@ -8,6 +8,7 @@ authors: | |Ron Colvin|Center for Internet Security |Allen Golbig|Jamf |=== +parent_values: cis_lvl2 profile: - section: "auditing" rules: diff --git a/baselines/cisv8.yaml b/baselines/cisv8.yaml index 329ed453..0bbe1916 100644 --- a/baselines/cisv8.yaml +++ b/baselines/cisv8.yaml @@ -9,6 +9,7 @@ authors: | |Dan Brodjieski|National Aeronautics and Space Administration |Allen Golbig|Jamf |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/baselines/cnssi-1253.yaml b/baselines/cnssi-1253.yaml index 99e1d79e..61233cad 100644 --- a/baselines/cnssi-1253.yaml +++ b/baselines/cnssi-1253.yaml @@ -7,6 +7,7 @@ authors: | |Ekkehard Koch| |Bob Gendler|National Institute of Standards and Technology |=== +parent_values: recommended profile: - section: "authentication" rules: diff --git a/rules/audit/audit_configure_capacity_notify.yaml b/rules/audit/audit_configure_capacity_notify.yaml index 43c8d502..5324acc5 100644 --- a/rules/audit/audit_configure_capacity_notify.yaml +++ b/rules/audit/audit_configure_capacity_notify.yaml @@ -30,7 +30,7 @@ macOS: - "12.0" odv: hint: "Percentage of free space." - default: 25 + recommended: 25 stig: 25 tags: - 800-53r5_high diff --git a/rules/audit/audit_retention_configure.yaml b/rules/audit/audit_retention_configure.yaml index f2579732..867e0604 100644 --- a/rules/audit/audit_retention_configure.yaml +++ b/rules/audit/audit_retention_configure.yaml @@ -38,7 +38,7 @@ macOS: - "12.0" odv: hint: "See man audit_control for possible values." - default: 7d + recommended: 7d stig: 7d cis_lvl1: 60d or 1G cis_lvl2: 60d or 1G diff --git a/rules/os/os_install_log_retention_configure.yaml b/rules/os/os_install_log_retention_configure.yaml index 805f7065..50de5e2a 100644 --- a/rules/os/os_install_log_retention_configure.yaml +++ b/rules/os/os_install_log_retention_configure.yaml @@ -40,7 +40,7 @@ macOS: - "12.0" odv: hint: "Number of days." - default: 365 + recommended: 365 cis_lvl1: 365 cis_lvl2: 365 tags: diff --git a/rules/os/os_policy_banner_loginwindow_enforce.yaml b/rules/os/os_policy_banner_loginwindow_enforce.yaml index 9d540047..593bbf42 100644 --- a/rules/os/os_policy_banner_loginwindow_enforce.yaml +++ b/rules/os/os_policy_banner_loginwindow_enforce.yaml @@ -56,7 +56,7 @@ macOS: - "12.0" odv: hint: "Organization's Policy Text" - default: |- + recommended: |- You are accessing a U.S. Government information system, which includes: 1) this computer, 2) this computer network, 3) all Government-furnished computers connected to this network, and 4) all Government-furnished devices and storage media attached to this network or to a computer on this network. You understand and consent to the following: you may access this information system for authorized use only; unauthorized use of the system is prohibited and subject to criminal and civil penalties; you have no reasonable expectation of privacy regarding any communication or data transiting or stored on this information system at any time and for any lawful Government purpose, the Government may monitor, intercept, audit, and search and seize any communication or data transiting or stored on this information system; and any communications or data transiting or stored on this information system may be disclosed or used for any lawful Government purpose. This information system may contain Controlled Unclassified Information (CUI) that is subject to safeguarding or dissemination controls in accordance with law, regulation, or Government-wide policy. Accessing and using this system indicates your understanding of this warning. stig: |- You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions: diff --git a/rules/os/os_policy_banner_ssh_configure.yaml b/rules/os/os_policy_banner_ssh_configure.yaml index b7de3ed5..0847e99f 100644 --- a/rules/os/os_policy_banner_ssh_configure.yaml +++ b/rules/os/os_policy_banner_ssh_configure.yaml @@ -36,7 +36,7 @@ macOS: - "12.0" odv: hint: "Organization's Policy Text" - default: |- + recommended: |- You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions: -The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations. -At any time, the USG may inspect and seize data stored on this IS. diff --git a/rules/os/os_ssh_server_alive_count_max_configure.yaml b/rules/os/os_ssh_server_alive_count_max_configure.yaml index 9aff9e5e..83ee4b0f 100644 --- a/rules/os/os_ssh_server_alive_count_max_configure.yaml +++ b/rules/os/os_ssh_server_alive_count_max_configure.yaml @@ -46,7 +46,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 0 + recommended: 0 tags: - 800-53r5_moderate - 800-53r5_high diff --git a/rules/os/os_ssh_server_alive_interval_configure.yaml b/rules/os/os_ssh_server_alive_interval_configure.yaml index 56d267d5..95ca0f42 100644 --- a/rules/os/os_ssh_server_alive_interval_configure.yaml +++ b/rules/os/os_ssh_server_alive_interval_configure.yaml @@ -49,7 +49,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 900 + recommended: 900 tags: - 800-53r5_moderate - 800-53r5_high diff --git a/rules/os/os_sshd_client_alive_count_max_configure.yaml b/rules/os/os_sshd_client_alive_count_max_configure.yaml index 4a8c1bc6..0ac6ce14 100644 --- a/rules/os/os_sshd_client_alive_count_max_configure.yaml +++ b/rules/os/os_sshd_client_alive_count_max_configure.yaml @@ -32,7 +32,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 0 + recommended: 0 stig: 0 tags: - 800-53r5_moderate diff --git a/rules/os/os_sshd_client_alive_interval_configure.yaml b/rules/os/os_sshd_client_alive_interval_configure.yaml index 246a2b58..43fd3750 100644 --- a/rules/os/os_sshd_client_alive_interval_configure.yaml +++ b/rules/os/os_sshd_client_alive_interval_configure.yaml @@ -35,7 +35,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 900 + recommended: 900 stig: 900 tags: - 800-53r5_moderate diff --git a/rules/os/os_sshd_login_grace_time_configure.yaml b/rules/os/os_sshd_login_grace_time_configure.yaml index 470f2af3..4ee916b1 100644 --- a/rules/os/os_sshd_login_grace_time_configure.yaml +++ b/rules/os/os_sshd_login_grace_time_configure.yaml @@ -32,7 +32,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 30 + recommended: 30 stig: 30 tags: - stig diff --git a/rules/os/os_sudo_timeout_configure.yaml b/rules/os/os_sudo_timeout_configure.yaml index d22af638..e61388bb 100644 --- a/rules/os/os_sudo_timeout_configure.yaml +++ b/rules/os/os_sudo_timeout_configure.yaml @@ -34,7 +34,7 @@ macOS: - "12.0" odv: hint: "Number of minutes." - default: 0 + recommended: 0 cis_lvl1: 0 cis_lvl2: 0 tags: diff --git a/rules/pwpolicy/pwpolicy_account_inactivity_enforce.yaml b/rules/pwpolicy/pwpolicy_account_inactivity_enforce.yaml index 1828ff67..fa580082 100644 --- a/rules/pwpolicy/pwpolicy_account_inactivity_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_account_inactivity_enforce.yaml @@ -59,7 +59,7 @@ macOS: - "12.0" odv: hint: "Number of days." - default: 35 + recommended: 35 tags: - 800-171 - cnssi-1253 diff --git a/rules/pwpolicy/pwpolicy_account_lockout_enforce.yaml b/rules/pwpolicy/pwpolicy_account_lockout_enforce.yaml index cbf0564c..cfb2cab1 100644 --- a/rules/pwpolicy/pwpolicy_account_lockout_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_account_lockout_enforce.yaml @@ -37,7 +37,7 @@ macOS: - "12.0" odv: hint: "Number of failed attempts." - default: 3 + recommended: 3 stig: 3 cis_lvl1: 5 cis_lvl2: 5 diff --git a/rules/pwpolicy/pwpolicy_account_lockout_timeout_enforce.yaml b/rules/pwpolicy/pwpolicy_account_lockout_timeout_enforce.yaml index 6066b242..2826706c 100644 --- a/rules/pwpolicy/pwpolicy_account_lockout_timeout_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_account_lockout_timeout_enforce.yaml @@ -37,7 +37,7 @@ macOS: - "12.0" odv: hint: "Number of minutes." - default: 15 + recommended: 15 stig: 15 tags: - 800-171 diff --git a/rules/pwpolicy/pwpolicy_history_enforce.yaml b/rules/pwpolicy/pwpolicy_history_enforce.yaml index a3fd7375..8412e0ef 100644 --- a/rules/pwpolicy/pwpolicy_history_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_history_enforce.yaml @@ -44,7 +44,7 @@ macOS: - "12.0" odv: hint: "Number of previous passwords." - default: 5 + recommended: 5 stig: 5 cis_lvl1: 15 cis_lvl2: 15 diff --git a/rules/pwpolicy/pwpolicy_max_lifetime_enforce.yaml b/rules/pwpolicy/pwpolicy_max_lifetime_enforce.yaml index f406a1c2..e42d4c83 100644 --- a/rules/pwpolicy/pwpolicy_max_lifetime_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_max_lifetime_enforce.yaml @@ -45,7 +45,7 @@ macOS: - "12.0" odv: hint: "Number of days." - default: 60 + recommended: 60 stig: 60 tags: - 800-171 diff --git a/rules/pwpolicy/pwpolicy_minimum_length_enforce.yaml b/rules/pwpolicy/pwpolicy_minimum_length_enforce.yaml index 2bcab441..487fd179 100644 --- a/rules/pwpolicy/pwpolicy_minimum_length_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_minimum_length_enforce.yaml @@ -45,7 +45,7 @@ macOS: - "12.0" odv: hint: "Minimum password length." - default: 15 + recommended: 15 stig: 15 cis_lvl1: 15 cis_lvl2: 15 diff --git a/rules/pwpolicy/pwpolicy_minimum_lifetime_enforce.yaml b/rules/pwpolicy/pwpolicy_minimum_lifetime_enforce.yaml index 2d1f97b5..ba820887 100644 --- a/rules/pwpolicy/pwpolicy_minimum_lifetime_enforce.yaml +++ b/rules/pwpolicy/pwpolicy_minimum_lifetime_enforce.yaml @@ -63,7 +63,7 @@ macOS: - "12.0" odv: hint: "Number of hours." - default: 24 + recommended: 24 tags: - 800-171 - cnssi-1253 diff --git a/rules/sysprefs/sysprefs_loginwindow_loginwindowtext_enable.yaml b/rules/sysprefs/sysprefs_loginwindow_loginwindowtext_enable.yaml index be8e9f7b..926dfec8 100644 --- a/rules/sysprefs/sysprefs_loginwindow_loginwindowtext_enable.yaml +++ b/rules/sysprefs/sysprefs_loginwindow_loginwindowtext_enable.yaml @@ -35,7 +35,7 @@ macOS: - "12.0" odv: hint: "Organization's approved message." - default: Center for Internet Security Test Message + recommended: Center for Internet Security Test Message cis_lvl1: Center for Internet Security Test Message cis_lvl2: Center for Internet Security Test Message tags: diff --git a/rules/sysprefs/sysprefs_screensaver_ask_for_password_delay_enforce.yaml b/rules/sysprefs/sysprefs_screensaver_ask_for_password_delay_enforce.yaml index db12ef9a..6d613cdc 100644 --- a/rules/sysprefs/sysprefs_screensaver_ask_for_password_delay_enforce.yaml +++ b/rules/sysprefs/sysprefs_screensaver_ask_for_password_delay_enforce.yaml @@ -44,7 +44,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 5 + recommended: 5 stig: 5 cis_lvl1: 5 cis_lvl2: 5 diff --git a/rules/sysprefs/sysprefs_screensaver_timeout_enforce.yaml b/rules/sysprefs/sysprefs_screensaver_timeout_enforce.yaml index 2d3d0f48..db544789 100644 --- a/rules/sysprefs/sysprefs_screensaver_timeout_enforce.yaml +++ b/rules/sysprefs/sysprefs_screensaver_timeout_enforce.yaml @@ -45,7 +45,7 @@ macOS: - "12.0" odv: hint: "Number of seconds." - default: 1200 + recommended: 1200 stig: 900 cis_lvl1: 1200 cis_lvl2: 1200 @@ -61,6 +61,7 @@ tags: - cis_lvl2 - cisv8 - stig + - test severity: "medium" mobileconfig: true mobileconfig_info: diff --git a/rules/sysprefs/sysprefs_time_server_configure.yaml b/rules/sysprefs/sysprefs_time_server_configure.yaml index 62c4a77c..73be2337 100644 --- a/rules/sysprefs/sysprefs_time_server_configure.yaml +++ b/rules/sysprefs/sysprefs_time_server_configure.yaml @@ -40,7 +40,7 @@ macOS: - "12.0" odv: hint: "Name of timeserver(s) separated by commas." - default: "time-a.nist.gov,time-b.nist.gov" + recommended: "time-a.nist.gov,time-b.nist.gov" stig: "time-a.nist.gov,time-b.nist.gov" cis_lvl1: "time.apple.com" cis_lvl2: "time.apple.com" diff --git a/scripts/generate_baseline.py b/scripts/generate_baseline.py index c6e0208d..e1796a31 100755 --- a/scripts/generate_baseline.py +++ b/scripts/generate_baseline.py @@ -212,7 +212,7 @@ def available_tags(all_rules): print(tag) return -def output_baseline(rules, os, keyword): +def output_baseline(rules, os, keyword, benchmark="recommended"): inherent_rules = [] permanent_rules = [] na_rules = [] @@ -240,6 +240,7 @@ def output_baseline(rules, os, keyword): output_text = f'title: "macOS {os}: Security Configuration - {keyword}"\n' output_text += f'description: |\n This guide describes the actions to take when securing a macOS {os} system against the {keyword} baseline.\n' output_text += f'authors: |\n |===\n |Name|Organization\n |===\n' + output_text += f'parent_values: "{benchmark}"\n' output_text += 'profile:\n' # sort the rules @@ -345,16 +346,12 @@ def sanitised_input(prompt, type_=None, range_=None, default_=None): else: return ui -def odv_query(rules, keyword): +def odv_query(rules, benchmark): print("The inclusion of any given rule is a risk-based-decision (RBD). While each rule is mapped to an 800-53 control, deploying it in your organization should be part of the decision-making process. \nYou will be prompted to include each rule, and for those with specific organizational defined values (ODV), you will be prompted for those as well.\n") - _established_benchmarks = ['stig', 'cis_lvl1', 'cis_lvl2'] - if any(bm in keyword for bm in _established_benchmarks): + if not benchmark == "recommended": print(f"WARNING: You are attempting to tailor an already established benchmark. Excluding rules or modifying ODVs may not meet the compliance of the established benchmark.\n") - benchmark = keyword - else: - benchmark = "default" - + included_rules = [] queried_rule_ids = [] @@ -378,15 +375,15 @@ def odv_query(rules, keyword): if rule.rule_odv == "missing": continue elif get_odv: - if benchmark == "default": + if benchmark == "recommended": print(f'{rule.rule_odv["hint"]}') - if isinstance(rule.rule_odv["default"], int): - odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the default value ({rule.rule_odv["default"]}): ', int, default_=rule.rule_odv["default"]) - elif isinstance(rule.rule_odv["default"], bool): - odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the default value ({rule.rule_odv["default"]}): ', bool, default_=rule.rule_odv["default"]) + if isinstance(rule.rule_odv["recommended"], int): + odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the recommended value ({rule.rule_odv["recommended"]}): ', int, default_=rule.rule_odv["recommended"]) + elif isinstance(rule.rule_odv["recommended"], bool): + odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the recommended value ({rule.rule_odv["recommended"]}): ', bool, default_=rule.rule_odv["recommended"]) else: - odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the default value ({rule.rule_odv["default"]}): ', str, default_=rule.rule_odv["default"]) - if odv and odv != rule.rule_odv["default"]: + odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the recommended value ({rule.rule_odv["recommended"]}): ', str, default_=rule.rule_odv["recommended"]) + if odv and odv != rule.rule_odv["recommended"]: write_odv_custom_rule(rule, odv) else: print(f'{rule.rule_odv["hint"]}') @@ -467,10 +464,17 @@ def main(): print("No rules found for the keyword provided, please verify from the following list:") available_tags(all_rules) elif args.tailor: + _established_benchmarks = ['stig', 'cis_lvl1', 'cis_lvl2'] + if any(bm in args.keyword for bm in _established_benchmarks): + benchmark = args.keyword + else: + benchmark = "recommended" + # prompt for name of benchmark to be used for filename + tailored_filename = sanitised_input(f'Enter a name for your tailored benchmark or press Enter for the default value ({args.keyword}): ', str, default_=args.keyword) # prompt for inclusion, add ODV - odv_baseline_rules = odv_query(found_rules, args.keyword) - baseline_output_file = open(f"{build_path}/{args.keyword}.yaml", 'w') - baseline_output_file.write(output_baseline(odv_baseline_rules, version_yaml["os"], args.keyword)) + odv_baseline_rules = odv_query(found_rules, benchmark) + baseline_output_file = open(f"{build_path}/{tailored_filename}.yaml", 'w') + baseline_output_file.write(output_baseline(odv_baseline_rules, version_yaml["os"], args.keyword, benchmark)) else: baseline_output_file = open(f"{build_path}/{args.keyword}.yaml", 'w') baseline_output_file.write(output_baseline(found_rules, version_yaml["os"], args.keyword)) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index eb4ebdd7..59837824 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -1,13 +1,10 @@ #!/usr/bin/env python3 # filename: generate_guidance.py # description: Process a given baseline, and output guidance files -import types import sys import os.path import plistlib -from unittest import result import xlwt -import io import glob import os import yaml @@ -20,7 +17,6 @@ from xlwt import Workbook from string import Template from itertools import groupby from uuid import uuid4 -from collections import namedtuple class MacSecurityRule(): @@ -1033,12 +1029,12 @@ fi #fix_script_file.close() compliance_script_file.close() -def fill_in_odv(resulting_yaml, baseline_name): +def fill_in_odv(resulting_yaml, parent_values): fields_to_process = ['title', 'discussion', 'check', 'fix'] _has_odv = False if "odv" in resulting_yaml: try: - odv = str(resulting_yaml['odv'][baseline_name]) + odv = str(resulting_yaml['odv'][parent_values]) _has_odv = True except KeyError: try: @@ -1069,7 +1065,7 @@ def fill_in_odv(resulting_yaml, baseline_name): -def get_rule_yaml(rule_file, custom=False, baseline_name=""): +def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): """ Takes a rule file, checks for a custom version, and returns the yaml for the rule """ global resulting_yaml @@ -1153,7 +1149,7 @@ def get_rule_yaml(rule_file, custom=False, baseline_name=""): except KeyError: resulting_yaml[yaml_field] = og_rule_yaml[yaml_field] - fill_in_odv(resulting_yaml, baseline_name) + fill_in_odv(resulting_yaml, parent_values) return resulting_yaml @@ -1550,6 +1546,7 @@ def main(): baseline_yaml = yaml.load(args.baseline, Loader=yaml.SafeLoader) + parent_values = baseline_yaml['parent_values'] version_file = os.path.join(parent_dir, "VERSION.yaml") with open(version_file) as r: version_yaml = yaml.load(r, Loader=yaml.SafeLoader) @@ -1735,7 +1732,7 @@ def main(): rule_location = rule_path[0] custom=False - rule_yaml = get_rule_yaml(rule_location, custom, baseline_name) + rule_yaml = get_rule_yaml(rule_location, custom, parent_values) # Determine if the references exist and set accordingly try: From 022c2fb49672595216b031e21606edd7713e90d4 Mon Sep 17 00:00:00 2001 From: Bob Gendler Date: Thu, 26 May 2022 13:28:46 -0400 Subject: [PATCH 07/14] default to recommended --- scripts/generate_scap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/generate_scap.py b/scripts/generate_scap.py index 22a799ac..7af7684b 100755 --- a/scripts/generate_scap.py +++ b/scripts/generate_scap.py @@ -223,20 +223,20 @@ def generate_scap(all_rules, all_baselines, args): resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = rule_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value].replace("$ODV",odv_value) except: - odv_label = "default" + odv_label = "recommended" for baseline in all_baselines: found_rules = [] for tag in rule_yaml['tags']: if tag == baseline: - if odv_label != "default" and odv_label == tag or odv_label == "custom": + 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 == "default" or odv_label == "custom": + elif odv_label == "recommended" or odv_label == "custom": if "odv" in rule_yaml: if baseline not in rule_yaml['odv']: From 4816fcbcc825b303c21f49559c31634ce7d4c4e7 Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Thu, 26 May 2022 14:06:17 -0400 Subject: [PATCH 08/14] fixed recommended ODV --- baselines/cnssi-1253.yaml | 1 - scripts/generate_guidance.py | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/baselines/cnssi-1253.yaml b/baselines/cnssi-1253.yaml index 61233cad..99e1d79e 100644 --- a/baselines/cnssi-1253.yaml +++ b/baselines/cnssi-1253.yaml @@ -7,7 +7,6 @@ authors: | |Ekkehard Koch| |Bob Gendler|National Institute of Standards and Technology |=== -parent_values: recommended profile: - section: "authentication" rules: diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 59837824..eb47b61c 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -1041,7 +1041,7 @@ def fill_in_odv(resulting_yaml, parent_values): odv = str(resulting_yaml['odv']['custom']) _has_odv = True except KeyError: - odv = str(resulting_yaml['odv']['default']) + odv = str(resulting_yaml['odv']['recommended']) _has_odv = True else: pass @@ -1546,7 +1546,10 @@ def main(): baseline_yaml = yaml.load(args.baseline, Loader=yaml.SafeLoader) - parent_values = baseline_yaml['parent_values'] + try: + parent_values = baseline_yaml['parent_values'] + except KeyError: + parent_values = "recommended" version_file = os.path.join(parent_dir, "VERSION.yaml") with open(version_file) as r: version_yaml = yaml.load(r, Loader=yaml.SafeLoader) From 1bea03ade9f3217d901f0b43c22ef394d97e4969 Mon Sep 17 00:00:00 2001 From: Bob Gendler Date: Thu, 26 May 2022 15:00:35 -0400 Subject: [PATCH 09/14] removed source,bash from check --- rules/os/os_library_validation_enabled.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/rules/os/os_library_validation_enabled.yaml b/rules/os/os_library_validation_enabled.yaml index a1802e0e..2d6e1aec 100644 --- a/rules/os/os_library_validation_enabled.yaml +++ b/rules/os/os_library_validation_enabled.yaml @@ -10,10 +10,7 @@ check: | result: string: "false" fix: | - [source,bash] - ---- This is implemented by a Configuration Profile. - ---- references: cce: - CCE-91108-1 From 5b0cf2d5a31b0793d639dc4645391b72ff5690d9 Mon Sep 17 00:00:00 2001 From: Bob Gendler Date: Thu, 26 May 2022 15:05:46 -0400 Subject: [PATCH 10/14] removed echo path for zsh script --- scripts/generate_guidance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index eb47b61c..5a765c3b 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -713,9 +713,9 @@ generate_report(){{ total=$((non_compliant + compliant)) percentage=$(printf %.2f $(( compliant * 100. / total )) ) /bin/echo - /bin/echo "Number of tests passed: ${{GREEN}}$compliant${{STD}}" - /bin/echo "Number of test FAILED: ${{RED}}$non_compliant${{STD}}" - /bin/echo "You are ${{YELLOW}}$percentage%${{STD}} percent compliant!" + echo "Number of tests passed: ${{GREEN}}$compliant${{STD}}" + echo "Number of test FAILED: ${{RED}}$non_compliant${{STD}}" + echo "You are ${{YELLOW}}$percentage%${{STD}} percent compliant!" pause }} From a523eb8a0c11ed5f3a7a5fa01f8b6a27dcb63da9 Mon Sep 17 00:00:00 2001 From: Bob Gendler Date: Thu, 26 May 2022 16:41:44 -0400 Subject: [PATCH 11/14] removed printf path --- scripts/generate_guidance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 5a765c3b..0766341c 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -631,7 +631,7 @@ ask() {{ fi # Ask the question - use /dev/tty in case stdin is redirected from somewhere else - /usr/bin/printf "${{YELLOW}} $1 [$prompt] ${{STD}}" + printf "${{YELLOW}} $1 [$prompt] ${{STD}}" read REPLY # Default? From aefc5d0aa3f6a0e29c5b70a2093b5b7239dc0e63 Mon Sep 17 00:00:00 2001 From: Dan Brodjieski Date: Fri, 27 May 2022 11:09:32 -0400 Subject: [PATCH 12/14] added 'all' option to tailoring --- scripts/generate_baseline.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/generate_baseline.py b/scripts/generate_baseline.py index e1796a31..7afd2953 100755 --- a/scripts/generate_baseline.py +++ b/scripts/generate_baseline.py @@ -355,6 +355,7 @@ def odv_query(rules, benchmark): included_rules = [] queried_rule_ids = [] + include_all = False for rule in rules: get_odv = False @@ -363,13 +364,21 @@ def odv_query(rules, benchmark): if any(tag in rule.rule_tags for tag in _always_include): #print(f"Including rule {rule.rule_id} by default") include = "Y" + elif include_all: + include = "Y" + get_odv = True + queried_rule_ids.append(rule.rule_id) + remove_odv_custom_rule(rule) else: if rule.rule_id not in queried_rule_ids: - include = sanitised_input(f"Would you like to include the rule for \"{rule.rule_id}\" in your benchmark? [Y/n]: ", str.lower, range_=('y', 'n'), default_="y") + include = sanitised_input(f"Would you like to include the rule for \"{rule.rule_id}\" in your benchmark? [Y/n/all]: ", str.lower, range_=('y', 'n', 'all'), default_="y") queried_rule_ids.append(rule.rule_id) get_odv = True # remove custom ODVs if there, they will be re-written if needed remove_odv_custom_rule(rule) + if include.upper() == "ALL": + include_all = True + include = "y" if include.upper() == "Y": included_rules.append(rule) if rule.rule_odv == "missing": @@ -386,7 +395,7 @@ def odv_query(rules, benchmark): if odv and odv != rule.rule_odv["recommended"]: write_odv_custom_rule(rule, odv) else: - print(f'{rule.rule_odv["hint"]}') + print(f'\nODV value: {rule.rule_odv["hint"]}') if isinstance(rule.rule_odv[benchmark], int): odv = sanitised_input(f'Enter the ODV for \"{rule.rule_id}\" or press Enter for the default value ({rule.rule_odv[benchmark]}): ', int, default_=rule.rule_odv[benchmark]) elif isinstance(rule.rule_odv[benchmark], bool): From ab4f0ea54f886d67470c7baaa62aa48300af1c32 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 28 May 2022 20:12:37 +1000 Subject: [PATCH 13/14] Updating `zsh_fix_footer` to run multiple functions if passed --- scripts/generate_guidance.py | 182 +++++++++++++++++------------------ 1 file changed, 89 insertions(+), 93 deletions(-) diff --git a/scripts/generate_guidance.py b/scripts/generate_guidance.py index 0766341c..a8a83c50 100755 --- a/scripts/generate_guidance.py +++ b/scripts/generate_guidance.py @@ -279,7 +279,7 @@ class PayloadDict: """ output_file_path = output_path.name preferences_path = os.path.dirname(output_file_path) - + settings_dict = {} for i in self.data['PayloadContent']: @@ -298,7 +298,7 @@ class PayloadDict: for setting in value['Forced']: for key, value in setting['mcx_preference_settings'].items(): settings_dict[key] = value - + #preferences_output_path = open(preferences_output_file, 'wb') plistlib.dump(settings_dict, fp) print(f"Settings plist written to {preferences_output_file}") @@ -317,10 +317,10 @@ class PayloadDict: for key,value in i.items(): if not key.startswith("Payload"): settings_dict[key] = value - + plistlib.dump(settings_dict, output_path) print(f"Settings plist written to {output_path.name}") - + def makeNewUUID(): return str(uuid4()) @@ -364,7 +364,7 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign except OSError: print("Creation of the directory %s failed" % unsigned_mobileconfig_output_path) - + if signing: signed_mobileconfig_output_path = os.path.join( f'{build_path}', 'mobileconfigs', 'signed') @@ -402,7 +402,7 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign #for rule in glob.glob('../rules/*/{}.yaml'.format(profile_rule)) + glob.glob('../custom/rules/**/{}.yaml'.format(profile_rule),recursive=True): rule_yaml = get_rule_yaml(rule, custom) - + if rule_yaml['mobileconfig']: for payload_type, info in rule_yaml['mobileconfig_info'].items(): valid = True @@ -428,7 +428,7 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign profile_errors.append(rule) logging.debug(e) valid = False - + if valid: if payload_type == "com.apple.systemuiserver": for setting_key, setting_value in info['mount-controls'].items(): @@ -482,7 +482,7 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign displayname=displayname, description=description) - + if payload == "com.apple.ManagedClient.preferences": for item in settings: @@ -511,13 +511,13 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign newProfile.finalizeAndSave(config_file) newProfile.finalizeAndSavePlist(settings_config_file) config_file.close() - + print(f""" CAUTION: These configuration profiles are intended for evaluation in a TEST - environment. Certain configuration profiles (Smartcards), when applied could - leave a system in a state where a user can no longer login with a password. + environment. Certain configuration profiles (Smartcards), when applied could + leave a system in a state where a user can no longer login with a password. Please use caution when applying configuration settings to a system. - + NOTE: If an MDM is already being leveraged, many of these profile settings may be available through the vendor. """) @@ -525,7 +525,7 @@ def generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, sign def default_audit_plist(baseline_name, build_path, baseline_yaml): """"Generate the default audit plist file to define exemptions """ - + # Output folder plist_output_path = os.path.join( f'{build_path}', 'preferences') @@ -548,7 +548,7 @@ def default_audit_plist(baseline_name, build_path, baseline_yaml): if profile_rule.startswith("supplemental"): continue plist_dict[profile_rule] = { "exempt": False } - + plistlib.dump(plist_dict, plist_file) @@ -567,7 +567,7 @@ def generate_script(baseline_name, build_path, baseline_yaml, reference): ## This script will attempt to audit all of the settings based on the installed profile. -## This script is provided as-is and should be fully tested on a system that is not in a production environment. +## This script is provided as-is and should be fully tested on a system that is not in a production environment. ################### Variables ################### @@ -633,7 +633,7 @@ ask() {{ # Ask the question - use /dev/tty in case stdin is redirected from somewhere else printf "${{YELLOW}} $1 [$prompt] ${{STD}}" read REPLY - + # Default? if [ -z "$REPLY" ]; then REPLY=$default @@ -651,7 +651,7 @@ ask() {{ # function to display menus show_menus() {{ /usr/bin/clear - /bin/echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + /bin/echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" /bin/echo " M A I N - M E N U" /bin/echo " macOS Security Compliance Tool" /bin/echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" @@ -681,7 +681,7 @@ compliance_count(){{ non_compliant=0 results=$(/usr/libexec/PlistBuddy -c "Print" /Library/Preferences/org.{baseline_name}.audit.plist) - + while IFS= read -r line; do if [[ "$line" =~ "finding = false" ]]; then compliant=$((compliant+1)) @@ -690,8 +690,8 @@ compliance_count(){{ non_compliant=$((non_compliant+1)) fi done <<< "$results" - - # Enable output of just the compliant or non-compliant numbers. + + # Enable output of just the compliant or non-compliant numbers. if [[ $1 = "compliant" ]] then /bin/echo $compliant @@ -709,7 +709,7 @@ generate_report(){{ count=($(compliance_count)) compliant=${{count[1]}} non_compliant=${{count[2]}} - + total=$((non_compliant + compliant)) percentage=$(printf %.2f $(( compliant * 100. / total )) ) /bin/echo @@ -720,7 +720,7 @@ generate_report(){{ }} view_report(){{ - + if [[ $lastComplianceScan == "No scans have been run" ]];then /bin/echo "no report to run, please run new scan" pause @@ -734,7 +734,7 @@ generate_stats(){{ count=($(compliance_count)) compliant=${{count[1]}} non_compliant=${{count[2]}} - + total=$((non_compliant + compliant)) percentage=$(printf %.2f $(( compliant * 100. / total )) ) /bin/echo "PASSED: $compliant FAILED: $non_compliant, $percentage percent compliant!" @@ -769,7 +769,7 @@ fi logging.debug(f"{rule}") rule_yaml = get_rule_yaml(rule, custom) - + if rule_yaml['id'].startswith("supplemental"): continue @@ -784,7 +784,7 @@ fi arch="i386" except: pass - + # grab the 800-53 controls try: rule_yaml['references']['800-53r5'] @@ -792,14 +792,14 @@ fi nist_80053r5 = 'N/A' else: nist_80053r5 = rule_yaml['references']['800-53r5'] - + if reference == "default": log_reference_id = [rule_yaml['id']] else: - try: + try: rule_yaml['references'][reference] except KeyError: - try: + try: rule_yaml['references']['custom'][reference] except KeyError: log_reference_id = [rule_yaml['id']] @@ -813,8 +813,8 @@ fi log_reference_id = rule_yaml['references'][reference] + [rule_yaml['id']] else: log_reference_id = [rule_yaml['references'][reference]] + [rule_yaml['id']] - - + + # group the controls if not nist_80053r5 == "N/A": nist_80053r5.sort() @@ -861,16 +861,16 @@ if [[ "$arch" == "$rule_arch" ]] || [[ -z "$rule_arch" ]]; then # check to see if rule is exempt unset exempt unset exempt_reason - + exempt=$(/usr/bin/osascript -l JavaScript << EOS 2>/dev/null -ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{7}.audit').objectForKey('{0}'))["exempt"] +ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{7}.audit').objectForKey('{0}'))["exempt"] EOS ) exempt_reason=$(/usr/bin/osascript -l JavaScript << EOS 2>/dev/null -ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{7}.audit').objectForKey('{0}'))["exempt_reason"] +ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{7}.audit').objectForKey('{0}'))["exempt_reason"] EOS ) - + if [[ $result_value == "{4}" ]]; then /bin/echo "$(date -u) {5} passed (Result: $result_value, Expected: "{3}")" | /usr/bin/tee -a "$audit_log" /usr/bin/defaults write "$audit_plist" {0} -dict-add finding -bool NO @@ -887,8 +887,8 @@ EOS /bin/sleep 1 fi fi - - + + else /bin/echo "$(date -u) {5} does not apply to this architechture" | tee -a "$audit_log" /usr/bin/defaults write "$audit_plist" {0} -dict-add finding -bool NO @@ -918,12 +918,12 @@ unset exempt unset exempt_reason exempt=$(/usr/bin/osascript -l JavaScript << EOS 2>/dev/null -ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{baseline_name}.audit').objectForKey('{rule_yaml['id']}'))["exempt"] +ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{baseline_name}.audit').objectForKey('{rule_yaml['id']}'))["exempt"] EOS ) exempt_reason=$(/usr/bin/osascript -l JavaScript << EOS 2>/dev/null -ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{baseline_name}.audit').objectForKey('{rule_yaml['id']}'))["exempt_reason"] +ObjC.unwrap($.NSUserDefaults.alloc.initWithSuiteName('org.{baseline_name}.audit').objectForKey('{rule_yaml['id']}'))["exempt_reason"] EOS ) @@ -965,7 +965,7 @@ if [[ ! -e "$audit_plist" ]]; then pause show_menus read_options - else + else exit 1 fi fi @@ -982,7 +982,7 @@ fi # append to existing logfile /bin/echo "$(date -u) Beginning remediation of non-compliant settings" >> "$audit_log" -# run mcxrefresh +# run mcxrefresh /usr/bin/mcxrefresh -u $CURR_USER_UID @@ -996,16 +996,12 @@ fi zparseopts -D -E -check=check -fix=fix -stats=stats -compliant=compliant -non_compliant=non_compliant -if [[ $check ]];then - run_scan -elif [[ $fix ]];then - run_fix -elif [[ $stats ]];then - generate_stats -elif [[ $compliant ]];then - compliance_count "compliant" -elif [[ $non_compliant ]];then - compliance_count "non-compliant" +if [[ $check ]] || [[ $fix ]] || [[ $stats ]] || [[ $compliant ]] || [[ $non_compliant ]]; then + if [[ $fix ]]; then run_fix; fi + if [[ $check ]]; then run_scan; fi + if [[ $stats ]];then generate_stats; fi + if [[ $compliant ]];then compliance_count "compliant"; fi + if [[ $non_compliant ]];then compliance_count "non-compliant"; fi else while true; do show_menus @@ -1050,25 +1046,25 @@ def fill_in_odv(resulting_yaml, parent_values): for field in fields_to_process: if "$ODV" in resulting_yaml[field]: resulting_yaml[field]=resulting_yaml[field].replace("$ODV", odv) - + for result_value in resulting_yaml['result']: if "$ODV" in str(resulting_yaml['result'][result_value]): resulting_yaml['result'][result_value] = odv - + if resulting_yaml['mobileconfig_info']: for mobileconfig_type in resulting_yaml['mobileconfig_info']: if isinstance(resulting_yaml['mobileconfig_info'][mobileconfig_type], dict): for mobileconfig_value in resulting_yaml['mobileconfig_info'][mobileconfig_type]: if "$ODV" in str(resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value]): resulting_yaml['mobileconfig_info'][mobileconfig_type][mobileconfig_value] = odv - - - - + + + + def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): """ Takes a rule file, checks for a custom version, and returns the yaml for the rule """ - global resulting_yaml + 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) @@ -1084,14 +1080,14 @@ def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): 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: #assume this is a completely new rule og_rule_path = glob.glob('../custom/rules/**/{}'.format(file_name), recursive=True)[0] resulting_yaml['customized'] = ["customized rule"] - + # get original/default rule yaml for comparison with open(og_rule_path) as og: og_rule_yaml = yaml.load(og, Loader=yaml.SafeLoader) @@ -1113,7 +1109,7 @@ def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): resulting_yaml['references'][ref] = rule_yaml['references'][ref] except KeyError: resulting_yaml['references'][ref] = og_rule_yaml['references'][ref] - try: + try: if "custom" in rule_yaml['references']: resulting_yaml['references']['custom'] = rule_yaml['references']['custom'] if 'customized' in resulting_yaml: @@ -1134,7 +1130,7 @@ def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): resulting_yaml['tags'] = og_rule_yaml['tags'] + rule_yaml['tags'] except KeyError: resulting_yaml['tags'] = og_rule_yaml['tags'] - else: + else: try: if og_rule_yaml[yaml_field] == rule_yaml[yaml_field]: #print("using default data in yaml field {}".format(yaml_field)) @@ -1148,7 +1144,7 @@ def get_rule_yaml(rule_file, custom=False, parent_values="recommended"): resulting_yaml['customized'] = ["customized {}".format(yaml_field)] except KeyError: resulting_yaml[yaml_field] = og_rule_yaml[yaml_field] - + fill_in_odv(resulting_yaml, parent_values) return resulting_yaml @@ -1190,7 +1186,7 @@ def generate_xls(baseline_name, build_path, baseline_yaml): sheet1.write(0, 10, "SRG", headers) sheet1.write(0, 11, "DISA STIG", headers) sheet1.write(0, 12, "CIS Benchmark", headers) - sheet1.write(0, 13, "CIS v8", headers) + sheet1.write(0, 13, "CIS v8", headers) sheet1.write(0, 14, "CCI", headers) sheet1.write(0, 15, "Modifed Rule", headers) sheet1.set_panes_frozen(True) @@ -1201,7 +1197,7 @@ def generate_xls(baseline_name, build_path, baseline_yaml): for rule in baseline_rules: if rule.rule_id.startswith("supplemental") or rule.rule_id.startswith("srg"): continue - + sheet1.write(counter, 0, rule.rule_cce, top) sheet1.col(0).width = 256 * 15 sheet1.write(counter, 1, rule.rule_id, top) @@ -1298,12 +1294,12 @@ def generate_xls(baseline_name, build_path, baseline_yaml): if title not in custom_ref_column: custom_ref_column[title] = column_counter column_counter = column_counter + 1 - sheet1.write(0, custom_ref_column[title], title, headers) + sheet1.write(0, custom_ref_column[title], title, headers) sheet1.col(custom_ref_column[title]).width = 512 * 25 added_ref = (str(ref)).strip('[]\'') added_ref = added_ref.replace(", ", "\n").replace("\'", "") sheet1.write(counter, custom_ref_column[title], added_ref, topWrap) - + tall_style = xlwt.easyxf('font:height 640;') # 36pt @@ -1426,7 +1422,7 @@ def is_asciidoctor_installed(): cmd = "which asciidoctor" process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) output, error = process.communicate() - + # return path to asciidoctor return output.decode("utf-8").strip() @@ -1447,7 +1443,7 @@ def verify_signing_hash(hash): with tempfile.NamedTemporaryFile(mode="w") as in_file: unsigned_tmp_file_path=in_file.name in_file.write("temporary file for signing") - + cmd = f"security cms -S -Z {hash} -i {unsigned_tmp_file_path}" FNULL = open(os.devnull, 'w') process = subprocess.Popen(cmd.split(), stdout=FNULL, stderr=FNULL) @@ -1456,7 +1452,7 @@ def verify_signing_hash(hash): return True else: return False - + def sign_config_profile(in_file, out_file, hash): """Signs the configuration profile using the identity associated with the provided hash """ @@ -1539,11 +1535,11 @@ def main(): log_reference = args.reference else: log_reference = "default" - use_custom_reference = False + use_custom_reference = False except IOError as msg: parser.error(str(msg)) - + baseline_yaml = yaml.load(args.baseline, Loader=yaml.SafeLoader) try: @@ -1554,17 +1550,17 @@ def main(): with open(version_file) as r: version_yaml = yaml.load(r, Loader=yaml.SafeLoader) - adoc_templates = [ "adoc_rule", - "adoc_supplemental", - "adoc_rule_no_setting", + adoc_templates = [ "adoc_rule", + "adoc_supplemental", + "adoc_rule_no_setting", "adoc_rule_custom_refs", - "adoc_section", - "adoc_header", - "adoc_footer", + "adoc_section", + "adoc_header", + "adoc_footer", "adoc_foreword", - "adoc_scope", - "adoc_authors", - "adoc_acronyms", + "adoc_scope", + "adoc_authors", + "adoc_acronyms", "adoc_additional_docs" ] adoc_templates_dict = {} @@ -1576,7 +1572,7 @@ def main(): adoc_templates_dict[template] = f"../custom/templates/{template}.adoc" else: adoc_templates_dict[template] = f"../templates/{template}.adoc" - + # check for custom PDF theme (must have theme in the name and end with .yml) pdf_theme="mscp-theme.yml" themes = glob.glob('../custom/templates/*theme*.yml') @@ -1585,7 +1581,7 @@ def main(): elif len(themes) == 1 : print(f"Found custom PDF theme: {themes[0]}") pdf_theme = themes[0] - + # Setup AsciiDoc templates @@ -1597,7 +1593,7 @@ def main(): with open(adoc_templates_dict['adoc_rule_no_setting']) as adoc_rule_no_setting_file: adoc_rule_no_setting_template = Template(adoc_rule_no_setting_file.read()) - + with open(adoc_templates_dict['adoc_rule_custom_refs']) as adoc_rule_custom_refs_file: adoc_rule_custom_refs_template = Template(adoc_rule_custom_refs_file.read()) @@ -1609,13 +1605,13 @@ def main(): with open(adoc_templates_dict['adoc_footer']) as adoc_footer_file: adoc_footer_template = Template(adoc_footer_file.read()) - + with open(adoc_templates_dict['adoc_foreword']) as adoc_foreword_file: adoc_foreword_template = adoc_foreword_file.read() + "\n" with open(adoc_templates_dict['adoc_scope']) as adoc_scope_file: adoc_scope_template = Template(adoc_scope_file.read() +"\n") - + with open(adoc_templates_dict['adoc_authors']) as adoc_authors_file: adoc_authors_template = Template(adoc_authors_file.read() + "\n") @@ -1640,7 +1636,7 @@ def main(): adoc_171_show=":show_171:" else: adoc_171_show=":show_171!:" - + if args.gary: adoc_tag_show=":show_tags:" adoc_STIG_show=":show_STIG:" @@ -1687,7 +1683,7 @@ def main(): adoc_output_file.write(adoc_acronyms_template) adoc_output_file.write(adoc_additional_docs_template) - + # Create sections and rules for sections in baseline_yaml['profile']: @@ -1734,7 +1730,7 @@ def main(): else: rule_location = rule_path[0] custom=False - + rule_yaml = get_rule_yaml(rule_location, custom, parent_values) # Determine if the references exist and set accordingly @@ -1758,7 +1754,7 @@ def main(): nist_80053r5 = 'N/A' else: nist_80053r5 = rule_yaml['references']['800-53r5'] - + try: rule_yaml['references']['800-171r2'] except KeyError: @@ -1912,16 +1908,16 @@ def main(): # Output footer adoc_output_file.write(footer_adoc) adoc_output_file.close() - + if args.profiles: print("Generating configuration profiles...") generate_profiles(baseline_name, build_path, parent_dir, baseline_yaml, signing, args.hash) - + if args.script: print("Generating compliance script...") generate_script(baseline_name, build_path, baseline_yaml, log_reference) default_audit_plist(baseline_name, build_path, baseline_yaml) - + if args.xls: print('Generating excel document...') generate_xls(baseline_name, build_path, baseline_yaml) @@ -1934,7 +1930,7 @@ def main(): process.communicate() else: print("If you would like to generate the HTML file from the AsciiDoc file, install the ruby gem for asciidoctor") - + asciidoctorPDF_path = is_asciidoctor_pdf_installed() # Don't create PDF if we are generating SCAP From 79084341c20ea30ef1ed6c02a82e867d631379e8 Mon Sep 17 00:00:00 2001 From: Allen Golbig Date: Thu, 2 Jun 2022 10:53:42 -0400 Subject: [PATCH 14/14] fixed spacing --- rules/supplemental/supplemental_cis_manual.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/supplemental/supplemental_cis_manual.yaml b/rules/supplemental/supplemental_cis_manual.yaml index ddee6059..eda052dd 100644 --- a/rules/supplemental/supplemental_cis_manual.yaml +++ b/rules/supplemental/supplemental_cis_manual.yaml @@ -1,5 +1,5 @@ id: supplemental_cis_manual -title: " CIS Manual Recommendations" +title: "CIS Manual Recommendations" discussion: | List of CIS recommendations that are manual check in the CIS macOS Benchmark.