1
|
|
|
from __future__ import absolute_import |
2
|
|
|
from __future__ import print_function |
3
|
|
|
import sys |
4
|
|
|
import collections |
5
|
|
|
|
6
|
|
|
|
7
|
|
|
from .constants import oval_namespace, XCCDF11_NS, cce_uri, ocil_cs, ocil_namespace |
8
|
|
|
from .constants import OVAL_TO_XCCDF_DATATYPE_CONSTRAINTS |
9
|
|
|
from .parse_oval import resolve_definition, find_extending_defs, get_container_groups |
10
|
|
|
from .xml import parse_file, map_elements_to_their_ids |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
from .checks import get_content_ref_if_exists_and_not_remote, is_cce_value_valid, is_cce_format_valid |
14
|
|
|
from .utils import SSGError |
15
|
|
|
from .xml import ElementTree as ET |
16
|
|
|
oval_ns = oval_namespace |
17
|
|
|
oval_cs = oval_namespace |
18
|
|
|
|
19
|
|
|
|
20
|
|
|
class FileLinker(object): |
21
|
|
|
""" |
22
|
|
|
Bass class which represents the linking of checks to their identifiers. |
23
|
|
|
""" |
24
|
|
|
|
25
|
|
|
CHECK_SYSTEM = None |
26
|
|
|
CHECK_NAMESPACE = None |
27
|
|
|
|
28
|
|
|
def __init__(self, translator, xccdftree, checks): |
29
|
|
|
self.translator = translator |
30
|
|
|
self.checks_related_to_us = self._get_related_checks(checks) |
31
|
|
|
self.fname = self._get_input_fname() |
32
|
|
|
self.tree = None |
33
|
|
|
self.linked_fname = self.fname.replace("unlinked", "linked") |
34
|
|
|
self.xccdftree = xccdftree |
35
|
|
|
|
36
|
|
|
def _get_related_checks(self, checks): |
37
|
|
|
""" |
38
|
|
|
Returns a list of checks which have the same check system as this |
39
|
|
|
class. |
40
|
|
|
""" |
41
|
|
|
return [ch for ch in checks if ch.get("system") == self.CHECK_SYSTEM] |
42
|
|
|
|
43
|
|
|
def _get_fnames_from_related_checks(self): |
44
|
|
|
""" |
45
|
|
|
Returns a list of filenames from non-remote check content href |
46
|
|
|
attributes. |
47
|
|
|
""" |
48
|
|
|
checkfiles = set() |
49
|
|
|
for check in self.checks_related_to_us: |
50
|
|
|
# Include the file in the particular check system only if it's NOT |
51
|
|
|
# a remotely located file (to allow OVAL checks to reference http:// |
52
|
|
|
# and https:// formatted URLs) |
53
|
|
|
checkcontentref = get_content_ref_if_exists_and_not_remote(check) |
54
|
|
|
if checkcontentref is not None: |
55
|
|
|
checkfiles.add(checkcontentref.get("href")) |
56
|
|
|
return checkfiles |
57
|
|
|
|
58
|
|
|
def _get_input_fname(self): |
59
|
|
|
""" |
60
|
|
|
Returns the input filename referenced from the related check. |
61
|
|
|
|
62
|
|
|
Raises SSGError if there are more than one filenames related to |
63
|
|
|
this check system. |
64
|
|
|
""" |
65
|
|
|
fnames = self._get_fnames_from_related_checks() |
66
|
|
|
if len(fnames) > 1: |
67
|
|
|
msg = ("referencing more than one file per check system " |
68
|
|
|
"is not yet supported by this script.") |
69
|
|
|
raise SSGError(msg) |
70
|
|
|
return fnames.pop() if fnames else None |
71
|
|
|
|
72
|
|
|
def save_linked_tree(self): |
73
|
|
|
""" |
74
|
|
|
Write internal tree to the file in self.linked_fname. |
75
|
|
|
""" |
76
|
|
|
assert self.tree is not None, \ |
77
|
|
|
"There is no tree to save, you have probably skipped the linking phase" |
78
|
|
|
ET.ElementTree(self.tree).write(self.linked_fname) |
79
|
|
|
|
80
|
|
|
def _get_checkid_string(self): |
81
|
|
|
raise NotImplementedError() |
82
|
|
|
|
83
|
|
|
def add_missing_check_exports(self, check, checkcontentref): |
84
|
|
|
pass |
85
|
|
|
|
86
|
|
|
def link_xccdf(self): |
87
|
|
|
for check in self.checks_related_to_us: |
88
|
|
|
checkcontentref = get_content_ref_if_exists_and_not_remote(check) |
89
|
|
|
if checkcontentref is None: |
90
|
|
|
continue |
91
|
|
|
|
92
|
|
|
self.add_missing_check_exports(check, checkcontentref) |
93
|
|
|
|
94
|
|
|
checkexports = check.findall("./{%s}check-export" % XCCDF11_NS) |
95
|
|
|
|
96
|
|
|
self._link_xccdf_checkcontentref(checkcontentref, checkexports) |
97
|
|
|
|
98
|
|
|
def _link_xccdf_checkcontentref(self, checkcontentref, checkexports): |
99
|
|
|
checkid = self.translator.generate_id( |
100
|
|
|
self._get_checkid_string(), checkcontentref.get("name")) |
101
|
|
|
checkcontentref.set("name", checkid) |
102
|
|
|
checkcontentref.set("href", self.linked_fname) |
103
|
|
|
|
104
|
|
|
variable_str = "{%s}variable" % self.CHECK_NAMESPACE |
105
|
|
|
for checkexport in checkexports: |
106
|
|
|
newexportname = self.translator.generate_id( |
107
|
|
|
variable_str, checkexport.get("export-name")) |
108
|
|
|
checkexport.set("export-name", newexportname) |
109
|
|
|
|
110
|
|
|
|
111
|
|
|
class OVALFileLinker(FileLinker): |
112
|
|
|
CHECK_SYSTEM = oval_cs |
113
|
|
|
CHECK_NAMESPACE = oval_ns |
114
|
|
|
|
115
|
|
|
def __init__(self, translator, xccdftree, checks): |
116
|
|
|
super(OVALFileLinker, self).__init__(translator, xccdftree, checks) |
117
|
|
|
self.oval_groups = None |
118
|
|
|
|
119
|
|
|
def _get_checkid_string(self): |
120
|
|
|
return "{%s}definition" % self.CHECK_NAMESPACE |
121
|
|
|
|
122
|
|
|
def link(self): |
123
|
|
|
self.oval_groups = get_container_groups(self.fname) |
124
|
|
|
self.tree = parse_file(self.fname) |
125
|
|
|
try: |
126
|
|
|
self._link_oval_tree() |
127
|
|
|
|
128
|
|
|
# Verify if CCE identifiers present in the XCCDF follow the required form |
129
|
|
|
# (either CCE-XXXX-X, or CCE-XXXXX-X). Drop from XCCDF those who don't follow it |
130
|
|
|
verify_correct_form_of_referenced_cce_identifiers(self.xccdftree) |
131
|
|
|
except SSGError as exc: |
132
|
|
|
raise SSGError( |
133
|
|
|
"Error processing {0}: {1}" |
134
|
|
|
.format(self.fname, str(exc))) |
135
|
|
|
self.tree = self.translator.translate(self.tree, store_defname=True) |
136
|
|
|
|
137
|
|
|
def _link_oval_tree(self): |
138
|
|
|
xccdf_to_cce_id_mapping = create_xccdf_id_to_cce_id_mapping(self.xccdftree) |
139
|
|
|
|
140
|
|
|
indexed_oval_defs = map_elements_to_their_ids( |
141
|
|
|
self.tree, ".//{0}".format(self._get_checkid_string())) |
142
|
|
|
|
143
|
|
|
defs_miss = get_oval_checks_extending_non_existing_checks(self.tree, indexed_oval_defs) |
144
|
|
|
if defs_miss: |
145
|
|
|
msg = ["Following extending definitions are missing:"] |
146
|
|
|
for missing, broken in transpose_dict_with_sets(defs_miss).items(): |
147
|
|
|
broken = [b.get("id") for b in broken] |
148
|
|
|
msg.append("\t'{missing}' needed by: {broken}" |
149
|
|
|
.format(missing=missing, broken=broken)) |
150
|
|
|
raise RuntimeError("\n".join(msg)) |
151
|
|
|
|
152
|
|
|
self._add_cce_id_refs_to_oval_checks(xccdf_to_cce_id_mapping) |
153
|
|
|
|
154
|
|
|
# Verify all by XCCDF referenced (local) OVAL checks are defined in OVAL file |
155
|
|
|
# If not drop the <check-content> OVAL checksystem reference from XCCDF |
156
|
|
|
self._ensure_by_xccdf_referenced_oval_def_is_defined_in_oval_file( |
157
|
|
|
indexed_oval_defs) |
158
|
|
|
|
159
|
|
|
check_and_correct_xccdf_to_oval_data_export_matching_constraints(self.xccdftree, self.tree) |
160
|
|
|
|
161
|
|
|
def _add_cce_id_refs_to_oval_checks(self, idmappingdict): |
162
|
|
|
""" |
163
|
|
|
For each XCCDF rule ID having <ident> CCE set and |
164
|
|
|
having OVAL check implemented (remote OVAL isn't sufficient!) |
165
|
|
|
add a new <reference> element into the OVAL definition having the |
166
|
|
|
following form: |
167
|
|
|
|
168
|
|
|
<reference source="CCE" ref_id="CCE-ID" /> |
169
|
|
|
|
170
|
|
|
where "CCE-ID" is the CCE identifier for that particular rule |
171
|
|
|
retrieved from the XCCDF file |
172
|
|
|
""" |
173
|
|
|
ovalrules = self.tree.findall(".//{0}".format(self._get_checkid_string())) |
174
|
|
|
for rule in ovalrules: |
175
|
|
|
ovalid = rule.get("id") |
176
|
|
|
assert ovalid is not None, \ |
177
|
|
|
"An OVAL rule doesn't have an ID" |
178
|
|
|
|
179
|
|
|
if ovalid not in idmappingdict: |
180
|
|
|
continue |
181
|
|
|
|
182
|
|
|
ovaldesc = rule.find(".//{%s}description" % self.CHECK_NAMESPACE) |
183
|
|
|
assert ovaldesc is not None, \ |
184
|
|
|
"OVAL rule '{0}' doesn't have a description, which is mandatory".format(ovalid) |
185
|
|
|
|
186
|
|
|
xccdfcceid = idmappingdict[ovalid] |
187
|
|
|
if is_cce_format_valid(xccdfcceid) and is_cce_value_valid(xccdfcceid): |
188
|
|
|
# Then append the <reference source="CCE" ref_id="CCE-ID" /> element right |
189
|
|
|
# after <description> element of specific OVAL check |
190
|
|
|
ccerefelem = ET.Element('{%s}reference' % self.CHECK_NAMESPACE, ref_id=xccdfcceid, |
191
|
|
|
source="CCE") |
192
|
|
|
metadata = rule.find(".//{%s}metadata" % self.CHECK_NAMESPACE) |
193
|
|
|
metadata.append(ccerefelem) |
194
|
|
|
|
195
|
|
|
def get_nested_definitions(self, oval_def_id): |
196
|
|
|
processed_def_ids = set() |
197
|
|
|
queue = set([oval_def_id]) |
198
|
|
|
while queue: |
199
|
|
|
def_id = queue.pop() |
200
|
|
|
processed_def_ids.add(def_id) |
201
|
|
|
definition_tree = self.oval_groups["definitions"].get(def_id) |
202
|
|
|
if definition_tree is None: |
203
|
|
|
print("WARNING: Definition '%s' was not found, can't figure " |
204
|
|
|
"out what depends on it." % (def_id), file=sys.stderr) |
205
|
|
|
continue |
206
|
|
|
extensions = find_extending_defs(self.oval_groups, definition_tree) |
207
|
|
|
if not extensions: |
208
|
|
|
continue |
209
|
|
|
queue |= extensions - processed_def_ids |
210
|
|
|
return processed_def_ids |
211
|
|
|
|
212
|
|
|
def add_missing_check_exports(self, check, checkcontentref): |
213
|
|
|
check_name = checkcontentref.get("name") |
214
|
|
|
if check_name is None: |
215
|
|
|
return |
216
|
|
|
oval_def = self.oval_groups["definitions"].get(check_name) |
217
|
|
|
if oval_def is None: |
218
|
|
|
return |
219
|
|
|
all_vars = set() |
220
|
|
|
for def_id in self.get_nested_definitions(check_name): |
221
|
|
|
extended_def = self.oval_groups["definitions"].get(def_id) |
222
|
|
|
if extended_def is None: |
223
|
|
|
print("WARNING: Definition '%s' was not found, can't figure " |
224
|
|
|
"out which variables it needs." % (def_id), file=sys.stderr) |
225
|
|
|
continue |
226
|
|
|
all_vars |= resolve_definition(self.oval_groups, extended_def) |
227
|
|
|
for varname in all_vars: |
228
|
|
|
export = ET.Element("{%s}check-export" % XCCDF11_NS) |
229
|
|
|
export.attrib["export-name"] = varname |
230
|
|
|
export.attrib["value-id"] = varname |
231
|
|
|
check.insert(0, export) |
232
|
|
|
|
233
|
|
|
def _ensure_by_xccdf_referenced_oval_def_is_defined_in_oval_file( |
234
|
|
|
self, indexed_oval_defs): |
235
|
|
|
# Ensure all OVAL checks referenced by XCCDF are implemented in OVAL file |
236
|
|
|
# Drop the reference from XCCDF to OVAL definition if: |
237
|
|
|
# * Particular OVAL definition isn't present in OVAL file, |
238
|
|
|
# * That OVAL definition doesn't constitute a remote OVAL |
239
|
|
|
# (@href of <check-content-ref> doesn't start with 'http' |
240
|
|
|
|
241
|
|
|
for xccdfid, rule in rules_with_ids_generator(self.xccdftree): |
242
|
|
|
# Search OVAL ID in OVAL document |
243
|
|
|
ovalid = indexed_oval_defs.get(xccdfid) |
244
|
|
|
if ovalid is not None: |
245
|
|
|
# The OVAL check was found, we can continue |
246
|
|
|
continue |
247
|
|
|
|
248
|
|
|
for check in rule.findall(".//{%s}check" % (XCCDF11_NS)): |
249
|
|
|
if check.get("system") != oval_cs: |
250
|
|
|
continue |
251
|
|
|
|
252
|
|
|
if get_content_ref_if_exists_and_not_remote(check) is None: |
253
|
|
|
continue |
254
|
|
|
|
255
|
|
|
# For local OVAL drop the reference to OVAL definition from XCCDF document |
256
|
|
|
# in the case: |
257
|
|
|
# * OVAL definition is referenced from XCCDF file, |
258
|
|
|
# * But not defined in OVAL file |
259
|
|
|
rule.remove(check) |
260
|
|
|
|
261
|
|
|
|
262
|
|
|
class OCILFileLinker(FileLinker): |
263
|
|
|
CHECK_SYSTEM = ocil_cs |
264
|
|
|
CHECK_NAMESPACE = ocil_namespace |
265
|
|
|
|
266
|
|
|
def _get_checkid_string(self): |
267
|
|
|
return "{%s}questionnaire" % self.CHECK_NAMESPACE |
268
|
|
|
|
269
|
|
|
def link(self): |
270
|
|
|
self.tree = parse_file(self.fname) |
271
|
|
|
self.tree = self.translator.translate(self.tree, store_defname=True) |
272
|
|
|
|
273
|
|
|
|
274
|
|
|
def _find_identcce(rule): |
275
|
|
|
for ident in rule.findall("./{%s}ident" % XCCDF11_NS): |
276
|
|
|
if ident.get("system") == cce_uri: |
277
|
|
|
return ident |
278
|
|
|
return None |
279
|
|
|
|
280
|
|
|
|
281
|
|
|
def rules_with_ids_generator(xccdftree): |
282
|
|
|
xccdfrules = xccdftree.findall(".//{%s}Rule" % XCCDF11_NS) |
283
|
|
|
for rule in xccdfrules: |
284
|
|
|
xccdfid = rule.get("id") |
285
|
|
|
if xccdfid is None: |
286
|
|
|
continue |
287
|
|
|
yield xccdfid, rule |
288
|
|
|
|
289
|
|
|
|
290
|
|
|
def create_xccdf_id_to_cce_id_mapping(xccdftree): |
291
|
|
|
# |
292
|
|
|
# Create dictionary having form of |
293
|
|
|
# |
294
|
|
|
# 'XCCDF ID' : 'CCE ID' |
295
|
|
|
# |
296
|
|
|
# for each XCCDF rule having <ident system='http://cce.mitre.org'>CCE-ID</ident> |
297
|
|
|
# element set in the XCCDF document |
298
|
|
|
xccdftocce_idmapping = {} |
299
|
|
|
|
300
|
|
|
for xccdfid, rule in rules_with_ids_generator(xccdftree): |
301
|
|
|
identcce = _find_identcce(rule) |
302
|
|
|
if identcce is None: |
303
|
|
|
continue |
304
|
|
|
|
305
|
|
|
xccdftocce_idmapping[xccdfid] = identcce.text |
306
|
|
|
|
307
|
|
|
return xccdftocce_idmapping |
308
|
|
|
|
309
|
|
|
|
310
|
|
|
def get_nonexisting_check_definition_extends(definition, indexed_oval_defs): |
311
|
|
|
# TODO: handle multiple levels of referrals. |
312
|
|
|
# OVAL checks that go beyond one level of extend_definition won't be properly identified |
313
|
|
|
for extdefinition in definition.findall(".//{%s}extend_definition" % oval_ns): |
314
|
|
|
# Verify each extend_definition in the definition |
315
|
|
|
extdefinitionref = extdefinition.get("definition_ref") |
316
|
|
|
|
317
|
|
|
# Search the OVAL tree for a definition with the referred ID |
318
|
|
|
referreddefinition = indexed_oval_defs.get(extdefinitionref) |
319
|
|
|
|
320
|
|
|
if referreddefinition is None: |
321
|
|
|
# There is no oval satisfying the extend_definition referal |
322
|
|
|
return extdefinitionref |
323
|
|
|
return None |
324
|
|
|
|
325
|
|
|
|
326
|
|
|
def get_oval_checks_extending_non_existing_checks(ovaltree, indexed_oval_defs): |
327
|
|
|
# Incomplete OVAL checks are as useful as non existing checks |
328
|
|
|
# Here we check if all extend_definition refs from a definition exists in local OVAL file |
329
|
|
|
definitions = ovaltree.find(".//{%s}definitions" % oval_ns) |
330
|
|
|
definitions_misses = collections.defaultdict(set) |
331
|
|
|
for definition in definitions: |
332
|
|
|
nonexisting_ref = get_nonexisting_check_definition_extends(definition, indexed_oval_defs) |
333
|
|
|
if nonexisting_ref is not None: |
334
|
|
|
definitions_misses[definition].add(nonexisting_ref) |
335
|
|
|
|
336
|
|
|
return definitions_misses |
337
|
|
|
|
338
|
|
|
|
339
|
|
|
def transpose_dict_with_sets(dict_in): |
340
|
|
|
""" |
341
|
|
|
Given a mapping X: key -> set of values, produce a mapping Y of the same type, where |
342
|
|
|
for every combination of a, b for which a in X[b], the following holds: b in Y[a]. |
343
|
|
|
""" |
344
|
|
|
result = collections.defaultdict(set) |
345
|
|
|
for key, values in dict_in.items(): |
346
|
|
|
for val in values: |
347
|
|
|
result[val].add(key) |
348
|
|
|
return result |
349
|
|
|
|
350
|
|
|
|
351
|
|
|
def drop_oval_definitions(ovaltree, defstoremove, oval_groups, indexed_oval_defs): |
352
|
|
|
definitions = ovaltree.find(".//{%s}definitions" % oval_ns) |
353
|
|
|
for definition in defstoremove: |
354
|
|
|
del oval_groups["definitions"][definition.get("id")] |
355
|
|
|
del indexed_oval_defs[definition.get("id")] |
356
|
|
|
definitions.remove(definition) |
357
|
|
|
|
358
|
|
|
|
359
|
|
|
def check_and_correct_xccdf_to_oval_data_export_matching_constraints(xccdftree, ovaltree): |
360
|
|
|
""" |
361
|
|
|
Verify if <xccdf:Value> 'type' to corresponding OVAL variable |
362
|
|
|
'datatype' export matching constraint: |
363
|
|
|
|
364
|
|
|
http://csrc.nist.gov/publications/nistpubs/800-126-rev2/SP800-126r2.pdf#page=30&zoom=auto,69,313 |
365
|
|
|
|
366
|
|
|
is met. Also correct the 'type' attribute of those <xccdf:Value> elements where necessary |
367
|
|
|
in order the produced content to meet this constraint. |
368
|
|
|
|
369
|
|
|
To correct the constraint we use simpler approach - prefer to fix |
370
|
|
|
'type' attribute of <xccdf:Value> rather than 'datatype' attribute |
371
|
|
|
of the corresponding OVAL variable since there might be additional |
372
|
|
|
OVAL variables, derived from the affected OVAL variable, and in that |
373
|
|
|
case we would need to fix the 'datatype' attribute in each of them. |
374
|
|
|
|
375
|
|
|
Define the <xccdf:Value> 'type' to OVAL variable 'datatype' export matching |
376
|
|
|
constraints mapping as specified in Table 16 of XCCDF v1.2 standard: |
377
|
|
|
|
378
|
|
|
http://csrc.nist.gov/publications/nistpubs/800-126-rev2/SP800-126r2.pdf#page=30&zoom=auto,69,313 |
379
|
|
|
""" |
380
|
|
|
indexed_xccdf_values = map_elements_to_their_ids( |
381
|
|
|
xccdftree, ".//{%s}Value" % (XCCDF11_NS)) |
382
|
|
|
|
383
|
|
|
# Loop through all <external_variables> in the OVAL document |
384
|
|
|
ovalextvars = ovaltree.findall(".//{%s}external_variable" % oval_ns) |
385
|
|
|
if ovalextvars is None: |
386
|
|
|
return |
387
|
|
|
|
388
|
|
|
for ovalextvar in ovalextvars: |
389
|
|
|
# Verify the found external variable has both 'id' and 'datatype' set |
390
|
|
|
if 'id' not in ovalextvar.attrib or 'datatype' not in ovalextvar.attrib: |
391
|
|
|
msg = "Invalid OVAL <external_variable> found - either without 'id' or 'datatype'." |
392
|
|
|
raise SSGError(msg) |
393
|
|
|
|
394
|
|
|
ovalvarid = ovalextvar.get('id') |
395
|
|
|
ovalvartype = ovalextvar.get('datatype') |
396
|
|
|
|
397
|
|
|
# Locate the corresponding <xccdf:Value> with the same ID in the XCCDF |
398
|
|
|
xccdfvar = indexed_xccdf_values.get(ovalvarid) |
399
|
|
|
|
400
|
|
|
if xccdfvar is None: |
401
|
|
|
return |
402
|
|
|
|
403
|
|
|
xccdfvartype = xccdfvar.get('type') |
404
|
|
|
# Verify the found value has 'type' attribute set |
405
|
|
|
if xccdfvartype is None: |
406
|
|
|
msg = ( |
407
|
|
|
"Invalid XCCDF variable '{0}': Missing the 'type' attribute." |
408
|
|
|
.format(xccdfvar.attrib("id"))) |
409
|
|
|
raise SSGError(msg) |
410
|
|
|
|
411
|
|
|
# This is the required XCCDF 'type' for <xccdf:Value> derived |
412
|
|
|
# from OVAL variable 'datatype' and mapping above |
413
|
|
|
reqxccdftype = OVAL_TO_XCCDF_DATATYPE_CONSTRAINTS[ovalvartype] |
414
|
|
|
# Compare the actual value of 'type' of <xccdf:Value> with the requirement |
415
|
|
|
if xccdfvartype != reqxccdftype: |
416
|
|
|
# If discrepancy is found, issue a warning |
417
|
|
|
sys.stderr.write( |
418
|
|
|
"Warning: XCCDF 'type' of \"%s\" value does " |
419
|
|
|
"not meet the XCCDF value 'type' to OVAL " |
420
|
|
|
"variable 'datatype' export matching " |
421
|
|
|
"constraint! Got: \"%s\", Expected: \"%s\". " |
422
|
|
|
"Resetting it! Set 'type' of \"%s\" " |
423
|
|
|
"<xccdf:value> to '%s' directly in the XCCDF " |
424
|
|
|
"content to dismiss this warning!\n" % |
425
|
|
|
(ovalvarid, xccdfvartype, reqxccdftype, |
426
|
|
|
ovalvarid, reqxccdftype) |
427
|
|
|
) |
428
|
|
|
# And reset the 'type' attribute of such a <xccdf:Value> to the required type |
429
|
|
|
xccdfvar.attrib['type'] = reqxccdftype |
430
|
|
|
|
431
|
|
|
|
432
|
|
|
def verify_correct_form_of_referenced_cce_identifiers(xccdftree): |
433
|
|
|
""" |
434
|
|
|
In SSG benchmarks, the CCEs till unassigned have the form of e.g. "RHEL7-CCE-TBD" |
435
|
|
|
(or any other format possibly not matching the above two requirements) |
436
|
|
|
|
437
|
|
|
If this is the case for specific SSG product, drop such CCE identifiers from the XCCDF |
438
|
|
|
since they are in invalid format! |
439
|
|
|
""" |
440
|
|
|
xccdfrules = xccdftree.findall(".//{%s}Rule" % XCCDF11_NS) |
441
|
|
|
for rule in xccdfrules: |
442
|
|
|
identcce = _find_identcce(rule) |
443
|
|
|
if identcce is not None: |
444
|
|
|
cceid = identcce.text |
445
|
|
|
if not is_cce_format_valid(cceid): |
446
|
|
|
print("Warning: CCE '{0}' is invalid for rule '{1}'. Removing CCE..." |
447
|
|
|
.format(cceid, rule.get("id"), file=sys.stderr)) |
448
|
|
|
rule.remove(identcce) |
449
|
|
|
sys.exit(1) |
450
|
|
|
|
451
|
|
|
|
452
|
|
|
def assert_that_check_ids_match_rule_id(checks, xccdf_rule): |
453
|
|
|
for check in checks: |
454
|
|
|
check_name = check.get("name") |
455
|
|
|
# Verify match of XCCDF vs OVAL / OCIL IDs for |
456
|
|
|
# * the case of OVAL <check> |
457
|
|
|
# * the case of OCIL <check> |
458
|
|
|
if (xccdf_rule != check_name and check_name is not None and |
459
|
|
|
xccdf_rule + '_ocil' != check_name and |
460
|
|
|
xccdf_rule != 'sample_rule'): |
461
|
|
|
msg_lines = ["The OVAL / OCIL ID does not match the XCCDF Rule ID!"] |
462
|
|
|
if '_ocil' in check_name: |
463
|
|
|
id_name = "OCIL ID" |
464
|
|
|
else: |
465
|
|
|
id_name = "OVAL ID" |
466
|
|
|
msg_lines.append(" {0:>14}: {1}".format(id_name, check_name)) |
467
|
|
|
msg_lines.append(" {0:>14}: {1}".format("XCCDF Rule ID", xccdf_rule)) |
468
|
|
|
raise SSGError("\n".join(msg_lines)) |
469
|
|
|
|
470
|
|
|
|
471
|
|
|
def check_that_oval_and_rule_id_match(xccdftree): |
472
|
|
|
for xccdfid, rule in rules_with_ids_generator(xccdftree): |
473
|
|
|
checks = rule.find("./{%s}check" % XCCDF11_NS) |
474
|
|
|
if checks is None: |
475
|
|
|
print("Rule {0} doesn't have checks." |
476
|
|
|
.format(xccdfid), file=sys.stderr) |
477
|
|
|
continue |
478
|
|
|
|
479
|
|
|
assert_that_check_ids_match_rule_id(checks, xccdfid) |
480
|
|
|
|