From c05cce1a4a5eb95be857b07948fda0c95cdaa106 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Tue, 8 Sep 2020 14:36:07 +0200
Subject: [PATCH 1/5] Align Bash applicability with CPE platform
Wraps the remediation of rules with Packager CPE Platform
with an if condition that checks for the respective
platforms's package.
---
ssg/build_remediations.py | 45 +++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index ccbdf9fc1f..2d4a805e78 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -27,6 +27,13 @@
'kubernetes': '.yml'
}
+PKG_MANAGER_TO_PACKAGE_CHECK_COMMAND = {
+ 'apt_get': 'dpkg-query -s {} &>/dev/null',
+ 'dnf': 'rpm --quiet -q {}',
+ 'yum': 'rpm --quiet -q {}',
+ 'zypper': 'rpm --quiet -q {}',
+}
+
FILE_GENERATED_HASH_COMMENT = '# THIS FILE IS GENERATED'
REMEDIATION_CONFIG_KEYS = ['complexity', 'disruption', 'platform', 'reboot',
@@ -262,6 +269,44 @@ class BashRemediation(Remediation):
def __init__(self, file_path):
super(BashRemediation, self).__init__(file_path, "bash")
+ def parse_from_file_with_jinja(self, env_yaml):
+ self.local_env_yaml.update(env_yaml)
+ result = super(BashRemediation, self).parse_from_file_with_jinja(self.local_env_yaml)
+
+ # There can be repeated inherited platforms and rule platforms
+ rule_platforms = set(self.associated_rule.inherited_platforms)
+ rule_platforms.add(self.associated_rule.platform)
+
+ platform_conditionals = []
+ for platform in rule_platforms:
+ if platform == "machine":
+ # Based on check installed_env_is_a_container
+ platform_conditionals.append('[ ! -f /.dockerenv -a ! -f /run/.containerenv ]')
+ elif platform is not None:
+ # Assume any other platform is a Package CPE
+
+ # Some package names are different from the platform names
+ if platform in self.local_env_yaml["platform_package_overrides"]:
+ platform = self.local_env_yaml["platform_package_overrides"].get(platform)
+
+ # Adjust package check command according to the pkg_manager
+ pkg_manager = self.local_env_yaml["pkg_manager"]
+ pkg_check_command = PKG_MANAGER_TO_PACKAGE_CHECK_COMMAND[pkg_manager]
+ platform_conditionals.append(pkg_check_command.format(platform))
+
+ if platform_conditionals:
+ platform_fix_text = "# Remediation is applicable only in certain platforms\n"
+
+ cond = platform_conditionals.pop(0)
+ platform_fix_text += "if {}".format(cond)
+ for cond in platform_conditionals:
+ platform_fix_text += " && {}".format(cond)
+ platform_fix_text += '; then\n{}\nelse\necho "Remediation is not applicable, nothing was done"\nfi'.format(result.contents)
+
+ remediation = namedtuple('remediation', ['contents', 'config'])
+ result = remediation(contents=platform_fix_text, config=result.config)
+
+ return result
class AnsibleRemediation(Remediation):
def __init__(self, file_path):
From 19e0c3b709e091159655d37b8ce5d693750f0a81 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Tue, 8 Sep 2020 14:41:01 +0200
Subject: [PATCH 2/5] Handle Bash platform wrapping in xccdf expansion
Adjust expansion of subs and variables not to remove the whole beginning
of the fix test. This was removing the package conditional wrapping.
---
ssg/build_remediations.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 2d4a805e78..49ec557000 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -736,14 +736,16 @@ def expand_xccdf_subs(fix, remediation_type, remediation_functions):
patcomp = re.compile(pattern, re.DOTALL)
fixparts = re.split(patcomp, fix.text)
if fixparts[0] is not None:
- # Split the portion of fix.text from fix start to first call of
- # remediation function, keeping only the third part:
- # * tail to hold part of the fix.text after inclusion,
- # but before first call of remediation function
+ # Split the portion of fix.text at the string remediation_functions,
+ # and remove preceeding comment whenever it is there.
+ # * head holds part of the fix.text before
+ # remediation_functions string
+ # * tail holds part of the fix.text after the
+ # remediation_functions string
try:
- rfpattern = '(.*remediation_functions)(.*)'
- rfpatcomp = re.compile(rfpattern, re.DOTALL)
- _, _, tail, _ = re.split(rfpatcomp, fixparts[0], maxsplit=2)
+ rfpattern = r'((?:# Include source function library\.\n)?.*remediation_functions)'
+ rfpatcomp = re.compile(rfpattern)
+ head, _, tail = re.split(rfpatcomp, fixparts[0], maxsplit=1)
except ValueError:
sys.stderr.write("Processing fix.text for: %s rule\n"
% fix.get('rule'))
@@ -751,9 +753,10 @@ def expand_xccdf_subs(fix, remediation_type, remediation_functions):
"after inclusion of remediation functions."
" Aborting..\n")
sys.exit(1)
- # If the 'tail' is not empty, make it new fix.text.
+ # If the 'head' is not empty, make it new fix.text.
# Otherwise use ''
- fix.text = tail if tail is not None else ''
+ fix.text = head if head is not None else ''
+ fix.text += tail if tail is not None else ''
# Drop the first element of 'fixparts' since it has been processed
fixparts.pop(0)
# Perform sanity check on new 'fixparts' list content (to continue
From 1292b93dc35a9a308464f1effb7f10f8de6db457 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Tue, 8 Sep 2020 20:56:17 +0200
Subject: [PATCH 3/5] Check if remediation has associated rule before use
---
ssg/build_remediations.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 49ec557000..85f7139d8f 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -273,9 +273,11 @@ def parse_from_file_with_jinja(self, env_yaml):
self.local_env_yaml.update(env_yaml)
result = super(BashRemediation, self).parse_from_file_with_jinja(self.local_env_yaml)
- # There can be repeated inherited platforms and rule platforms
- rule_platforms = set(self.associated_rule.inherited_platforms)
- rule_platforms.add(self.associated_rule.platform)
+ rule_platforms = set()
+ if self.associated_rule:
+ # There can be repeated inherited platforms and rule platforms
+ rule_platforms.update(self.associated_rule.inherited_platforms)
+ rule_platforms.add(self.associated_rule.platform)
platform_conditionals = []
for platform in rule_platforms:
From 7953a02e61bb56b501c56f46972247751292dcbb Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Thu, 10 Sep 2020 10:59:43 +0200
Subject: [PATCH 4/5] Fix python2 compat and improve code readability
---
ssg/build_remediations.py | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 85f7139d8f..673d6d0cc6 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -28,10 +28,10 @@
}
PKG_MANAGER_TO_PACKAGE_CHECK_COMMAND = {
- 'apt_get': 'dpkg-query -s {} &>/dev/null',
- 'dnf': 'rpm --quiet -q {}',
- 'yum': 'rpm --quiet -q {}',
- 'zypper': 'rpm --quiet -q {}',
+ 'apt_get': 'dpkg-query -s {0} &>/dev/null',
+ 'dnf': 'rpm --quiet -q {0}',
+ 'yum': 'rpm --quiet -q {0}',
+ 'zypper': 'rpm --quiet -q {0}',
}
FILE_GENERATED_HASH_COMMENT = '# THIS FILE IS GENERATED'
@@ -297,16 +297,23 @@ def parse_from_file_with_jinja(self, env_yaml):
platform_conditionals.append(pkg_check_command.format(platform))
if platform_conditionals:
- platform_fix_text = "# Remediation is applicable only in certain platforms\n"
+ wrapped_fix_text = ["# Remediation is applicable only in certain platforms"]
- cond = platform_conditionals.pop(0)
- platform_fix_text += "if {}".format(cond)
- for cond in platform_conditionals:
- platform_fix_text += " && {}".format(cond)
- platform_fix_text += '; then\n{}\nelse\necho "Remediation is not applicable, nothing was done"\nfi'.format(result.contents)
+ all_conditions = " && ".join(platform_conditionals)
+ wrapped_fix_text.append("if {0}; then".format(all_conditions))
+
+ # Avoid adding extra blank line
+ if not result.contents.startswith("\n"):
+ wrapped_fix_text.append("")
+
+ wrapped_fix_text.append("{0}".format(result.contents))
+ wrapped_fix_text.append("")
+ wrapped_fix_text.append("else")
+ wrapped_fix_text.append(" >&2 echo 'Remediation is not applicable, nothing was done'")
+ wrapped_fix_text.append("fi")
remediation = namedtuple('remediation', ['contents', 'config'])
- result = remediation(contents=platform_fix_text, config=result.config)
+ result = remediation(contents="\n".join(wrapped_fix_text), config=result.config)
return result
From 0bd3912651367c64789bb3d67b44c3b8848708c0 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Thu, 10 Sep 2020 17:25:27 +0200
Subject: [PATCH 5/5] Document the perils of indenting wrapped Bash fixes
---
ssg/build_remediations.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 673d6d0cc6..f269d4d2d6 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -306,6 +306,9 @@ def parse_from_file_with_jinja(self, env_yaml):
if not result.contents.startswith("\n"):
wrapped_fix_text.append("")
+ # It is possible to indent the original body of the remediation with textwrap.indent(),
+ # however, it is not supported by python2, and there is a risk of breaking remediations
+ # For example, remediations with a here-doc block could be affected.
wrapped_fix_text.append("{0}".format(result.contents))
wrapped_fix_text.append("")
wrapped_fix_text.append("else")