Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platforms: prevent building content when version comparison is used and platform provides remediation conditional #10040

12 changes: 9 additions & 3 deletions docs/manual/developer/06_contributing_with_content.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ A rule itself contains these attributes:
means it will be evaluated only if the targeted scan environment is either
bare-metal or virtual
machine. Also, it can restrict applicability on higher software
layers. By setting to `shadow-utils`, the rule will have its
layers. By setting to `package[audit]`, the rule will have its
applicability restricted to only environments which have
`shadow-utils` package installed.
`audit` package installed.

The available options can be found
in the file <product>/cpe/<product>-cpe-dictionary.xml
Expand All @@ -189,6 +189,12 @@ A rule itself contains these attributes:
but only if there is no *shadow-utils* or *ssh* package installed.

Some platforms can also define applicability based on the version of the entity they check.
Please note that this feature is not yet consistently implemented.
Read the following before using it.
If the version specification is used, it is correctly rendered into appropriate OVAL check which is later used when deciding on applicability of a rule.
However, conditionals used to limit applicability of Bash and Ansible remediations are currently not generated correctly in this case; the version specification is not taken into account.
Therefore, the build will be interrupted in case you try to use a platform with version specification which influences Bash or Ansible remediation.

Version specifiers notation loosely follows [PEP440](https://peps.python.org/pep-0440/#version-specifiers),
most notably not supporting *wildcard*, *epoch* and *non-numeric* suffixes.

Expand Down Expand Up @@ -1230,7 +1236,7 @@ included in the dictionary.
Allows the construction of logical expressions involving CPEs.

CPEs can be combined to form an applicability statement.
The `platfrom` property of a rule (or a group) can contain a [Boolean expression](https://booleanpy.readthedocs.io/en/latest/concepts.html),
The `platform` property of a rule (or a group) can contain a [Boolean expression](https://booleanpy.readthedocs.io/en/latest/concepts.html),
that describes the relationship of a set of individual CPEs (symbols), which would later be converted
by the build system into the CPE AL definition in the XCCDF document.

Expand Down
37 changes: 32 additions & 5 deletions ssg/build_remediations.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,45 @@ def get_rule_specific_cpe_platform_names(self):
if p not in inherited_cpe_platform_names}
return rule_specific_cpe_platform_names

def _check_if_platform_uses_version_comparison(self, platform, language):
version_comparison_characters = ("<", ">", "=")
languages_with_not_working_version_comparison = ("ansible", "bash")
if language in languages_with_not_working_version_comparison:
for char in version_comparison_characters:
if char in platform.original_expression:
return True
return False

def _get_stripped_conditional(self, language, platform):
# if a platform uses specification of versions as an applicability criteria
# it is currently correctly used in OVAL checks
# but it is not implemented in conditionals, therefore creating inconsitency
# we prevent building content which uses
# such a platform with a remediation conditional specified
if self._check_if_platform_uses_version_comparison(platform, language):
raise ValueError(
"The platform definition you are trying to use uses version comparison. "
"Remediation conditionals for such platforms are currently not implemented "
"for {0} remediations. {1} can't be used.".format(
language, platform.original_expression))
conditional = platform.get_remediation_conditional(language)
if conditional is not None:
stripped_conditional = conditional.strip()
if stripped_conditional:
return stripped_conditional
return None

def get_stripped_conditionals(self, language, cpe_platform_names, cpe_platforms):
"""
collect conditionals of platforms defined by cpe_platform_names
and strip them of white spaces
"""
stripped_conditionals = []
for p in cpe_platform_names:
conditional = cpe_platforms[p].get_remediation_conditional(language)
if conditional is not None:
stripped_conditional = conditional.strip()
if stripped_conditional:
stripped_conditionals.append(stripped_conditional)
platform = cpe_platforms[p]
maybe_stripped_conditional = self._get_stripped_conditional(language, platform)
if maybe_stripped_conditional is not None:
stripped_conditionals.append(maybe_stripped_conditional)
return stripped_conditionals

def get_rule_specific_conditionals(self, language, cpe_platforms):
Expand Down
1 change: 0 additions & 1 deletion tests/unit/ssg-module/data/package_ntp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ xml_content: <ns0:platform xmlns:ns0="http://cpe.mitre.org/language/2.0" id="pac
operator="AND" negate="false"><ns0:fact-ref name="cpe:/a:ntp" /></ns0:logical-test></ns0:platform>
bash_conditional: ( ( rpm --quiet -q ntp ) )
ansible_conditional: ( ( "ntp" in ansible_facts.packages ) )
definition_location: ''
documentation_complete: true
title: 'NTP Package'
9 changes: 9 additions & 0 deletions tests/unit/ssg-module/data/package_ntp_eq_1_0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: package_ntp_eq_1_0
original_expression: package[ntp]==1.0
xml_content: <ns0:platform xmlns:ns0="http://cpe.mitre.org/language/2.0" id="package_ntp_eq_1_0"><ns0:logical-test
operator="AND" negate="false"><ns0:fact-ref name="cpe:/a:ntp:eq:1.0" /></ns0:logical-test></ns0:platform>
bash_conditional: rpm --quiet -q ntp
ansible_conditional: '"ntp" in ansible_facts.packages'
title: ''
definition_location: /home/vojta/repos/upstream/content/build/rhel7/platforms/package_ntp_eq_1_0.yml
vojtapolasek marked this conversation as resolved.
Show resolved Hide resolved
documentation_complete: true
14 changes: 14 additions & 0 deletions tests/unit/ssg-module/test_build_remediations.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ def cpe_platforms(env_yaml):
platforms[platform.name] = platform
return platforms

@pytest.fixture
def cpe_platforms_with_version_comparison(env_yaml):
platforms = dict()
platform_path = os.path.join(DATADIR, "package_ntp_eq_1_0.yml")
platform = ssg.build_yaml.Platform.from_yaml(platform_path, env_yaml)
platforms[platform.name] = platform
return platforms


def test_is_supported_file_name():
assert sbr.is_supported_filename('bash', 'something.sh')
Expand Down Expand Up @@ -118,3 +126,9 @@ def test_get_rule_dir_remediations():
something_bash = sbr.get_rule_dir_remediations(rule_dir, 'bash', 'something')
assert len(something_bash) == 1
assert something_bash != rhel_bash

def test_deny_to_parse_remediation_if_platform_has_version_comparison(cpe_platforms_with_version_comparison):
remediation_cls = sbr.REMEDIATION_TO_CLASS["bash"]
remediation_obj = remediation_cls(rhel_bash)
with pytest.raises(ValueError):
remediation_obj.get_stripped_conditionals("bash", ["package_ntp_eq_1_0"], cpe_platforms_with_version_comparison)