|
1
|
|
|
# Copyright 2022, Red Hat, Inc. |
|
2
|
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later |
|
3
|
|
|
|
|
4
|
1 |
|
import logging |
|
5
|
1 |
|
import uuid |
|
6
|
|
|
|
|
7
|
1 |
|
from lxml.etree import Element |
|
8
|
|
|
|
|
9
|
1 |
|
from openscap_report.dataclasses import dataclass |
|
10
|
|
|
|
|
11
|
1 |
|
from ..data_structures import OvalNode |
|
12
|
1 |
|
from ..exceptions import MissingOVALResult |
|
13
|
1 |
|
from ..namespaces import NAMESPACES |
|
14
|
1 |
|
from .oval_test_parser import OVALTestParser |
|
15
|
|
|
|
|
16
|
1 |
|
STR_TO_BOOL = {'true': True, 'false': False} |
|
17
|
1 |
|
STR_NEGATION_BOOL = {'true': 'false', 'false': 'true'} |
|
18
|
|
|
|
|
19
|
|
|
|
|
20
|
1 |
|
@dataclass |
|
21
|
1 |
|
class OVALReport: |
|
22
|
1 |
|
oval_report_id: str |
|
23
|
1 |
|
oval_report_element: Element |
|
24
|
1 |
|
oval_results_element: Element |
|
25
|
1 |
|
oval_test_parser: OVALTestParser |
|
26
|
|
|
|
|
27
|
|
|
|
|
28
|
1 |
|
class OVALResultParser: |
|
29
|
1 |
|
def __init__(self, root, oval_var_id_to_value_id, ref_values): |
|
30
|
1 |
|
self.root = root |
|
31
|
1 |
|
self.oval_var_id_to_value_id = oval_var_id_to_value_id |
|
32
|
1 |
|
self.ref_values = ref_values |
|
33
|
1 |
|
self.oval_reports = self._get_oval_reports() |
|
34
|
1 |
|
logging.info(self.oval_reports) |
|
35
|
|
|
|
|
36
|
1 |
|
def _get_oval_reports(self): |
|
37
|
1 |
|
oval_reports = {} |
|
38
|
1 |
|
reports = self.root.find('.//arf:reports', NAMESPACES) |
|
39
|
1 |
|
if reports is None: |
|
40
|
1 |
|
raise MissingOVALResult("all_OVAL_results") |
|
41
|
|
|
|
|
42
|
1 |
|
for report_element in reports: |
|
43
|
1 |
|
report_id = report_element.get("id") |
|
44
|
1 |
|
if "oval" in report_id: |
|
45
|
1 |
|
oval_results = self._get_oval_results(report_element) |
|
46
|
1 |
|
oval_test_parser = OVALTestParser( |
|
47
|
|
|
report_element, self.oval_var_id_to_value_id, self.ref_values |
|
48
|
|
|
) |
|
49
|
1 |
|
oval_reports[report_id] = OVALReport( |
|
50
|
|
|
report_id, |
|
51
|
|
|
report_element, |
|
52
|
|
|
oval_results, |
|
53
|
|
|
oval_test_parser |
|
54
|
|
|
) |
|
55
|
1 |
|
return oval_reports |
|
56
|
|
|
|
|
57
|
1 |
|
def _get_oval_results(self, oval_report): |
|
58
|
1 |
|
return oval_report.find( |
|
59
|
|
|
('.//XMLSchema:oval_results/XMLSchema:results/' |
|
60
|
|
|
'XMLSchema:system/XMLSchema:definitions'), NAMESPACES) |
|
61
|
|
|
|
|
62
|
1 |
|
def get_oval_trees_by_oval_reports(self): |
|
63
|
1 |
|
dict_of_oval_reports = {} |
|
64
|
1 |
|
for report_id, report in self.oval_reports.items(): |
|
65
|
1 |
|
dict_of_oval_results = {} |
|
66
|
1 |
|
for definition in report.oval_results_element: |
|
67
|
1 |
|
id_definition = definition.get('definition_id') |
|
68
|
1 |
|
criteria_result = definition[0] |
|
69
|
1 |
|
dict_of_oval_results[id_definition] = self._build_node( |
|
70
|
|
|
criteria_result, |
|
71
|
|
|
"Definition", |
|
72
|
|
|
id_definition, |
|
73
|
|
|
report_id |
|
74
|
|
|
) |
|
75
|
1 |
|
dict_of_oval_reports[report_id] = self._fill_extend_definition(dict_of_oval_results) |
|
76
|
1 |
|
return dict_of_oval_reports |
|
77
|
|
|
|
|
78
|
1 |
|
@staticmethod |
|
79
|
1 |
|
def _get_negation(node): |
|
80
|
1 |
|
negation = False |
|
81
|
1 |
|
if node.get('negate') is not None: |
|
82
|
1 |
|
negation = STR_TO_BOOL[node.get('negate')] |
|
83
|
1 |
|
return negation |
|
84
|
|
|
|
|
85
|
1 |
|
@staticmethod |
|
86
|
1 |
|
def _get_result(negation, tree): |
|
87
|
|
|
""" |
|
88
|
|
|
This method removes the negation of |
|
89
|
|
|
the result. Because negation is already |
|
90
|
|
|
included in the result in ARF file. |
|
91
|
|
|
""" |
|
92
|
1 |
|
result = tree.get('result') |
|
93
|
1 |
|
if negation and result in ('true', 'false'): |
|
94
|
1 |
|
result = STR_NEGATION_BOOL[result] |
|
95
|
1 |
|
return result |
|
96
|
|
|
|
|
97
|
1 |
|
def _get_extend_definition_node(self, child): |
|
98
|
1 |
|
negation = self._get_negation(child) |
|
99
|
1 |
|
result_of_node = self._get_result(negation, child) |
|
100
|
1 |
|
return OvalNode( |
|
101
|
|
|
node_id=child.get('definition_ref'), |
|
102
|
|
|
node_type="extend_definition", |
|
103
|
|
|
value=result_of_node, |
|
104
|
|
|
negation=negation, |
|
105
|
|
|
tag="Extend definition", |
|
106
|
|
|
) |
|
107
|
|
|
|
|
108
|
1 |
|
def _get_test_node(self, child, oval_report_id): |
|
109
|
1 |
|
negation = self._get_negation(child) |
|
110
|
1 |
|
result_of_node = self._get_result(negation, child) |
|
111
|
1 |
|
test_id = child.get('test_ref') |
|
112
|
1 |
|
oval_test_parser = self.oval_reports[oval_report_id].oval_test_parser |
|
113
|
1 |
|
return OvalNode( |
|
114
|
|
|
node_id=test_id, |
|
115
|
|
|
node_type="value", |
|
116
|
|
|
value=result_of_node, |
|
117
|
|
|
negation=negation, |
|
118
|
|
|
tag="Test", |
|
119
|
|
|
test_info=oval_test_parser.get_test_info(test_id), |
|
120
|
|
|
) |
|
121
|
|
|
|
|
122
|
1 |
|
def _build_node(self, tree, tag, id_definition, oval_report_id): |
|
123
|
1 |
|
negation = self._get_negation(tree) |
|
124
|
1 |
|
node = OvalNode( |
|
125
|
|
|
node_id=id_definition, |
|
126
|
|
|
node_type=tree.get('operator'), |
|
127
|
|
|
negation=negation, |
|
128
|
|
|
value=self._get_result(negation, tree), |
|
129
|
|
|
tag=tag, |
|
130
|
|
|
children=[], |
|
131
|
|
|
) |
|
132
|
1 |
|
for child in tree: |
|
133
|
1 |
|
if child.get('operator') is not None: |
|
134
|
1 |
|
node.children.append( |
|
135
|
|
|
self._build_node( |
|
136
|
|
|
child, |
|
137
|
|
|
"Criteria", |
|
138
|
|
|
f"no-id-criteria-{uuid.uuid4()}", |
|
139
|
|
|
oval_report_id |
|
140
|
|
|
) |
|
141
|
|
|
) |
|
142
|
|
|
else: |
|
143
|
1 |
|
if child.get('definition_ref') is not None: |
|
144
|
1 |
|
node.children.append(self._get_extend_definition_node(child)) |
|
145
|
|
|
else: |
|
146
|
1 |
|
node.children.append(self._get_test_node(child, oval_report_id)) |
|
147
|
1 |
|
return node |
|
148
|
|
|
|
|
149
|
1 |
|
def _fill_extend_definition(self, dict_of_oval_definitions): |
|
150
|
1 |
|
out = {} |
|
151
|
1 |
|
for id_definition, definition in dict_of_oval_definitions.items(): |
|
152
|
1 |
|
out[id_definition] = self._fill_extend_definition_help( |
|
153
|
|
|
definition, dict_of_oval_definitions) |
|
154
|
1 |
|
return out |
|
155
|
|
|
|
|
156
|
1 |
|
def _fill_extend_definition_help(self, node, dict_of_oval_definitions): |
|
157
|
1 |
|
out = OvalNode( |
|
158
|
|
|
node_id=node.node_id, |
|
159
|
|
|
node_type=node.node_type, |
|
160
|
|
|
negation=node.negation, |
|
161
|
|
|
value=node.value, |
|
162
|
|
|
tag=node.tag, |
|
163
|
|
|
children=[], |
|
164
|
|
|
) |
|
165
|
1 |
|
for child in node.children: |
|
166
|
1 |
|
if child.node_type in ("AND", "OR", "ONE", "XOR"): |
|
167
|
1 |
|
out.children.append( |
|
168
|
|
|
self._fill_extend_definition_help(child, dict_of_oval_definitions)) |
|
169
|
1 |
|
elif child.node_type == "extend_definition": |
|
170
|
1 |
|
out.children.append( |
|
171
|
|
|
self._find_definition_by_id(child, dict_of_oval_definitions)) |
|
172
|
|
|
else: |
|
173
|
1 |
|
out.children.append(child) |
|
174
|
1 |
|
return out |
|
175
|
|
|
|
|
176
|
1 |
|
def _find_definition_by_id(self, node, dict_of_oval_definitions): |
|
177
|
1 |
|
extend_definition_id = node.node_id |
|
178
|
1 |
|
dict_of_oval_definitions[extend_definition_id].negation = node.negation |
|
179
|
1 |
|
dict_of_oval_definitions[extend_definition_id].tag = node.tag |
|
180
|
1 |
|
return self._fill_extend_definition_help( |
|
181
|
|
|
dict_of_oval_definitions[extend_definition_id], dict_of_oval_definitions) |
|
182
|
|
|
|