commit 7a1fe8125480948e4a15db51b723436da6cd3a7a
Author: Gabriel Becker <ggasparb@redhat.com>
Date: Fri Apr 5 09:48:15 2019 +0200
Backport files so machine only tests can run.
diff --git a/example/product.yml b/example/product.yml
new file mode 100644
index 0000000..32538fa
--- /dev/null
+++ b/example/product.yml
@@ -0,0 +1,13 @@
+product: example
+full_name: Example
+type: platform
+
+benchmark_root: "../linux_os/guide"
+
+profiles_root: "./profiles"
+
+pkg_manager: "dnf"
+
+init_system: "systemd"
+
+cpes: []
diff --git a/linux_os/guide/system/auditing/auditd_configure_rules/audit_rules_etc_group_openat.rule b/linux_os/guide/system/auditing/auditd_configure_rules/audit_rules_etc_group_openat.rule
index 61bde4d..355fca0 100644
--- a/linux_os/guide/system/auditing/auditd_configure_rules/audit_rules_etc_group_openat.rule
+++ b/linux_os/guide/system/auditing/auditd_configure_rules/audit_rules_etc_group_openat.rule
@@ -34,3 +34,5 @@ warnings:
have been placed independent of other system calls. Grouping system calls related
to the same event is more efficient. See the following example:
<pre>-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F a2&03 -F path=/etc/group -F auid>=1000 -F auid!=unset -F key=group-modify</pre>
+
+platform: machine
diff --git a/linux_os/guide/system/auditing/group.yml b/linux_os/guide/system/auditing/group.yml
index 5ea9fee..e052243 100644
--- a/linux_os/guide/system/auditing/group.yml
+++ b/linux_os/guide/system/auditing/group.yml
@@ -101,3 +101,5 @@ description: |-
the process, which in this case, is <tt>exe="/usr/sbin/httpd"</tt>.
</li></ul>
</li></ul>
+
+platform: machine
diff --git a/ssg/constants.py b/ssg/constants.py
index 9bef085..da36007 100644
--- a/ssg/constants.py
+++ b/ssg/constants.py
@@ -4,6 +4,10 @@ from __future__ import print_function
import datetime
import os.path
+product_directories = ['debian8', 'fedora', 'ol7', 'opensuse', 'rhel6',
+ 'rhel7', 'sle11', 'sle12', 'ubuntu1404',
+ 'ubuntu1604', 'wrlinux', 'rhel-osp7', 'chromium',
+ 'eap6', 'firefox', 'fuse6', 'jre', 'example']
JINJA_MACROS_BASE_DEFINITIONS = os.path.join(os.path.dirname(os.path.dirname(
__file__)), "shared", "macros.jinja")
@@ -68,6 +72,11 @@ PKG_MANAGER_TO_SYSTEM = {
"apt_get": "dpkg",
}
+PKG_MANAGER_TO_CONFIG_FILE = {
+ "yum": "/etc/yum.conf",
+ "dnf": "/etc/dnf/dnf.conf",
+}
+
RHEL_CENTOS_CPE_MAPPING = {
"cpe:/o:redhat:enterprise_linux:6": "cpe:/o:centos:centos:6",
"cpe:/o:redhat:enterprise_linux:7": "cpe:/o:centos:centos:7",
commit 6c91ac3b8fbeebe7e8eeabddbf0430f66bd59a0e
Author: Gabriel Becker <ggasparb@redhat.com>
Date: Thu Apr 4 17:38:28 2019 +0200
Backport of platform support from https://github.com/ComplianceAsCode/content/pull/3576.
diff --git a/ssg/build_yaml.py b/ssg/build_yaml.py
index ea6ffbe..7520a7c 100644
--- a/ssg/build_yaml.py
+++ b/ssg/build_yaml.py
@@ -6,6 +6,7 @@ import os.path
import datetime
import sys
+from .constants import XCCDF_PLATFORM_TO_CPE
from .checks import is_cce_valid
from .yaml import open_and_expand, open_and_macro_expand
from .utils import required_key
@@ -382,6 +383,7 @@ class Group(object):
self.values = {}
self.groups = {}
self.rules = {}
+ self.platform = None
@staticmethod
def from_yaml(yaml_file, env_yaml=None):
@@ -397,6 +399,7 @@ class Group(object):
group.description = required_key(yaml_contents, "description")
del yaml_contents["description"]
group.warnings = yaml_contents.pop("warnings", [])
+ group.platform = yaml_contents.pop("platform", None)
for warning_list in group.warnings:
if len(warning_list) != 1:
@@ -418,6 +421,14 @@ class Group(object):
add_sub_element(group, 'description', self.description)
add_warning_elements(group, self.warnings)
+ if self.platform:
+ platform_el = ET.SubElement(group, "platform")
+ try:
+ platform_cpe = XCCDF_PLATFORM_TO_CPE[self.platform]
+ except KeyError:
+ raise ValueError("Unsupported platform '%s' in rule '%s'." % (self.platform, self.id_))
+ platform_el.set("idref", platform_cpe)
+
for _value in self.values.values():
group.append(_value.to_xml_element())
for _group in self.groups.values():
@@ -440,11 +451,15 @@ class Group(object):
def add_group(self, group):
if group is None:
return
+ if self.platform and not group.platform:
+ group.platform = self.platform
self.groups[group.id_] = group
def add_rule(self, rule):
if rule is None:
return
+ if self.platform and not rule.platform:
+ rule.platform = self.platform
self.rules[rule.id_] = rule
def __str__(self):
@@ -467,6 +482,7 @@ class Rule(object):
self.ocil = None
self.external_oval = None
self.warnings = []
+ self.platform = None
@staticmethod
def from_yaml(yaml_file, env_yaml=None):
@@ -491,6 +507,7 @@ class Rule(object):
rule.ocil = yaml_contents.pop("ocil", None)
rule.external_oval = yaml_contents.pop("oval_external_content", None)
rule.warnings = yaml_contents.pop("warnings", [])
+ rule.platform = yaml_contents.pop("platform", None)
for warning_list in rule.warnings:
if len(warning_list) != 1:
@@ -594,6 +611,14 @@ class Rule(object):
add_warning_elements(rule, self.warnings)
+ if self.platform:
+ platform_el = ET.SubElement(rule, "platform")
+ try:
+ platform_cpe = XCCDF_PLATFORM_TO_CPE[self.platform]
+ except KeyError:
+ raise ValueError("Unsupported platform '%s' in rule '%s'." % (self.platform, self.id_))
+ platform_el.set("idref", platform_cpe)
+
return rule
def to_file(self, file_name):
@@ -663,6 +688,8 @@ def add_from_directory(action, parent_group, guide_directory, profiles_dir,
profiles_dir, env_yaml, bash_remediation_fns)
if group is not None:
+ if parent_group:
+ parent_group.add_group(group)
for value_yaml in values:
if action == "list-inputs":
print(value_yaml)
@@ -682,9 +709,7 @@ def add_from_directory(action, parent_group, guide_directory, profiles_dir,
rule = Rule.from_yaml(rule_yaml, env_yaml)
group.add_rule(rule)
- if parent_group:
- parent_group.add_group(group)
- else:
+ if not parent_group:
# We are on the top level!
# Lets dump the XCCDF group or benchmark to a file
if action == "build":
diff --git a/ssg/constants.py b/ssg/constants.py
index 54e5d61..9bef085 100644
--- a/ssg/constants.py
+++ b/ssg/constants.py
@@ -194,5 +194,10 @@ OCILREFATTR_TO_TAG = {
"question_ref": "question",
}
+XCCDF_PLATFORM_TO_CPE = {
+ "machine": "cpe:/a:machine",
+ "container": "cpe:/a:container"
+}
+
# Application constants
DEFAULT_UID_MIN = 1000
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5b791a2..ecaa6dc 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -31,3 +31,8 @@ add_test(
NAME "max-path-len"
COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/ensure_paths_are_short.py"
)
+
+add_test(
+ NAME "machine-only-rules"
+ COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/test_machine_only_rules.py" --source_dir "${CMAKE_SOURCE_DIR}" --build_dir "${CMAKE_BINARY_DIR}"
+)
diff --git a/tests/test_machine_only_rules.py b/tests/test_machine_only_rules.py
new file mode 100644
index 0000000..94a2e4e
--- /dev/null
+++ b/tests/test_machine_only_rules.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python3
+
+import os
+import argparse
+import xml.etree.ElementTree as ET
+import sys
+import ssg.constants
+import ssg.yaml
+
+machine_cpe = "cpe:/a:machine"
+
+
+def main():
+ args = parse_command_line_args()
+ for product in ssg.constants.product_directories:
+ product_dir = os.path.join(args.source_dir, product)
+ product_yaml_path = os.path.join(product_dir, "product.yml")
+ product_yaml = ssg.yaml.open_raw(product_yaml_path)
+ guide_dir = os.path.abspath(
+ os.path.join(product_dir, product_yaml['benchmark_root']))
+ if not check_product(args.build_dir, product, guide_dir):
+ sys.exit(1)
+
+
+def check_product(build_dir, product, guide_dir):
+ input_groups, input_rules = scan_rules_groups(guide_dir, False)
+ ds_path = os.path.join(build_dir, "ssg-" + product + "-ds.xml")
+ if not check_ds(ds_path, "groups", input_groups):
+ return False
+ return True
+
+
+def check_ds(ds_path, what, input_elems):
+ try:
+ tree = ET.parse(ds_path)
+ except IOError as e:
+ sys.stderr.write("The product datastream '%s' hasn't been build, "
+ "skipping the test." % (ds_path))
+ return True
+ root = tree.getroot()
+ if what == "groups":
+ replacement = "xccdf_org.ssgproject.content_group_"
+ xpath_query = ".//{%s}Group" % ssg.constants.XCCDF12_NS
+ if what == "rules":
+ replacement = "xccdf_org.ssgproject.content_rule_"
+ xpath_query = ".//{%s}Rule" % ssg.constants.XCCDF12_NS
+ benchmark = root.find(".//{%s}Benchmark" % ssg.constants.XCCDF12_NS)
+ for elem in benchmark.findall(xpath_query):
+ elem_id = elem.get("id")
+ elem_short_id = elem_id.replace(replacement, "")
+ if elem_short_id not in input_elems:
+ continue
+ platforms = elem.findall("{%s}platform" % ssg.constants.XCCDF12_NS)
+ machine_platform = False
+ for p in platforms:
+ idref = p.get("idref")
+ if idref == machine_cpe:
+ machine_platform = True
+ if not machine_platform:
+ sys.stderr.write("%s %s in %s is missing <platform> element" %
+ (what, elem_short_id, ds_path))
+ return False
+ return True
+
+
+def parse_command_line_args():
+ parser = argparse.ArgumentParser(
+ description="Tests if 'machine' CPEs are "
+ "propagated to the built datastream")
+ parser.add_argument("--source_dir", required=True,
+ help="Content source directory path")
+ parser.add_argument("--build_dir", required=True,
+ help="Build directory containing built datastreams")
+ args = parser.parse_args()
+ return args
+
+
+def check_if_machine_only(dirpath, name, is_machine_only_group):
+ if name in os.listdir(dirpath):
+ if is_machine_only_group:
+ return True
+ yml_path = os.path.join(dirpath, name)
+ with open(yml_path, "r") as yml_file:
+ yml_file_contents = yml_file.read()
+ if "platform: machine" in yml_file_contents:
+ return True
+ return False
+
+
+def scan_rules_groups(dirpath, parent_machine_only):
+ groups = set()
+ rules = set()
+ name = os.path.basename(dirpath)
+ is_machine_only = False
+ if check_if_machine_only(dirpath, "group.yml", parent_machine_only):
+ groups.add(name)
+ is_machine_only = True
+ if check_if_machine_only(dirpath, "rule.yml", parent_machine_only):
+ rules.add(name)
+ for dir_item in os.listdir(dirpath):
+ subdir_path = os.path.join(dirpath, dir_item)
+ if os.path.isdir(subdir_path):
+ subdir_groups, subdir_rules = scan_rules_groups(
+ subdir_path, is_machine_only)
+ groups |= subdir_groups
+ rules |= subdir_rules
+ return groups, rules
+
+
+if __name__ == "__main__":
+ main()