mirror of
https://github.com/usnistgov/macos_security.git
synced 2026-02-03 14:03:24 +00:00
Merge branch 'dev_2.0' into dev_2.0_generate_mapping
This commit is contained in:
@@ -86,8 +86,13 @@ Deploy a configuration profile containing the following payload.
|
||||
|===
|
||||
|
||||
|ID
|
||||
|{{ rule.rule_id }}
|
||||
{{ rule.severity if rule.severity is not none and rule.tags not in check_tags }}
|
||||
|{{ rule.rule_id }}
|
||||
|
||||
{% if rule.severity is not none %}
|
||||
|{% trans %}Severity{% endtrans %}
|
||||
|{{ rule.severity }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|{% trans %}References{% endtrans %}
|
||||
|
|
||||
|
||||
@@ -322,7 +322,7 @@ run_scan(){
|
||||
|
||||
{% for profile in baseline.profile %}
|
||||
{% for rule in profile.rules %}
|
||||
{% if "manual" not in rule.tags %}
|
||||
{% if "manual" not in rule.tags and "Excluded" not in rule.section %}
|
||||
{% include "check.jinja" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -363,7 +363,9 @@ run_fix(){
|
||||
|
||||
{% for profile in baseline.profile %}
|
||||
{% for rule in profile.rules %}
|
||||
{% include "fix.jinja" %}
|
||||
{% if "manual" not in rule.tags and "Excluded" not in rule.section %}
|
||||
{% include "fix.jinja" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
@@ -175,7 +175,9 @@ run_default_state(){
|
||||
|
||||
{% for profile in baseline.profile %}
|
||||
{% for rule in profile.rules %}
|
||||
{% include "restore.jinja" %}
|
||||
{% if "manual" not in rule.tags and "Excluded" not in rule.section %}
|
||||
{% include "restore.jinja" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
@@ -298,6 +298,23 @@
|
||||
"max": 90
|
||||
},
|
||||
"note": "Delay the availability of a software update in days, e.g. 30"
|
||||
},
|
||||
{
|
||||
"ruleId": "os_tv_content_allowed",
|
||||
"type": "number",
|
||||
"validation": {
|
||||
"min": 0,
|
||||
"max": 1000
|
||||
},
|
||||
"note": "Possible values, with the U.S. description of the rating level: `1000`: All | `600`: TV-MA | `500`: TV-14 | `400`: TV-PG | `300`: TV-G | `200`: TV-Y7 | `100`: TV-Y | `0`: None"
|
||||
},
|
||||
{
|
||||
"ruleId": "os_movie_content_allowed",
|
||||
"type": "number",
|
||||
"validation": {
|
||||
"min": 0,
|
||||
"max": 1000
|
||||
},
|
||||
"note": "Possible values, with the U.S. description of the rating level: `1000`: All | `500`: NC-17 | `400`: R | `300`: PG-13 | `200`: PG | `100`: G | `0`: None"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from ..common_utils.logger_instance import logger
|
||||
|
||||
_SENTINEL = object()
|
||||
|
||||
|
||||
class Sectionmap(StrEnum):
|
||||
AUDIT = "auditing"
|
||||
AUTH = "authentication"
|
||||
@@ -157,7 +158,6 @@ class References(BaseModelWithAccessors):
|
||||
bsi: bsiReferences | None = None
|
||||
custom_refs: customReferences | None = None
|
||||
|
||||
|
||||
def get_ref(
|
||||
self,
|
||||
key: str,
|
||||
@@ -221,8 +221,9 @@ class References(BaseModelWithAccessors):
|
||||
# Not found
|
||||
if default is not _SENTINEL:
|
||||
return default
|
||||
raise KeyError(f"Field '{key}' not found in any namespace ({', '.join(search_order)})")
|
||||
|
||||
raise KeyError(
|
||||
f"Field '{key}' not found in any namespace ({', '.join(search_order)})"
|
||||
)
|
||||
|
||||
|
||||
class Macsecurityrule(BaseModelWithAccessors):
|
||||
@@ -234,7 +235,6 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
Attributes:
|
||||
title (str): The title of the security rule.
|
||||
rule_id (str): Unique identifier for the rule.
|
||||
severity (str): Severity level of the rule.
|
||||
discussion (str): Detailed discussion or rationale for the rule.
|
||||
references (References): Reference information (e.g., NIST, CIS) associated with the rule.
|
||||
odv (dict[str, Any] | None): Organizational Defined Values for the rule, if applicable.
|
||||
@@ -254,7 +254,7 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
os_version: float = Field(default_factory=float)
|
||||
check (str): The commands to evaluate the state of a rule.
|
||||
fix: (str): The commands to remediate and set the configuration for a rule.
|
||||
severity: (dict[str, Any]): The category for impact assigned to a rule for associated benchmarks.
|
||||
severity: (str): The category for impact assigned to a rule for associated benchmarks.
|
||||
default_state: (str): The command to restore the system to the default configuration for a rule.
|
||||
|
||||
Class Methods:
|
||||
@@ -299,7 +299,7 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
os_version: float = Field(default_factory=float)
|
||||
check: str | None = None
|
||||
fix: str | None = None
|
||||
severity: dict[str, Any] | None = None
|
||||
severity: str | None = None
|
||||
default_state: str | None = None
|
||||
|
||||
@classmethod
|
||||
@@ -360,7 +360,7 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
default_state_value: str | None = None
|
||||
mechanism: str = "Manual"
|
||||
payloads: list[Mobileconfigpayload] | None = []
|
||||
severity: dict[str, Any] | None = {}
|
||||
severity: str | None = None
|
||||
tags: list[str] = []
|
||||
|
||||
rule_file = next(
|
||||
@@ -501,8 +501,8 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
if benchmarks:
|
||||
for benchmark in benchmarks:
|
||||
name = benchmark.get("name")
|
||||
if "severity" in benchmark:
|
||||
severity[name] = benchmark["severity"]
|
||||
if "severity" in benchmark and name == parent_values:
|
||||
severity = benchmark.get("severity", "")
|
||||
|
||||
match tags:
|
||||
case "inherent":
|
||||
@@ -536,6 +536,11 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
cis: dict[str, Any] = rule_yaml["references"].get("cis", {})
|
||||
elif ref_key == "bsi":
|
||||
bsi: dict[str, Any] = rule_yaml["references"].get("bsi", {})
|
||||
elif ref_key == "custom": # support for 1.0 custom refs format
|
||||
for custom_ref_key in rule_yaml["references"]["custom"]:
|
||||
custom_refs[custom_ref_key] = rule_yaml["references"][
|
||||
"custom"
|
||||
].get(custom_ref_key, {})
|
||||
else:
|
||||
custom_refs[ref_key] = rule_yaml["references"].get(ref_key, {})
|
||||
|
||||
@@ -588,16 +593,8 @@ class Macsecurityrule(BaseModelWithAccessors):
|
||||
bsi["indigo"] = [bsi["indigo"]]
|
||||
# Map custom references
|
||||
if custom_refs:
|
||||
if "custom_refs" in custom_refs and isinstance(
|
||||
custom_refs["custom_refs"], dict
|
||||
):
|
||||
if custom_refs["custom_refs"] is not None and not isinstance(
|
||||
custom_refs["custom_refs"], list
|
||||
):
|
||||
rule_yaml["references"]["custom_refs"] = {}
|
||||
rule_yaml["references"]["custom_refs"]["references"] = [
|
||||
custom_refs
|
||||
]
|
||||
rule_yaml["references"]["custom_refs"] = {}
|
||||
rule_yaml["references"]["custom_refs"]["references"] = [custom_refs]
|
||||
|
||||
rule = cls(
|
||||
**rule_yaml,
|
||||
|
||||
@@ -245,7 +245,7 @@ class Payload(BaseModel):
|
||||
settings_dict (dict[str, Any]): The settings to save.
|
||||
"""
|
||||
try:
|
||||
create_file(preferences_file, settings_dict)
|
||||
create_file(preferences_file, settings_dict, append=True)
|
||||
logger.success(f"Settings plist written to {preferences_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating plist file {preferences_file}: {e}")
|
||||
|
||||
@@ -404,10 +404,6 @@ def parse_cli() -> None:
|
||||
)
|
||||
sys.exit()
|
||||
|
||||
if args.os_name == "visionos":
|
||||
logger.warning("visionOS is not supported at this time.")
|
||||
sys.exit()
|
||||
|
||||
if args.subcommand == "guidance":
|
||||
if args.os_name != "macos" and args.script:
|
||||
logger.error(
|
||||
|
||||
@@ -46,6 +46,31 @@ def collect_tags_and_benchmarks(
|
||||
return sorted(tags_set), benchmark_platforms
|
||||
|
||||
|
||||
def collect_established_benchmarks(
|
||||
rules: list[Macsecurityrule],
|
||||
) -> list[str]:
|
||||
"""
|
||||
Attempts to collect all established benchmarks in the MSCP library. An established
|
||||
benchmark is one where an ODV has been defined for a given benchmark.
|
||||
|
||||
Args:
|
||||
rules (list[Macsecurityrule]): A list of collected rules from the library.
|
||||
|
||||
Returns:
|
||||
list: A sorted set of discovered benchmarks
|
||||
"""
|
||||
established_benchmarks_set: set[str] = set()
|
||||
|
||||
for rule in rules:
|
||||
for odv in rule.odv or []:
|
||||
established_benchmarks_set.add(odv)
|
||||
|
||||
# remove "hint" from available benchmarks
|
||||
established_benchmarks_set.remove("hint")
|
||||
|
||||
return sorted(established_benchmarks_set)
|
||||
|
||||
|
||||
def print_keyword_summary(
|
||||
tags: list[str], benchmark_platforms: dict[str, set[str]]
|
||||
) -> None:
|
||||
@@ -97,7 +122,7 @@ def generate_baseline(args: argparse.Namespace) -> None:
|
||||
baselines_data: dict = open_file(
|
||||
Path(config.get("includes_dir", ""), "800-53_baselines.yaml")
|
||||
)
|
||||
established_benchmarks: tuple[str, ...] = ("stig", "cis_lvl1", "cis_lvl2")
|
||||
|
||||
# removing misc_tags, unsure we need it.
|
||||
# misc_tags: tuple[str, str, str, str] = (
|
||||
# "permanent",
|
||||
@@ -148,6 +173,8 @@ def generate_baseline(args: argparse.Namespace) -> None:
|
||||
|
||||
all_tags, benchmark_map = collect_tags_and_benchmarks(all_rules)
|
||||
|
||||
established_benchmarks: tuple[str, ...] = collect_established_benchmarks(all_rules)
|
||||
|
||||
if args.list_tags:
|
||||
print_keyword_summary(all_tags, benchmark_map)
|
||||
|
||||
|
||||
@@ -130,7 +130,9 @@ def generate_profiles(
|
||||
signed_output_path: Path = Path(build_path, "mobileconfigs", "signed")
|
||||
plist_output_path: Path = Path(build_path, "mobileconfigs", "preferences")
|
||||
granular_output_path: Path = Path(build_path, "mobileconfigs", "granular")
|
||||
granular_signed_output_path: Path = Path(build_path, "mobileconfigs", "granular", "signed")
|
||||
granular_signed_output_path: Path = Path(
|
||||
build_path, "mobileconfigs", "granular", "signed"
|
||||
)
|
||||
|
||||
make_dir(unsigned_output_path)
|
||||
make_dir(plist_output_path)
|
||||
@@ -144,7 +146,10 @@ def generate_profiles(
|
||||
make_dir(signed_output_path)
|
||||
|
||||
valid_rules: list[Macsecurityrule] = [
|
||||
rule for profile in baseline.profile for rule in profile.rules
|
||||
rule
|
||||
for profile in baseline.profile
|
||||
for rule in profile.rules
|
||||
if "Excluded" not in rule.section
|
||||
]
|
||||
|
||||
grouped_payloads: dict = get_payload_content_by_type(valid_rules)
|
||||
@@ -218,7 +223,8 @@ def generate_profiles(
|
||||
if signing:
|
||||
sign_config_profile(
|
||||
granular_output_path / f"{setting}.mobileconfig",
|
||||
granular_signed_output_path / f"{setting}.mobileconfig",
|
||||
granular_signed_output_path
|
||||
/ f"{setting}.mobileconfig",
|
||||
hash_value,
|
||||
)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user