From 844be904d8de624abe9bbe620d7a06417dfff842 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Thu, 27 Aug 2020 13:19:01 +0200
Subject: [PATCH 1/5] Align Ansible task applicability with CPE platform
Adds a when clause to Ansible snippets of rules with Package CPE platform.
If the when clause is added, a fact_packages Task needs to added as
well.
---
ssg/build_remediations.py | 52 ++++++++++++++++++++++++++++++++++++---
1 file changed, 49 insertions(+), 3 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index a9ef3014ac..597aed5889 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -6,8 +6,7 @@
import os.path
import re
import codecs
-from collections import defaultdict, namedtuple
-
+from collections import defaultdict, namedtuple, OrderedDict
import ssg.yaml
from . import build_yaml
@@ -343,10 +342,46 @@ def _get_rule_reference(self, ref_class):
else:
return []
+ def inject_package_facts_task(self, parsed_snippet):
+ """ Injects a package_facts task only if
+ the snippet has a task with a when clause with ansible_facts.packages,
+ and the snippet doesn't already have an package_facts task
+ """
+ has_package_facts_task = False
+ has_ansible_facts_packages_clause = False
+
+ for p_task in parsed_snippet:
+ # We are only interested in the OrderedDicts, which represent Ansible tasks
+ if not isinstance(p_task, dict):
+ continue
+
+ if "package_facts" in p_task:
+ has_package_facts_task = True
+
+ if "ansible_facts.packages" in p_task.get("when", ""):
+ has_ansible_facts_packages_clause = True
+
+ if has_ansible_facts_packages_clause and not has_package_facts_task:
+ facts_task = OrderedDict({'name': 'Gather the package facts',
+ 'package_facts': {'manager': 'auto'}})
+ parsed_snippet.insert(0, facts_task)
+
def update_when_from_rule(self, to_update):
additional_when = ""
- if self.associated_rule.platform == "machine":
+ rule_platform = self.associated_rule.platform
+ if rule_platform == "machine":
additional_when = 'ansible_virtualization_type not in ["docker", "lxc", "openvz"]'
+ elif rule_platform is not None:
+ # Assume any other platform is a Package CPE
+
+ # It doesn't make sense to add a conditional on the task that
+ # gathers data for the conditional
+ if "package_facts" in to_update:
+ return
+
+ additional_when = '"' + rule_platform + '" in ansible_facts.packages'
+ # After adding the conditional, we need to make sure package_facts are collected.
+ # This is done via inject_package_facts_task()
to_update.setdefault("when", "")
new_when = ssg.yaml.update_yaml_list_or_string(to_update["when"], additional_when)
if not new_when:
@@ -355,10 +390,21 @@ def update_when_from_rule(self, to_update):
to_update["when"] = new_when
def update(self, parsed, config):
+ # We split the remediation update in three steps
+
+ # 1. Update the when clause
for p in parsed:
if not isinstance(p, dict):
continue
self.update_when_from_rule(p)
+
+ # 2. Inject any extra task necessary
+ self.inject_package_facts_task(parsed)
+
+ # 3. Add tags to all tasks, including the ones we have injected
+ for p in parsed:
+ if not isinstance(p, dict):
+ continue
self.update_tags_from_config(p, config)
self.update_tags_from_rule(p)
From 60e5723e0e35ec8d79bafdd113f04691e61738e7 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Thu, 27 Aug 2020 17:09:06 +0200
Subject: [PATCH 2/5] Add inherited_platform to Rule
This field is exported to the rule when it is resolved.
---
ssg/build_yaml.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ssg/build_yaml.py b/ssg/build_yaml.py
index 4ba114eee4..fe290ffc05 100644
--- a/ssg/build_yaml.py
+++ b/ssg/build_yaml.py
@@ -832,6 +832,7 @@ class Rule(object):
"conflicts": lambda: list(),
"requires": lambda: list(),
"platform": lambda: None,
+ "inherited_platforms": lambda: list(),
"template": lambda: None,
}
@@ -851,6 +852,7 @@ def __init__(self, id_):
self.requires = []
self.conflicts = []
self.platform = None
+ self.inherited_platforms = [] # platforms inherited from the group
self.template = None
@classmethod
@@ -1293,6 +1295,9 @@ def _process_rules(self):
continue
self.all_rules.add(rule)
self.loaded_group.add_rule(rule)
+
+ rule.inherited_platforms.append(self.loaded_group.platform)
+
if self.resolved_rules_dir:
output_for_rule = os.path.join(
self.resolved_rules_dir, "{id_}.yml".format(id_=rule.id_))
From 3a0bb0d2981670e90a8eaca53b28e1a6f7cc29d6 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Thu, 27 Aug 2020 17:21:35 +0200
Subject: [PATCH 3/5] Add when clauses for inherited platforms too
Consider the Rule's Group platform while including 'when' clauses to
Ansible snippets.
Some rules have two platforms, a machine platform and a package
platform. One of them is represented of the Rule, and the other is
represented in the Rule's Group.
The platforms are organized like this to due limiation in XCCDF,
multiple platforms in a Rule are ORed, not ANDed.
---
ssg/build_remediations.py | 44 ++++++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 17 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 597aed5889..a2a996d0af 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -358,8 +358,13 @@ def inject_package_facts_task(self, parsed_snippet):
if "package_facts" in p_task:
has_package_facts_task = True
- if "ansible_facts.packages" in p_task.get("when", ""):
- has_ansible_facts_packages_clause = True
+ # When clause of the task can be string or a list, lets normalize to list
+ task_when = p_task.get("when", "")
+ if type(task_when) is str:
+ task_when = [ task_when ]
+ for when in task_when:
+ if "ansible_facts.packages" in when:
+ has_ansible_facts_packages_clause = True
if has_ansible_facts_packages_clause and not has_package_facts_task:
facts_task = OrderedDict({'name': 'Gather the package facts',
@@ -367,21 +372,26 @@ def inject_package_facts_task(self, parsed_snippet):
parsed_snippet.insert(0, facts_task)
def update_when_from_rule(self, to_update):
- additional_when = ""
- rule_platform = self.associated_rule.platform
- if rule_platform == "machine":
- additional_when = 'ansible_virtualization_type not in ["docker", "lxc", "openvz"]'
- elif rule_platform is not None:
- # Assume any other platform is a Package CPE
-
- # It doesn't make sense to add a conditional on the task that
- # gathers data for the conditional
- if "package_facts" in to_update:
- return
-
- additional_when = '"' + rule_platform + '" in ansible_facts.packages'
- # After adding the conditional, we need to make sure package_facts are collected.
- # This is done via inject_package_facts_task()
+ additional_when = []
+
+ rule_platforms = set([self.associated_rule.platform] +
+ self.associated_rule.inherited_platforms)
+
+ for platform in rule_platforms:
+ if platform == "machine":
+ additional_when.append('ansible_virtualization_type not in ["docker", "lxc", "openvz"]')
+ elif platform is not None:
+ # Assume any other platform is a Package CPE
+
+ # It doesn't make sense to add a conditional on the task that
+ # gathers data for the conditional
+ if "package_facts" in to_update:
+ continue
+
+ additional_when.append('"' + platform + '" in ansible_facts.packages')
+ # After adding the conditional, we need to make sure package_facts are collected.
+ # This is done via inject_package_facts_task()
+
to_update.setdefault("when", "")
new_when = ssg.yaml.update_yaml_list_or_string(to_update["when"], additional_when)
if not new_when:
From 99c92e39bccc3fcfadca41096e66ca146137b207 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Mon, 31 Aug 2020 16:06:14 +0200
Subject: [PATCH 4/5] Improve inherihted and rule's platforms handling
Add a quick comment too.
---
ssg/build_remediations.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index a2a996d0af..9e622ef740 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -374,8 +374,9 @@ def inject_package_facts_task(self, parsed_snippet):
def update_when_from_rule(self, to_update):
additional_when = []
- rule_platforms = set([self.associated_rule.platform] +
- self.associated_rule.inherited_platforms)
+ # There can be repeated inherited platforms and rule platforms
+ rule_platforms = set(self.associated_rule.inherited_platforms)
+ rule_platforms.add(self.associated_rule.platform)
for platform in rule_platforms:
if platform == "machine":
From 596da9993edfbd244cbaa6d797abbd68b2e82185 Mon Sep 17 00:00:00 2001
From: Watson Sato <wsato@redhat.com>
Date: Mon, 31 Aug 2020 16:10:53 +0200
Subject: [PATCH 5/5] Code style and grammar changes
---
ssg/build_remediations.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ssg/build_remediations.py b/ssg/build_remediations.py
index 9e622ef740..866450dd8c 100644
--- a/ssg/build_remediations.py
+++ b/ssg/build_remediations.py
@@ -345,7 +345,7 @@ def _get_rule_reference(self, ref_class):
def inject_package_facts_task(self, parsed_snippet):
""" Injects a package_facts task only if
the snippet has a task with a when clause with ansible_facts.packages,
- and the snippet doesn't already have an package_facts task
+ and the snippet doesn't already have a package_facts task
"""
has_package_facts_task = False
has_ansible_facts_packages_clause = False
@@ -361,7 +361,7 @@ def inject_package_facts_task(self, parsed_snippet):
# When clause of the task can be string or a list, lets normalize to list
task_when = p_task.get("when", "")
if type(task_when) is str:
- task_when = [ task_when ]
+ task_when = [task_when]
for when in task_when:
if "ansible_facts.packages" in when:
has_ansible_facts_packages_clause = True