1
|
|
|
from __future__ import absolute_import |
2
|
|
|
from __future__ import print_function |
3
|
|
|
|
4
|
|
|
import os |
5
|
|
|
import os.path |
6
|
|
|
import sys |
7
|
|
|
import re |
8
|
|
|
from copy import deepcopy |
9
|
|
|
import collections |
10
|
|
|
|
11
|
|
|
from .build_yaml import Rule, DocumentationNotComplete |
12
|
|
|
from .constants import oval_namespace as oval_ns |
13
|
|
|
from .constants import oval_footer |
14
|
|
|
from .constants import oval_header |
15
|
|
|
from .constants import MULTI_PLATFORM_LIST |
16
|
|
|
from .id_translate import IDTranslator |
17
|
|
|
from .jinja import process_file_with_macros |
18
|
|
|
from .rule_yaml import parse_prodtype |
19
|
|
|
from .rules import get_rule_dir_id, get_rule_dir_ovals, find_rule_dirs_in_paths |
20
|
|
|
from . import utils, products |
21
|
|
|
from .utils import mkdir_p |
22
|
|
|
from .xml import ElementTree, oval_generated_header |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
def _create_subtree(shorthand_tree, category): |
26
|
|
|
parent_tag = "{%s}%ss" % (oval_ns, category) |
27
|
|
|
parent = ElementTree.Element(parent_tag) |
28
|
|
|
for node in shorthand_tree.findall(".//{%s}def-group/*" % oval_ns): |
29
|
|
|
if node.tag is ElementTree.Comment: |
30
|
|
|
continue |
31
|
|
|
elif node.tag.endswith(category): |
32
|
|
|
append(parent, node) |
33
|
|
|
return parent |
34
|
|
|
|
35
|
|
|
|
36
|
|
|
def expand_shorthand(shorthand_path, oval_path, env_yaml): |
37
|
|
|
shorthand_file_content = process_file_with_macros(shorthand_path, env_yaml) |
38
|
|
|
wrapped_shorthand = (oval_header + shorthand_file_content + oval_footer) |
39
|
|
|
shorthand_tree = ElementTree.fromstring(wrapped_shorthand.encode("utf-8")) |
40
|
|
|
header = oval_generated_header("test", "5.11", "1.0") |
41
|
|
|
skeleton = header + oval_footer |
42
|
|
|
root = ElementTree.fromstring(skeleton.encode("utf-8")) |
43
|
|
|
for category in ["definition", "test", "object", "state", "variable"]: |
44
|
|
|
subtree = _create_subtree(shorthand_tree, category) |
45
|
|
|
if list(subtree): |
46
|
|
|
root.append(subtree) |
47
|
|
|
id_translator = IDTranslator("test") |
48
|
|
|
root_translated = id_translator.translate(root) |
49
|
|
|
|
50
|
|
|
ElementTree.ElementTree(root_translated).write(oval_path) |
51
|
|
|
|
52
|
|
|
|
53
|
|
|
def _check_is_applicable_for_product(oval_check_def, product): |
54
|
|
|
"""Based on the <platform> specifier of the OVAL check determine if this |
55
|
|
|
OVAL check is applicable for this product. Return 'True' if so, 'False' |
56
|
|
|
otherwise""" |
57
|
|
|
|
58
|
|
|
product, product_version = utils.parse_name(product) |
59
|
|
|
|
60
|
|
|
# Define general platforms |
61
|
|
|
multi_platforms = ['<platform>multi_platform_all', |
62
|
|
|
'<platform>multi_platform_' + product] |
63
|
|
|
|
64
|
|
|
# First test if OVAL check isn't for 'multi_platform_all' or |
65
|
|
|
# 'multi_platform_' + product |
66
|
|
|
for multi_prod in multi_platforms: |
67
|
|
|
if multi_prod in oval_check_def and product in MULTI_PLATFORM_LIST: |
68
|
|
|
return True |
69
|
|
|
|
70
|
|
|
# Current SSG checks aren't unified which element of '<platform>' |
71
|
|
|
# and '<product>' to use as OVAL AffectedType metadata element, |
72
|
|
|
# e.g. Chromium content uses both of them across the various checks |
73
|
|
|
# Thus for now check both of them when checking concrete platform / product |
74
|
|
|
affected_type_elements = ['<platform>', '<product>'] |
75
|
|
|
|
76
|
|
|
for afftype in affected_type_elements: |
77
|
|
|
# Get official name for product (prefixed with content of afftype) |
78
|
|
|
product_name = afftype + utils.map_name(product) |
79
|
|
|
# Append the product version to the official name |
80
|
|
|
if product_version is not None: |
81
|
|
|
# Some product versions have a dot in between the numbers |
82
|
|
|
# While the prodtype doesn't have the dot, the full product name does |
83
|
|
|
if product == "ubuntu" or product == "macos": |
84
|
|
|
product_version = product_version[:2] + "." + product_version[2:] |
85
|
|
|
product_name += ' ' + product_version |
86
|
|
|
|
87
|
|
|
# Test if this OVAL check is for the concrete product version |
88
|
|
|
if product_name in oval_check_def: |
89
|
|
|
return True |
90
|
|
|
|
91
|
|
|
# OVAL check isn't neither a multi platform one, nor isn't applicable |
92
|
|
|
# for this product => return False to indicate that |
93
|
|
|
|
94
|
|
|
return False |
95
|
|
|
|
96
|
|
|
|
97
|
|
|
def finalize_affected_platforms(xml_tree, env_yaml): |
98
|
|
|
"""Depending on your use-case of OVAL you may not need the <affected> |
99
|
|
|
element. Such use-cases including using OVAL as a check engine for XCCDF |
100
|
|
|
benchmarks. Since the XCCDF Benchmarks use cpe:platform with CPE IDs, |
101
|
|
|
the affected element in OVAL definitions is redundant and just bloats the |
102
|
|
|
files. This function removes all *irrelevant* affected platform elements |
103
|
|
|
from given OVAL tree. It then adds one platform of the product we are |
104
|
|
|
building. |
105
|
|
|
""" |
106
|
|
|
|
107
|
|
|
for affected in xml_tree.findall(".//{%s}affected" % (oval_ns)): |
108
|
|
|
for platform in affected.findall("./{%s}platform" % (oval_ns)): |
109
|
|
|
affected.remove(platform) |
110
|
|
|
for product in affected.findall("./{%s}product" % (oval_ns)): |
111
|
|
|
affected.remove(product) |
112
|
|
|
|
113
|
|
|
final = ElementTree.SubElement( |
114
|
|
|
affected, "{%s}%s" % (oval_ns, utils.required_key(env_yaml, "type"))) |
115
|
|
|
final.text = utils.required_key(env_yaml, "full_name") |
116
|
|
|
|
117
|
|
|
return xml_tree |
118
|
|
|
|
119
|
|
|
|
120
|
|
|
def oval_entities_are_identical(firstelem, secondelem): |
121
|
|
|
"""Check if OVAL entities represented by XML elements are identical |
122
|
|
|
Return: True if identical, False otherwise |
123
|
|
|
Based on: http://stackoverflow.com/a/24349916""" |
124
|
|
|
|
125
|
|
|
# Per https://github.com/ComplianceAsCode/content/pull/1343#issuecomment-234541909 |
126
|
|
|
# and https://github.com/ComplianceAsCode/content/pull/1343#issuecomment-234545296 |
127
|
|
|
# ignore the differences in 'comment', 'version', 'state_operator', and |
128
|
|
|
# 'deprecated' attributes. Also ignore different nsmap, since all these |
129
|
|
|
# don't affect the semantics of the OVAL entities |
130
|
|
|
|
131
|
|
|
# Operate on copies of the elements (since we will modify |
132
|
|
|
# some attributes). Deepcopy will also reset the namespace map |
133
|
|
|
# on copied elements for us |
134
|
|
|
firstcopy = deepcopy(firstelem) |
135
|
|
|
secondcopy = deepcopy(secondelem) |
136
|
|
|
|
137
|
|
|
# Ignore 'comment', 'version', 'state_operator', and 'deprecated' |
138
|
|
|
# attributes since they don't change the semantics of an element |
139
|
|
|
for copy in [firstcopy, secondcopy]: |
140
|
|
|
for key in copy.keys(): |
141
|
|
|
if key in ["comment", "version", "state_operator", |
142
|
|
|
"deprecated"]: |
143
|
|
|
del copy.attrib[key] |
144
|
|
|
|
145
|
|
|
# Compare the equality of the copies |
146
|
|
|
if firstcopy.tag != secondcopy.tag: |
147
|
|
|
return False |
148
|
|
|
if firstcopy.text != secondcopy.text: |
149
|
|
|
return False |
150
|
|
|
if firstcopy.tail != secondcopy.tail: |
151
|
|
|
return False |
152
|
|
|
if firstcopy.attrib != secondcopy.attrib: |
153
|
|
|
return False |
154
|
|
|
if len(firstcopy) != len(secondcopy): |
155
|
|
|
return False |
156
|
|
|
|
157
|
|
|
return all(oval_entities_are_identical( |
158
|
|
|
fchild, schild) for fchild, schild in zip(firstcopy, secondcopy)) |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
def oval_entity_is_extvar(elem): |
162
|
|
|
"""Check if OVAL entity represented by XML element is OVAL |
163
|
|
|
<external_variable> element |
164
|
|
|
Return: True if <external_variable>, False otherwise""" |
165
|
|
|
|
166
|
|
|
return elem.tag == '{%s}external_variable' % oval_ns |
167
|
|
|
|
168
|
|
|
|
169
|
|
|
element_child_cache = collections.defaultdict(dict) |
170
|
|
|
|
171
|
|
|
|
172
|
|
|
def append(element, newchild): |
173
|
|
|
"""Append new child ONLY if it's not a duplicate""" |
174
|
|
|
|
175
|
|
|
global element_child_cache |
176
|
|
|
|
177
|
|
|
newid = newchild.get("id") |
178
|
|
|
existing = element_child_cache[element].get(newid, None) |
179
|
|
|
|
180
|
|
|
if existing is not None: |
181
|
|
|
# ID is identical and OVAL entities are identical |
182
|
|
|
if oval_entities_are_identical(existing, newchild): |
183
|
|
|
# Moreover the entity is OVAL <external_variable> |
184
|
|
|
if oval_entity_is_extvar(newchild): |
185
|
|
|
# If OVAL entity is identical to some already included |
186
|
|
|
# in the benchmark and represents an OVAL <external_variable> |
187
|
|
|
# it's safe to ignore this ID (since external variables are |
188
|
|
|
# in multiple checks for clarity reasons) |
189
|
|
|
pass |
190
|
|
|
# Some other OVAL entity |
191
|
|
|
else: |
192
|
|
|
# If OVAL entity is identical, but not external_variable, the |
193
|
|
|
# implementation should be rewritten each entity to be present |
194
|
|
|
# just once |
195
|
|
|
sys.stderr.write("ERROR: OVAL ID '%s' is used multiple times " |
196
|
|
|
"and should represent the same elements.\n" |
197
|
|
|
% (newid)) |
198
|
|
|
sys.stderr.write("Rewrite the OVAL checks. Place the identical " |
199
|
|
|
"IDs into their own definition and extend " |
200
|
|
|
"this definition by it.\n") |
201
|
|
|
sys.exit(1) |
202
|
|
|
# ID is identical, but OVAL entities are semantically difference => |
203
|
|
|
# report and error and exit with failure |
204
|
|
|
# Fixes: https://github.com/ComplianceAsCode/content/issues/1275 |
205
|
|
|
else: |
206
|
|
|
if not oval_entity_is_extvar(existing) and \ |
207
|
|
|
not oval_entity_is_extvar(newchild): |
208
|
|
|
# This is an error scenario - since by skipping second |
209
|
|
|
# implementation and using the first one for both references, |
210
|
|
|
# we might evaluate wrong requirement for the second entity |
211
|
|
|
# => report an error and exit with failure in that case |
212
|
|
|
# See |
213
|
|
|
# https://github.com/ComplianceAsCode/content/issues/1275 |
214
|
|
|
# for a reproducer and what could happen in this case |
215
|
|
|
sys.stderr.write("ERROR: it's not possible to use the " + |
216
|
|
|
"same ID: %s " % newid + "for two " + |
217
|
|
|
"semantically different OVAL entities:\n") |
218
|
|
|
sys.stderr.write("First entity %s\n" % ElementTree.tostring(existing)) |
219
|
|
|
sys.stderr.write("Second entity %s\n" % ElementTree.tostring(newchild)) |
220
|
|
|
sys.stderr.write("Use different ID for the second entity!!!\n") |
221
|
|
|
sys.exit(1) |
222
|
|
|
else: |
223
|
|
|
element.append(newchild) |
224
|
|
|
element_child_cache[element][newid] = newchild |
225
|
|
|
|
226
|
|
|
|
227
|
|
|
def check_oval_version(oval_version): |
228
|
|
|
"""Not necessary, but should help with typos""" |
229
|
|
|
|
230
|
|
|
supported_versions = ["5.11"] |
231
|
|
|
if oval_version not in supported_versions: |
232
|
|
|
supported_versions_str = ", ".join(supported_versions) |
233
|
|
|
sys.stderr.write( |
234
|
|
|
"Suspicious oval version \"%s\", one of {%s} is " |
235
|
|
|
"expected.\n" % (oval_version, supported_versions_str)) |
236
|
|
|
sys.exit(1) |
237
|
|
|
|
238
|
|
|
|
239
|
|
|
def _check_is_loaded(loaded_dict, filename, version): |
240
|
|
|
if filename in loaded_dict: |
241
|
|
|
if loaded_dict[filename] >= version: |
242
|
|
|
return True |
243
|
|
|
|
244
|
|
|
# Should rather fail, than override something unwanted |
245
|
|
|
sys.stderr.write( |
246
|
|
|
"You cannot override generic OVAL file in version '%s' " |
247
|
|
|
"by more specific one in older version '%s'" % |
248
|
|
|
(version, loaded_dict[filename]) |
249
|
|
|
) |
250
|
|
|
sys.exit(1) |
251
|
|
|
|
252
|
|
|
return False |
253
|
|
|
|
254
|
|
|
|
255
|
|
|
def _create_oval_tree_from_string(xml_content): |
256
|
|
|
try: |
257
|
|
|
argument = oval_header + xml_content + oval_footer |
258
|
|
|
oval_file_tree = ElementTree.fromstring(argument) |
259
|
|
|
except ElementTree.ParseError as error: |
260
|
|
|
line, column = error.position |
261
|
|
|
lines = argument.splitlines() |
262
|
|
|
before = '\n'.join(lines[:line]) |
263
|
|
|
column_pointer = ' ' * (column - 1) + '^' |
264
|
|
|
sys.stderr.write( |
265
|
|
|
"%s\n%s\nError when parsing OVAL file.\n" % |
266
|
|
|
(before, column_pointer)) |
267
|
|
|
sys.exit(1) |
268
|
|
|
return oval_file_tree |
269
|
|
|
|
270
|
|
|
|
271
|
|
|
def _check_oval_version_from_oval(oval_file_tree, oval_version): |
272
|
|
|
for defgroup in oval_file_tree.findall("./{%s}def-group" % oval_ns): |
273
|
|
|
file_oval_version = defgroup.get("oval_version") |
274
|
|
|
|
275
|
|
|
if file_oval_version is None: |
|
|
|
|
276
|
|
|
# oval_version does not exist in <def-group/> |
277
|
|
|
# which means the OVAL is supported for any version. |
278
|
|
|
# By default, that version is 5.11 |
279
|
|
|
file_oval_version = "5.11" |
280
|
|
|
|
281
|
|
|
if tuple(oval_version.split(".")) >= tuple(file_oval_version.split(".")): |
282
|
|
|
return True |
283
|
|
|
|
284
|
|
|
|
285
|
|
|
def _check_rule_id(oval_file_tree, rule_id): |
286
|
|
|
for definition in oval_file_tree.findall( |
287
|
|
|
"./{%s}def-group/{%s}definition" % (oval_ns, oval_ns)): |
288
|
|
|
definition_id = definition.get("id") |
289
|
|
|
return definition_id == rule_id |
290
|
|
|
return False |
291
|
|
|
|
292
|
|
|
|
293
|
|
|
def _list_full_paths(directory): |
294
|
|
|
full_paths = [os.path.join(directory, x) for x in os.listdir(directory)] |
295
|
|
|
return sorted(full_paths) |
296
|
|
|
|
297
|
|
|
|
298
|
|
|
class OVALBuilder: |
299
|
|
|
def __init__( |
300
|
|
|
self, env_yaml, product_yaml_path, shared_directories, |
301
|
|
|
build_ovals_dir): |
302
|
|
|
self.env_yaml = env_yaml |
303
|
|
|
self.product_yaml = products.Product(product_yaml_path) |
304
|
|
|
self.shared_directories = shared_directories |
305
|
|
|
self.build_ovals_dir = build_ovals_dir |
306
|
|
|
self.already_loaded = dict() |
307
|
|
|
self.oval_version = utils.required_key( |
308
|
|
|
env_yaml, "target_oval_version_str") |
309
|
|
|
self.product = utils.required_key(env_yaml, "product") |
310
|
|
|
|
311
|
|
|
def build_shorthand(self, include_benchmark): |
312
|
|
|
if self.build_ovals_dir: |
313
|
|
|
mkdir_p(self.build_ovals_dir) |
314
|
|
|
all_checks = [] |
315
|
|
|
if include_benchmark: |
316
|
|
|
all_checks += self._get_checks_from_benchmark() |
317
|
|
|
all_checks += self._get_checks_from_shared_directories() |
318
|
|
|
document_body = "".join(all_checks) |
319
|
|
|
return document_body |
320
|
|
|
|
321
|
|
|
def _get_checks_from_benchmark(self): |
322
|
|
|
product_dir = self.product_yaml["product_dir"] |
323
|
|
|
relative_guide_dir = utils.required_key(self.env_yaml, "benchmark_root") |
324
|
|
|
guide_dir = os.path.abspath( |
325
|
|
|
os.path.join(product_dir, relative_guide_dir)) |
326
|
|
|
additional_content_directories = self.env_yaml.get( |
327
|
|
|
"additional_content_directories", []) |
328
|
|
|
dirs_to_scan = [guide_dir] |
329
|
|
|
for rd in additional_content_directories: |
330
|
|
|
abspath = os.path.abspath(os.path.join(product_dir, rd)) |
331
|
|
|
dirs_to_scan.append(abspath) |
332
|
|
|
rule_dirs = list(find_rule_dirs_in_paths(dirs_to_scan)) |
333
|
|
|
oval_checks = self._process_directories(rule_dirs, True) |
334
|
|
|
return oval_checks |
335
|
|
|
|
336
|
|
|
def _get_checks_from_shared_directories(self): |
337
|
|
|
# earlier directory has higher priority |
338
|
|
|
reversed_dirs = self.shared_directories[::-1] |
339
|
|
|
oval_checks = self._process_directories(reversed_dirs, False) |
340
|
|
|
return oval_checks |
341
|
|
|
|
342
|
|
|
def _process_directories(self, directories, from_benchmark): |
343
|
|
|
oval_checks = [] |
344
|
|
|
for directory in directories: |
345
|
|
|
if not os.path.exists(directory): |
346
|
|
|
continue |
347
|
|
|
oval_checks += self._process_directory(directory, from_benchmark) |
348
|
|
|
return oval_checks |
349
|
|
|
|
350
|
|
|
def _get_list_of_oval_files(self, directory, from_benchmark): |
351
|
|
|
if from_benchmark: |
352
|
|
|
oval_files = get_rule_dir_ovals(directory, self.product) |
353
|
|
|
else: |
354
|
|
|
oval_files = _list_full_paths(directory) |
355
|
|
|
return oval_files |
356
|
|
|
|
357
|
|
|
def _process_directory(self, directory, from_benchmark): |
358
|
|
|
try: |
359
|
|
|
context = self._get_context(directory, from_benchmark) |
360
|
|
|
except DocumentationNotComplete: |
361
|
|
|
return [] |
362
|
|
|
oval_files = self._get_list_of_oval_files(directory, from_benchmark) |
363
|
|
|
oval_checks = self._get_directory_oval_checks( |
364
|
|
|
context, oval_files, from_benchmark) |
365
|
|
|
return oval_checks |
366
|
|
|
|
367
|
|
|
def _get_directory_oval_checks(self, context, oval_files, from_benchmark): |
368
|
|
|
oval_checks = [] |
369
|
|
|
for file_path in oval_files: |
370
|
|
|
xml_content = self._process_oval_file( |
371
|
|
|
file_path, from_benchmark, context) |
372
|
|
|
if xml_content is None: |
373
|
|
|
continue |
374
|
|
|
oval_checks.append(xml_content) |
375
|
|
|
return oval_checks |
376
|
|
|
|
377
|
|
|
def _read_oval_file(self, file_path, context, from_benchmark): |
378
|
|
|
if from_benchmark or "checks_from_templates" not in file_path: |
379
|
|
|
xml_content = process_file_with_macros(file_path, context) |
380
|
|
|
else: |
381
|
|
|
with open(file_path, "r") as f: |
382
|
|
|
xml_content = f.read() |
383
|
|
|
return xml_content |
384
|
|
|
|
385
|
|
|
def _create_key(self, file_path, from_benchmark): |
386
|
|
|
if from_benchmark: |
387
|
|
|
rule_id = os.path.basename( |
388
|
|
|
(os.path.dirname(os.path.dirname(file_path)))) |
389
|
|
|
oval_key = "%s.xml" % rule_id |
390
|
|
|
else: |
391
|
|
|
oval_key = os.path.basename(file_path) |
392
|
|
|
return oval_key |
393
|
|
|
|
394
|
|
|
def _process_oval_file(self, file_path, from_benchmark, context): |
395
|
|
|
if not file_path.endswith(".xml"): |
396
|
|
|
return None |
397
|
|
|
oval_key = self._create_key(file_path, from_benchmark) |
398
|
|
|
if _check_is_loaded(self.already_loaded, oval_key, self.oval_version): |
399
|
|
|
return None |
400
|
|
|
xml_content = self._read_oval_file(file_path, context, from_benchmark) |
401
|
|
|
if not self._manage_oval_file_xml_content( |
402
|
|
|
file_path, xml_content, from_benchmark): |
403
|
|
|
return None |
404
|
|
|
self.already_loaded[oval_key] = self.oval_version |
405
|
|
|
return xml_content |
406
|
|
|
|
407
|
|
|
def _check_affected(self, tree): |
408
|
|
|
definitions = tree.findall(".//{%s}definition" % (oval_ns)) |
409
|
|
|
for definition in definitions: |
410
|
|
|
def_id = definition.get("id") |
411
|
|
|
affected = definition.findall( |
412
|
|
|
"./{%s}metadata/{%s}affected" % (oval_ns, oval_ns)) |
413
|
|
|
if not affected: |
414
|
|
|
raise ValueError( |
415
|
|
|
"Definition '%s' doesn't contain OVAL 'affected' element" |
416
|
|
|
% (def_id)) |
417
|
|
|
|
418
|
|
|
def _manage_oval_file_xml_content( |
419
|
|
|
self, file_path, xml_content, from_benchmark): |
420
|
|
|
oval_file_tree = _create_oval_tree_from_string(xml_content) |
421
|
|
|
self._check_affected(oval_file_tree) |
422
|
|
|
if not _check_is_applicable_for_product(xml_content, self.product): |
423
|
|
|
return False |
424
|
|
|
if not _check_oval_version_from_oval(oval_file_tree, self.oval_version): |
425
|
|
|
return False |
426
|
|
|
if from_benchmark: |
427
|
|
|
self._benchmark_specific_actions( |
428
|
|
|
file_path, xml_content, oval_file_tree) |
429
|
|
|
return True |
430
|
|
|
|
431
|
|
|
def _benchmark_specific_actions( |
432
|
|
|
self, file_path, xml_content, oval_file_tree): |
433
|
|
|
rule_id = os.path.basename( |
434
|
|
|
(os.path.dirname(os.path.dirname(file_path)))) |
435
|
|
|
self._store_intermediate_file(rule_id, xml_content) |
436
|
|
|
if not _check_rule_id(oval_file_tree, rule_id): |
437
|
|
|
msg = "ERROR: OVAL definition in '%s' doesn't match rule ID '%s'." % ( |
438
|
|
|
file_path, rule_id) |
439
|
|
|
print(msg, file=sys.stderr) |
440
|
|
|
sys.exit(1) |
441
|
|
|
|
442
|
|
|
def _get_context(self, directory, from_benchmark): |
443
|
|
|
if from_benchmark: |
444
|
|
|
rule_path = os.path.join(directory, "rule.yml") |
445
|
|
|
rule = Rule.from_yaml(rule_path, self.env_yaml) |
446
|
|
|
context = self._create_local_env_yaml_for_rule(rule) |
447
|
|
|
else: |
448
|
|
|
context = self.env_yaml |
449
|
|
|
return context |
450
|
|
|
|
451
|
|
|
def _create_local_env_yaml_for_rule(self, rule): |
452
|
|
|
local_env_yaml = dict() |
453
|
|
|
local_env_yaml.update(self.env_yaml) |
454
|
|
|
local_env_yaml['rule_id'] = rule.id_ |
455
|
|
|
local_env_yaml['rule_title'] = rule.title |
456
|
|
|
prodtypes = parse_prodtype(rule.prodtype) |
457
|
|
|
local_env_yaml['products'] = prodtypes # default is all |
458
|
|
|
return local_env_yaml |
459
|
|
|
|
460
|
|
|
def _store_intermediate_file(self, rule_id, xml_content): |
461
|
|
|
if not self.build_ovals_dir: |
462
|
|
|
return |
463
|
|
|
output_file_name = rule_id + ".xml" |
464
|
|
|
output_filepath = os.path.join(self.build_ovals_dir, output_file_name) |
465
|
|
|
with open(output_filepath, "w") as f: |
466
|
|
|
f.write(xml_content) |
467
|
|
|
|