diff --git a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_incorrect_family.fail.sh b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_incorrect_family.fail.sh index 039d35dece7..d11ad5cd9f6 100644 --- a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_incorrect_family.fail.sh +++ b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_incorrect_family.fail.sh @@ -1,4 +1,5 @@ #!/bin/bash +# check = sce # variables = var_nftables_family=inet,var_nftables_table=filter var_nftables_family="ip" diff --git a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_no_tables.fail.sh b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_no_tables.fail.sh index dc3f2647140..b4be4954bfc 100644 --- a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_no_tables.fail.sh +++ b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_no_tables.fail.sh @@ -1,4 +1,5 @@ #!/bin/bash +# check = sce # variables = var_nftables_family=inet,var_nftables_table=filter nft list tables | diff --git a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_table_present.pass.sh b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_table_present.pass.sh index 04b2411a9ba..2857f6139ca 100644 --- a/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_table_present.pass.sh +++ b/linux_os/guide/system/network/network-nftables/set_nftables_table/tests/nftables_table_present.pass.sh @@ -1,4 +1,5 @@ #!/bin/bash +# check = sce # variables = var_nftables_family=inet,var_nftables_table=filter var_nftables_family="inet" diff --git a/tests/README.md b/tests/README.md index 3e3ba2556b9..d6540ea05cf 100644 --- a/tests/README.md +++ b/tests/README.md @@ -182,6 +182,10 @@ The header consists of comments (starting by `#`). Possible keys are: variables (XCCDF Values), use the `variables` key instead. This key is intended to be used in regression testing of bugs in profiles, it isn't intended for casual use. +- `check` is a string specifying one of the available check engine types + (`oval`, `sce`, `any`). It specifies for which check engine the scenario should + be executed. The special value `any` means that this scenario works with any + check engine and it's the default behavior that is used if this key isn't provided. - `remediation` is a string specifying one of the allowed remediation types (eg. `bash`, `ansible`, `none`). The `none` value means that the tested rule has no implemented remediation. The `none` value can also be used in case that diff --git a/tests/ssg_test_suite/rule.py b/tests/ssg_test_suite/rule.py index 583e7ae2045..d979dea4e2e 100644 --- a/tests/ssg_test_suite/rule.py +++ b/tests/ssg_test_suite/rule.py @@ -406,6 +406,7 @@ def _load_all_tests(self, rule): return all_tests def _get_rule_test_content(self, rule): + checks = xml_operations.find_checks_in_rule(self.datastream, self.benchmark_id, rule.id) all_tests = self._load_all_tests(rule) scenarios = [] other_content = dict() @@ -414,8 +415,8 @@ def _get_rule_test_content(self, rule): if re.search(scenario_matches_regex, file_name): scenario = Scenario(file_name, file_content) scenario.override_profile(self.scenarios_profile) - if scenario.matches_regex_and_platform( - self.scenarios_regex, self.benchmark_cpes): + if scenario.matches_regex_and_check_and_platform( + self.scenarios_regex, checks, self.benchmark_cpes): scenarios.append(scenario) else: other_content[file_name] = file_content @@ -532,6 +533,13 @@ def _verify_rule_presence(self, rule_id, script, profiles): def _check_rule_scenario(self, scenario, remote_rule_dir, rule_id, remediation_available): + if "sce" in scenario.script_params["check"] and not self.test_env.sce_support: + logging.warning( + "Scenario {0} is used for SCE testing but OpenSCAP installed " + "on the back end doesn't support SCE. To test SCE, please " + "install the openscap-engine-sce package on the back end.".format(scenario.script) + ) + return if not _apply_script( remote_rule_dir, self.test_env, scenario.script): logging.error("Environment failed to prepare, skipping test") @@ -586,6 +594,7 @@ def _parse_parameters(self): 'templates': [], 'packages': [], 'platform': ['multi_platform_all'], + 'check': ['any'], 'remediation': ['all'], 'variables': [], } @@ -645,9 +654,22 @@ def matches_platform(self, benchmark_cpes): self.script) return False - def matches_regex_and_platform(self, scenarios_regex, benchmark_cpes): + def matches_check(self, rule_checks): + if "any" in self.script_params["check"]: + return True + for check in self.script_params["check"]: + if check in rule_checks: + return True + logging.warning( + "Script %s is not applicable for %s check type" % + (self.script, ", ".join(self.script_params['check']))) + return False + + + def matches_regex_and_check_and_platform(self, scenarios_regex, checks, benchmark_cpes): return ( self.matches_regex(scenarios_regex) + and self.matches_check(checks) and self.matches_platform(benchmark_cpes)) diff --git a/tests/ssg_test_suite/test_env.py b/tests/ssg_test_suite/test_env.py index e4b490881fb..9db67b90a63 100644 --- a/tests/ssg_test_suite/test_env.py +++ b/tests/ssg_test_suite/test_env.py @@ -11,6 +11,7 @@ import ssg_test_suite from ssg_test_suite import common +from ssg_test_suite.log import LogHelper class SavedState(object): @@ -62,6 +63,7 @@ def __init__(self, scanning_mode): self.running_state = None self.scanning_mode = scanning_mode + self.sce_support = False self.backend = None self.ssh_port = None @@ -101,6 +103,7 @@ def start(self): by subsequent procedures. """ self.refresh_connection_parameters() + self.check_sce_support() def refresh_connection_parameters(self): self.domain_ip = self.get_ip_address() @@ -199,6 +202,16 @@ def online_scan(self, args, verbose_path): def offline_scan(self, args, verbose_path): raise NotImplementedError() + def check_sce_support(self): + log_file_name = os.path.join(LogHelper.LOG_DIR, "env-preparation.log") + with open(log_file_name, 'a') as log_file: + oscap_output = self.execute_ssh_command("oscap --version", log_file) + sce_regex = r"SCE Version: [\d.]+ \(from libopenscap_sce.so.\d+\)" + for line in oscap_output.splitlines(): + if re.match(sce_regex, line): + self.sce_support = True + return + class VMTestEnv(TestEnv): name = "libvirt-based" diff --git a/tests/ssg_test_suite/xml_operations.py b/tests/ssg_test_suite/xml_operations.py index f7dc99eb8fe..627542caddb 100644 --- a/tests/ssg_test_suite/xml_operations.py +++ b/tests/ssg_test_suite/xml_operations.py @@ -12,6 +12,12 @@ from ssg.constants import puppet_system as puppet_rem_system from ssg.constants import anaconda_system as anaconda_rem_system from ssg.constants import ignition_system as ignition_rem_system +from ssg.constants import oval_namespace, SCE_SYSTEM + +CHECK_SYSTEM_URI_TO_TYPE = { + oval_namespace: "oval", + SCE_SYSTEM: "sce", +} SYSTEM_ATTRIBUTE = { 'bash': bash_rem_system, @@ -307,3 +313,20 @@ def find_fix_in_benchmark(datastream, benchmark_id, rule_id, fix_type='bash', lo fix = rule.find("xccdf-1.2:fix[@system='{0}']".format(system_attribute), PREFIX_TO_NS) return fix + + +def find_checks_in_rule(datastream, benchmark_id, rule_id): + """ + Return check types for given rule from benchmark. + """ + checks = set() + rule = find_rule_in_benchmark(datastream, benchmark_id, rule_id) + if rule is None: + return checks + check_els = rule.findall("xccdf-1.2:check", PREFIX_TO_NS) + for check_el in check_els: + system = check_el.get("system") + check = CHECK_SYSTEM_URI_TO_TYPE.get(system, None) + if check is not None: + checks.add(check) + return checks diff --git a/tests/unit/ssg_test_suite/data/correct.pass.sh b/tests/unit/ssg_test_suite/data/correct.pass.sh index 8e5e284ee21..5a2bc100526 100644 --- a/tests/unit/ssg_test_suite/data/correct.pass.sh +++ b/tests/unit/ssg_test_suite/data/correct.pass.sh @@ -2,6 +2,7 @@ # packages = sudo,authselect # platform = multi_platform_rhel,Fedora # profiles = xccdf_org.ssgproject.content_profile_cis +# check = oval # remediation = none # variables = var_password_pam_remember=5,var_password_pam_remember_control_flag=requisite diff --git a/tests/unit/ssg_test_suite/test_rule.py b/tests/unit/ssg_test_suite/test_rule.py index aad635a1974..cecb241ae40 100644 --- a/tests/unit/ssg_test_suite/test_rule.py +++ b/tests/unit/ssg_test_suite/test_rule.py @@ -25,6 +25,8 @@ def test_scenario(): assert "xccdf_org.ssgproject.content_profile_cis" in \ s.script_params["profiles"] assert OSCAP_PROFILE_ALL_ID not in s.script_params["profiles"] + assert len(s.script_params["check"]) == 1 + assert "oval" in s.script_params["check"] assert len(s.script_params["remediation"]) == 1 assert "none" in s.script_params["remediation"] assert len(s.script_params["variables"]) == 2 @@ -38,6 +40,10 @@ def test_scenario(): assert not s.matches_regex(r"^wrong") assert s.matches_platform({"cpe:/o:redhat:enterprise_linux:7"}) assert not s.matches_platform({"cpe:/o:debian:debian:8"}) + assert s.matches_check({"oval"}) + assert not s.matches_check({"sce"}) + assert not s.matches_check({"fancy_unsupported_language"}) + assert not s.matches_check({}) def test_scenario_defaults(): file_name = "correct_defaults.pass.sh" @@ -52,6 +58,8 @@ def test_scenario_defaults(): assert len(s.script_params["packages"]) == 0 assert len(s.script_params["platform"]) == 1 assert "multi_platform_all" in s.script_params["platform"] + assert len(s.script_params["check"]) == 1 + assert "any" in s.script_params["check"] assert len(s.script_params["remediation"]) == 1 assert "all" in s.script_params["remediation"] assert len(s.script_params["variables"]) == 0