-
Notifications
You must be signed in to change notification settings - Fork 706
/
Copy pathbuild_derivatives.py
203 lines (160 loc) · 7.11 KB
/
build_derivatives.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""
Common functions for enabling derivative products
"""
from __future__ import absolute_import
from __future__ import print_function
import re
from .xml import ElementTree
from .constants import (
standard_profiles,
OSCAP_VENDOR,
cpe_dictionary_namespace,
oval_namespace,
datastream_namespace,
)
from .build_cpe import ProductCPEs, get_linked_cpe_oval_document
from .products import load_product_yaml
def add_cpes(elem, namespace, mapping):
"""
Adds derivative CPEs next to RHEL ones, checks XCCDF elements of given
namespace.
"""
affected = False
for child in list(elem):
affected = affected or add_cpes(child, namespace, mapping)
# precompute this so that we can affect the tree while iterating
children = list(elem.findall(".//{%s}platform" % (namespace)))
for child in children:
idref = child.get("idref")
if idref in mapping:
new_platform = ElementTree.Element("{%s}platform" % (namespace))
new_platform.set("idref", mapping[idref])
# this is done for the newline and indentation
new_platform.tail = child.tail
index = list(elem).index(child)
# insert it right after the respective RHEL CPE
elem.insert(index + 1, new_platform)
affected = True
return affected
def get_cpe_item(product_yaml, cpe_ref, cpe_items_dir):
product_cpes = ProductCPEs()
product_cpes.load_cpes_from_directory_tree(cpe_items_dir, product_yaml)
return product_cpes.get_cpe(cpe_ref)
def add_cpe_item_to_dictionary(
tree_root, product_yaml_path, cpe_ref, id_name, cpe_items_dir
):
cpe_list = tree_root.find(".//{%s}cpe-list" % cpe_dictionary_namespace)
if cpe_list is not None:
product_yaml = load_product_yaml(product_yaml_path)
cpe_item = get_cpe_item(product_yaml, cpe_ref, cpe_items_dir)
cpe_item.content_id = id_name
cpe_item.set_cpe_oval_def_id()
cpe_list.append(
cpe_item.to_xml_element("ssg-%s-cpe-oval.xml" % product_yaml.get("product"))
)
return cpe_item.cpe_oval_short_def_id
return None
def add_element_to(oval_root, tag_name, component_element):
xml_el = oval_root.find(".//{%s}%s" % (oval_namespace, tag_name))
if xml_el is None:
xml_el = ElementTree.Element("{%s}%s" % (oval_namespace, tag_name))
oval_root.append(xml_el)
if xml_el.find("%s[@id='%s']" % (component_element.tag, component_element.get("id"))) is None:
xml_el.append(component_element)
def add_oval_components_to_oval_xml(oval_root, tag_name, component_dict):
for component in component_dict.values():
add_element_to(oval_root, tag_name, component.get_xml_element())
def get_cpe_oval_root(root):
for component_el in root.findall("./{%s}component" % datastream_namespace):
if "cpe-oval" in component_el.get("id", ""):
return component_el
return None
def add_oval_definition_to_cpe_oval(root, unlinked_oval_file_path, oval_def_id):
oval_cpe_root = get_cpe_oval_root(root)
if oval_cpe_root is None:
raise Exception("CPE OVAL is missing in base DS!")
oval_document = get_linked_cpe_oval_document(unlinked_oval_file_path)
references_to_keep = oval_document.get_all_references_of_definition(oval_def_id)
oval_document.keep_referenced_components(references_to_keep)
add_oval_components_to_oval_xml(
oval_cpe_root, "definitions", oval_document.definitions
)
add_oval_components_to_oval_xml(oval_cpe_root, "tests", oval_document.tests)
add_oval_components_to_oval_xml(oval_cpe_root, "objects", oval_document.objects)
add_oval_components_to_oval_xml(oval_cpe_root, "states", oval_document.states)
add_oval_components_to_oval_xml(oval_cpe_root, "variables", oval_document.variables)
def add_notice(benchmark, namespace, notice, warning):
"""
Adds derivative notice as the first notice to given benchmark.
"""
index = -1
prev_element = None
existing_notices = list(benchmark.findall("./{%s}notice" % (namespace)))
if existing_notices:
prev_element = existing_notices[0]
# insert before the first notice
index = list(benchmark).index(prev_element)
else:
existing_descriptions = list(
benchmark.findall("./{%s}description" % (namespace))
)
prev_element = existing_descriptions[-1]
# insert after the last description
index = list(benchmark).index(prev_element) + 1
if index == -1:
raise RuntimeError(
"Can't find existing notices or description in benchmark '%s'." %
(benchmark)
)
elem = ElementTree.Element("{%s}notice" % (namespace))
elem.set("id", warning)
elem.append(notice)
# this is done for the newline and indentation
elem.tail = prev_element.tail
benchmark.insert(index, elem)
return True
def remove_idents(tree_root, namespace, prod="RHEL"):
"""
Remove product identifiers from rules in XML tree
"""
ident_exp = '.*' + prod + '-*'
ref_exp = prod + '-*'
for rule in tree_root.findall(".//{%s}Rule" % (namespace)):
for ident in rule.findall(".//{%s}ident" % (namespace)):
if ident is not None:
if (re.search(r'CCE-*', ident.text) or
re.search(ident_exp, ident.text)):
rule.remove(ident)
for ref in rule.findall(".//{%s}reference" % (namespace)):
if ref.text is not None:
if re.search(ref_exp, ref.text):
rule.remove(ref)
for fix in rule.findall(".//{%s}fix" % (namespace)):
sub_elems = fix.findall(".//{%s}sub" % (namespace))
for sub_elem in sub_elems:
sub_elem.tail = re.sub(r"[\s]+- CCE-.*", "", sub_elem.tail)
sub_elem.tail = re.sub(r"CCE-[0-9]*-[0-9]*", "", sub_elem.tail)
if fix.text is not None:
fix.text = re.sub(r"[\s]+- CCE-.*", "", fix.text)
fix.text = re.sub(r"CCE-[0-9]*-[0-9]*", "", fix.text)
def remove_cce_reference(tree_root, namespace):
"""
Remove CCE identifiers from OVAL checks in XML tree
"""
for definition in tree_root.findall(".//{%s}definition" % (namespace)):
for metadata in definition.findall(".//{%s}metadata" % (namespace)):
for ref in metadata.findall(".//{%s}reference" % (namespace)):
if (re.search(r'CCE-*', ref.get("ref_id"))):
metadata.remove(ref)
def profile_handling(tree_root, namespace):
ns_profiles = []
for i in standard_profiles:
ns_profiles.append("xccdf_%s.content_profile_%s" % (OSCAP_VENDOR, i))
all_profiles = standard_profiles + ns_profiles
for profile in tree_root.findall(".//{%s}Profile" % (namespace)):
if profile.get("id") not in all_profiles:
tree_root.remove(profile)
def replace_platform(tree_root, namespace, product):
for oval in tree_root.findall(".//{%s}oval_definitions" % (namespace)):
for platform in oval.findall(".//{%s}platform" % (namespace)):
platform.text = (platform.text).replace("Red Hat Enterprise Linux", product)