Blob Blame History Raw
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")