|
1
|
|
|
from __future__ import absolute_import |
|
2
|
|
|
from __future__ import print_function |
|
3
|
|
|
import collections |
|
4
|
|
|
|
|
5
|
|
|
import platform |
|
6
|
|
|
import re |
|
7
|
|
|
import xml.etree.ElementTree as ET |
|
8
|
|
|
|
|
9
|
|
|
from .constants import ( |
|
10
|
|
|
xml_version, oval_header, timestamp, PREFIX_TO_NS, XCCDF11_NS, XCCDF12_NS) |
|
11
|
|
|
from .constants import ( |
|
12
|
|
|
datastream_namespace, |
|
13
|
|
|
oval_namespace, |
|
14
|
|
|
stig_ns, |
|
15
|
|
|
cat_namespace, |
|
16
|
|
|
xlink_namespace, |
|
17
|
|
|
ocil_namespace, |
|
18
|
|
|
cpe_language_namespace, |
|
19
|
|
|
) |
|
20
|
|
|
|
|
21
|
|
|
|
|
22
|
|
|
try: |
|
23
|
|
|
from xml.etree import cElementTree as ElementTree |
|
24
|
|
|
except ImportError: |
|
25
|
|
|
from xml.etree import ElementTree as ElementTree |
|
26
|
|
|
|
|
27
|
|
|
|
|
28
|
|
|
def oval_generated_header(product_name, schema_version, ssg_version): |
|
29
|
|
|
return xml_version + oval_header + \ |
|
30
|
|
|
""" |
|
31
|
|
|
<generator> |
|
32
|
|
|
<oval:product_name>%s from SCAP Security Guide</oval:product_name> |
|
33
|
|
|
<oval:product_version>ssg: %s, python: %s</oval:product_version> |
|
34
|
|
|
<oval:schema_version>%s</oval:schema_version> |
|
35
|
|
|
<oval:timestamp>%s</oval:timestamp> |
|
36
|
|
|
</generator>""" % (product_name, ssg_version, platform.python_version(), |
|
37
|
|
|
schema_version, timestamp) |
|
38
|
|
|
|
|
39
|
|
|
|
|
40
|
|
|
def register_namespaces(ns=None): |
|
41
|
|
|
""" |
|
42
|
|
|
Register all possible namespaces |
|
43
|
|
|
""" |
|
44
|
|
|
try: |
|
45
|
|
|
if ns is None: |
|
46
|
|
|
ns = PREFIX_TO_NS |
|
47
|
|
|
for prefix, uri in ns.items(): |
|
48
|
|
|
ElementTree.register_namespace(prefix, uri) |
|
49
|
|
|
except Exception: |
|
50
|
|
|
# Probably an old version of Python |
|
51
|
|
|
# Doesn't matter, as this is non-essential. |
|
52
|
|
|
pass |
|
53
|
|
|
|
|
54
|
|
|
|
|
55
|
|
|
def get_namespaces_from(file): |
|
56
|
|
|
""" |
|
57
|
|
|
Return dictionary of namespaces in file. Return empty dictionary in case of error. |
|
58
|
|
|
""" |
|
59
|
|
|
result = {} |
|
60
|
|
|
try: |
|
61
|
|
|
result = { |
|
62
|
|
|
key: value |
|
63
|
|
|
for _, (key, value) in ElementTree.iterparse(file, events=["start-ns"]) |
|
64
|
|
|
} |
|
65
|
|
|
except Exception: |
|
66
|
|
|
# Probably an old version of Python |
|
67
|
|
|
# Doesn't matter, as this is non-essential. |
|
68
|
|
|
pass |
|
69
|
|
|
finally: |
|
70
|
|
|
return result |
|
71
|
|
|
|
|
72
|
|
|
|
|
73
|
|
|
def open_xml(filename): |
|
74
|
|
|
""" |
|
75
|
|
|
Given a filename, register all possible namespaces, and return the XML tree. |
|
76
|
|
|
""" |
|
77
|
|
|
register_namespaces() |
|
78
|
|
|
return ElementTree.parse(filename) |
|
79
|
|
|
|
|
80
|
|
|
|
|
81
|
|
|
def parse_file(filename): |
|
82
|
|
|
""" |
|
83
|
|
|
Given a filename, return the root of the ElementTree |
|
84
|
|
|
""" |
|
85
|
|
|
tree = open_xml(filename) |
|
86
|
|
|
return tree.getroot() |
|
87
|
|
|
|
|
88
|
|
|
|
|
89
|
|
|
def map_elements_to_their_ids(tree, xpath_expr): |
|
90
|
|
|
""" |
|
91
|
|
|
Given an ElementTree and an XPath expression, |
|
92
|
|
|
iterate through matching elements and create 1:1 id->element mapping. |
|
93
|
|
|
|
|
94
|
|
|
Raises AssertionError if a matching element doesn't have the ``id`` |
|
95
|
|
|
attribute. |
|
96
|
|
|
|
|
97
|
|
|
Returns mapping as a dictionary |
|
98
|
|
|
""" |
|
99
|
|
|
aggregated = {} |
|
100
|
|
|
for element in tree.findall(xpath_expr): |
|
101
|
|
|
element_id = element.get("id") |
|
102
|
|
|
assert element_id is not None |
|
103
|
|
|
aggregated[element_id] = element |
|
104
|
|
|
return aggregated |
|
105
|
|
|
|
|
106
|
|
|
|
|
107
|
|
|
SSG_XHTML_TAGS = [ |
|
108
|
|
|
'table', 'tr', 'th', 'td', 'ul', 'li', 'ol', |
|
109
|
|
|
'p', 'code', 'strong', 'b', 'em', 'i', 'pre', 'br', 'hr', 'small', |
|
110
|
|
|
] |
|
111
|
|
|
|
|
112
|
|
|
|
|
113
|
|
|
def add_xhtml_namespace(data): |
|
114
|
|
|
""" |
|
115
|
|
|
Given a xml blob, adds the xhtml namespace to all relevant tags. |
|
116
|
|
|
""" |
|
117
|
|
|
# The use of lambda in the lines below is a workaround for https://bugs.python.org/issue1519638 |
|
118
|
|
|
# I decided for this approach to avoid adding workarounds in the matching regex, this way only |
|
119
|
|
|
# the substituted part contains the workaround. |
|
120
|
|
|
# Transform <tt> in <code> |
|
121
|
|
|
data = re.sub(r'<(\/)?tt(\/)?>', |
|
122
|
|
|
lambda m: r'<' + (m.group(1) or '') + 'code' + (m.group(2) or '') + '>', data) |
|
123
|
|
|
# Adds xhtml prefix to elements: <tag>, </tag>, <tag/> |
|
124
|
|
|
return re.sub(r'<(\/)?((?:%s).*?)(\/)?>' % "|".join(SSG_XHTML_TAGS), |
|
125
|
|
|
lambda m: r'<' + (m.group(1) or '') + 'xhtml:' + |
|
126
|
|
|
(m.group(2) or '') + (m.group(3) or '') + '>', |
|
127
|
|
|
data) |
|
128
|
|
|
|
|
129
|
|
|
|
|
130
|
|
|
def determine_xccdf_tree_namespace(tree): |
|
131
|
|
|
root = tree.getroot() |
|
132
|
|
|
if root.tag == "{%s}Benchmark" % XCCDF11_NS: |
|
133
|
|
|
xccdf_ns = XCCDF11_NS |
|
134
|
|
|
elif root.tag == "{%s}Benchmark" % XCCDF12_NS: |
|
135
|
|
|
xccdf_ns = XCCDF12_NS |
|
136
|
|
|
else: |
|
137
|
|
|
raise ValueError("Unknown root element '%s'" % root.tag) |
|
138
|
|
|
return xccdf_ns |
|
139
|
|
|
|
|
140
|
|
|
|
|
141
|
|
|
def get_element_tag_without_ns(xml_tag): |
|
142
|
|
|
return re.search(r'^{.*}(.*)', xml_tag).group(1) |
|
143
|
|
|
|
|
144
|
|
|
|
|
145
|
|
|
def get_element_namespace(self): |
|
146
|
|
|
return re.search(r'^{(.*)}.*', self.root.tag).group(1) |
|
147
|
|
|
|
|
148
|
|
|
|
|
149
|
|
|
class XMLElement(object): |
|
150
|
|
|
''' |
|
151
|
|
|
Represents an generic element read from an XML file. |
|
152
|
|
|
''' |
|
153
|
|
|
ns = { |
|
154
|
|
|
"ds": datastream_namespace, |
|
155
|
|
|
"xccdf-1.1": XCCDF11_NS, |
|
156
|
|
|
"xccdf-1.2": XCCDF12_NS, |
|
157
|
|
|
"oval": oval_namespace, |
|
158
|
|
|
"catalog": cat_namespace, |
|
159
|
|
|
"xlink": xlink_namespace, |
|
160
|
|
|
"ocil": ocil_namespace, |
|
161
|
|
|
"cpe-lang": cpe_language_namespace, |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
def __init__(self, root): |
|
165
|
|
|
self.root = root |
|
166
|
|
|
self._determine_xccdf_version() |
|
167
|
|
|
|
|
168
|
|
|
def get_attr(self, attr): |
|
169
|
|
|
return self.root.get(attr) |
|
170
|
|
|
|
|
171
|
|
|
def get_namespace(self): |
|
172
|
|
|
return re.search(r'^{(.*)}.*', self.root.tag).group(1) |
|
173
|
|
|
|
|
174
|
|
|
def _determine_xccdf_version(self): |
|
175
|
|
|
if self.get_namespace() == self.ns["xccdf-1.1"]: |
|
176
|
|
|
self.content_xccdf_ns = "xccdf-1.1" |
|
177
|
|
|
else: |
|
178
|
|
|
self.content_xccdf_ns = "xccdf-1.2" |
|
179
|
|
|
|
|
180
|
|
|
|
|
181
|
|
|
class XMLContent(XMLElement): |
|
182
|
|
|
''' |
|
183
|
|
|
Can represent a Data Stream or an XCCDF Benchmark read from an XML file. |
|
184
|
|
|
''' |
|
185
|
|
|
|
|
186
|
|
|
check_engines = [("OVAL", "oval:oval_definitions"), ("OCIL", "ocil:ocil")] |
|
187
|
|
|
|
|
188
|
|
|
def __init__(self, root): |
|
189
|
|
|
super(XMLContent, self).__init__(root) |
|
190
|
|
|
self.component_refs = self.get_component_refs() |
|
191
|
|
|
self.uris = self.get_uris() |
|
192
|
|
|
self.components = self._find_all_component_contents() |
|
193
|
|
|
|
|
194
|
|
|
def get_component_refs(self): |
|
195
|
|
|
component_refs = dict() |
|
196
|
|
|
for ds in self.root.findall("ds:data-stream", self.ns): |
|
197
|
|
|
checks = ds.find("ds:checks", self.ns) |
|
198
|
|
|
for component_ref in checks.findall("ds:component-ref", self.ns): |
|
199
|
|
|
component_ref_href = component_ref.get("{%s}href" % (self.ns["xlink"])) |
|
200
|
|
|
component_ref_id = component_ref.get("id") |
|
201
|
|
|
component_refs[component_ref_href] = component_ref_id |
|
202
|
|
|
return component_refs |
|
203
|
|
|
|
|
204
|
|
|
def get_uris(self): |
|
205
|
|
|
uris = dict() |
|
206
|
|
|
for ds in self.root.findall("ds:data-stream", self.ns): |
|
207
|
|
|
checklists = ds.find("ds:checklists", self.ns) |
|
208
|
|
|
catalog = checklists.find(".//catalog:catalog", self.ns) |
|
209
|
|
|
for uri in catalog.findall("catalog:uri", self.ns): |
|
210
|
|
|
uri_uri = uri.get("uri") |
|
211
|
|
|
uri_name = uri.get("name") |
|
212
|
|
|
uris[uri_uri] = uri_name |
|
213
|
|
|
return uris |
|
214
|
|
|
|
|
215
|
|
|
def is_benchmark(self): |
|
216
|
|
|
if self.root.tag == "{%s}Benchmark" % (self.ns["xccdf-1.2"]): |
|
217
|
|
|
return True |
|
218
|
|
|
elif self.root.tag == "{%s}Benchmark" % (self.ns["xccdf-1.1"]): |
|
219
|
|
|
self.content_xccdf_ns = "xccdf-1.1" |
|
220
|
|
|
return True |
|
221
|
|
|
|
|
222
|
|
|
def get_benchmarks(self): |
|
223
|
|
|
ds_components = self.root.findall("ds:component", self.ns) |
|
224
|
|
|
if not ds_components: |
|
225
|
|
|
# The content is not a DS, maybe it is just an XCCDF Benchmark |
|
226
|
|
|
if self.is_benchmark(): |
|
227
|
|
|
yield XMLBenchmark(self.root) |
|
228
|
|
|
for component in ds_components: |
|
229
|
|
|
for benchmark in component.findall("%s:Benchmark" % self.content_xccdf_ns, self.ns): |
|
230
|
|
|
yield XMLBenchmark(benchmark) |
|
231
|
|
|
|
|
232
|
|
|
def find_benchmark(self, id_): |
|
233
|
|
|
ds_components = self.root.findall("ds:component", self.ns) |
|
234
|
|
|
if not ds_components: |
|
235
|
|
|
# The content is not a DS, maybe it is just an XCCDF Benchmark |
|
236
|
|
|
if self.is_benchmark(): |
|
237
|
|
|
return XMLBenchmark(self.root) |
|
238
|
|
|
for component in ds_components: |
|
239
|
|
|
benchmark = component.find("%s:Benchmark[@id='%s']" |
|
240
|
|
|
% (self.content_xccdf_ns, id_), self.ns) |
|
241
|
|
|
if benchmark is not None: |
|
242
|
|
|
return XMLBenchmark(benchmark) |
|
243
|
|
|
return None |
|
244
|
|
|
|
|
245
|
|
|
def _find_all_component_contents(self): |
|
246
|
|
|
component_doc_dict = collections.defaultdict(dict) |
|
247
|
|
|
for component in self.root.findall("ds:component", self.ns): |
|
248
|
|
|
for check_id, check_tag in self.check_engines: |
|
249
|
|
|
def_doc = component.find(check_tag, self.ns) |
|
250
|
|
|
if def_doc is None: |
|
251
|
|
|
continue |
|
252
|
|
|
comp_id = component.get("id") |
|
253
|
|
|
comp_href = "#" + comp_id |
|
254
|
|
|
try: |
|
255
|
|
|
filename = self.uris["#" + self.component_refs[comp_href]] |
|
256
|
|
|
except KeyError: |
|
257
|
|
|
continue |
|
258
|
|
|
xml_component = XMLComponent(def_doc) |
|
259
|
|
|
component_doc_dict[check_id][filename] = xml_component |
|
260
|
|
|
return component_doc_dict |
|
261
|
|
|
|
|
262
|
|
|
|
|
263
|
|
|
class XMLBenchmark(XMLElement): |
|
264
|
|
|
''' |
|
265
|
|
|
Represents an XCCDF Benchmark read from an XML file. |
|
266
|
|
|
''' |
|
267
|
|
|
|
|
268
|
|
|
def __init__(self, root): |
|
269
|
|
|
super(XMLBenchmark, self).__init__(root) |
|
270
|
|
|
self.root = root |
|
271
|
|
|
|
|
272
|
|
|
def find_rules(self, rule_id): |
|
273
|
|
|
if rule_id: |
|
274
|
|
|
rules = [XMLRule(r) for r in self.root.iterfind( |
|
275
|
|
|
".//%s:Rule[@id='%s']" % (self.content_xccdf_ns, rule_id), self.ns)] |
|
276
|
|
|
if len(rules) == 0: |
|
277
|
|
|
raise ValueError("Can't find rule %s" % (rule_id)) |
|
278
|
|
|
else: |
|
279
|
|
|
rules = [XMLRule(r) for r in self.root.iterfind( |
|
280
|
|
|
".//%s:Rule" % (self.content_xccdf_ns), self.ns)] |
|
281
|
|
|
return rules |
|
282
|
|
|
|
|
283
|
|
|
def find_rule(self, rule_id): |
|
284
|
|
|
rule = self.root.find( |
|
285
|
|
|
".//%s:Rule[@id='%s']" % (self.content_xccdf_ns, rule_id), self.ns) |
|
286
|
|
|
return XMLRule(rule) if rule else None |
|
287
|
|
|
|
|
288
|
|
|
def find_all_cpe_platforms(self, idref): |
|
289
|
|
|
cpes = [XMLCPEPlatform(p) for p in self.root.iterfind( |
|
290
|
|
|
".//cpe-lang:platform[@id='{0}']".format(idref.replace("#", "")), self.ns)] |
|
291
|
|
|
return cpes |
|
292
|
|
|
|
|
293
|
|
|
|
|
294
|
|
|
class XMLRule(XMLElement): |
|
295
|
|
|
''' |
|
296
|
|
|
Represents an XCCDF Rule read from an XML file. |
|
297
|
|
|
''' |
|
298
|
|
|
|
|
299
|
|
|
def __init__(self, root): |
|
300
|
|
|
super(XMLRule, self).__init__(root) |
|
301
|
|
|
self.root = root |
|
302
|
|
|
|
|
303
|
|
|
def get_check_element(self, check_system_uri): |
|
304
|
|
|
return self.root.find( |
|
305
|
|
|
"%s:check[@system='%s']" % (self.content_xccdf_ns, check_system_uri), self.ns) |
|
306
|
|
|
|
|
307
|
|
|
def get_check_content_ref_element(self, check_element): |
|
308
|
|
|
return check_element.find( |
|
309
|
|
|
"%s:check-content-ref" % (self.content_xccdf_ns), self.ns) |
|
310
|
|
|
|
|
311
|
|
|
def get_fix_element(self, fix_uri): |
|
312
|
|
|
return self.root.find("%s:fix[@system='%s']" % (self.content_xccdf_ns, fix_uri), self.ns) |
|
313
|
|
|
|
|
314
|
|
|
def get_version_element(self): |
|
315
|
|
|
return self.root.find("%s:version" % (self.content_xccdf_ns), self.ns) |
|
316
|
|
|
|
|
317
|
|
|
def get_all_platform_elements(self): |
|
318
|
|
|
return self.root.findall(".//%s:platform" % (self.content_xccdf_ns), self.ns) |
|
319
|
|
|
|
|
320
|
|
|
def _get_description_text(self, el): |
|
321
|
|
|
desc_text = el.text if el.text else "" |
|
322
|
|
|
# If a 'sub' element is found, lets replace it with the id of the variable it references |
|
323
|
|
|
if get_element_tag_without_ns(el.tag) == "sub": |
|
324
|
|
|
desc_text += "'%s'" % el.attrib['idref'] |
|
325
|
|
|
for desc_el in el: |
|
326
|
|
|
desc_text += self._get_description_text(desc_el) |
|
327
|
|
|
desc_text += el.tail if el.tail else "" |
|
328
|
|
|
return desc_text |
|
329
|
|
|
|
|
330
|
|
|
def get_element_text(self, el): |
|
331
|
|
|
el_tag = get_element_tag_without_ns(el.tag) |
|
332
|
|
|
if el_tag == "description": |
|
333
|
|
|
temp_text = self._get_description_text(el) |
|
334
|
|
|
else: |
|
335
|
|
|
temp_text = "".join(el.itertext()) |
|
336
|
|
|
return temp_text |
|
337
|
|
|
|
|
338
|
|
|
def join_text_elements(self): |
|
339
|
|
|
""" |
|
340
|
|
|
This function collects the text of almost all subelements. |
|
341
|
|
|
Similar to what itertext() would do, except that this function skips some elements that |
|
342
|
|
|
are not relevant for comparison. |
|
343
|
|
|
|
|
344
|
|
|
This function also injects a line for each element whose text was collected, to |
|
345
|
|
|
facilitate tracking of where in the rule the text came from. |
|
346
|
|
|
""" |
|
347
|
|
|
text = "" |
|
348
|
|
|
for el in self.root: |
|
349
|
|
|
el_tag = get_element_tag_without_ns(el.tag) |
|
350
|
|
|
if el_tag == "fix": |
|
351
|
|
|
# We ignore the fix element because it has its own dedicated differ |
|
352
|
|
|
continue |
|
353
|
|
|
if el_tag == "reference" and el.get("href" == stig_ns): |
|
354
|
|
|
# We ignore references to DISA Benchmark Rules, |
|
355
|
|
|
# they have a format of SV-\d+r\d+_rule |
|
356
|
|
|
# and can change for non-text related changes |
|
357
|
|
|
continue |
|
358
|
|
|
el_text = self.get_element_text(el).strip() |
|
359
|
|
|
if el_text: |
|
360
|
|
|
text += "\n[%s]:\n" % el_tag |
|
361
|
|
|
text += el_text + "\n" |
|
362
|
|
|
|
|
363
|
|
|
return text |
|
364
|
|
|
|
|
365
|
|
|
|
|
366
|
|
|
class XMLComponent(XMLElement): |
|
367
|
|
|
''' |
|
368
|
|
|
Represents the element of the Data stream component that has relevant content. |
|
369
|
|
|
|
|
370
|
|
|
This make it easier to access contents pertaining to a SCAP component. |
|
371
|
|
|
''' |
|
372
|
|
|
def __init__(self, root): |
|
373
|
|
|
super(XMLComponent, self).__init__(root) |
|
374
|
|
|
|
|
375
|
|
|
def find_oval_definition(self, def_id): |
|
376
|
|
|
definitions = self.root.find("oval:definitions", self.ns) |
|
377
|
|
|
definition = definitions.find("oval:definition[@id='%s']" % (def_id), self.ns) |
|
378
|
|
|
return XMLOvalDefinition(definition) |
|
379
|
|
|
|
|
380
|
|
|
def find_ocil_questionnaire(self, def_id): |
|
381
|
|
|
questionnaires = self.root.find("ocil:questionnaires", self.ns) |
|
382
|
|
|
questionnaire = questionnaires.find( |
|
383
|
|
|
"ocil:questionnaire[@id='%s']" % def_id, self.ns) |
|
384
|
|
|
return XMLOcilQuestionnaire(questionnaire) |
|
385
|
|
|
|
|
386
|
|
|
def find_ocil_test_action(self, test_action_ref): |
|
387
|
|
|
test_actions = self.root.find("ocil:test_actions", self.ns) |
|
388
|
|
|
test_action = test_actions.find( |
|
389
|
|
|
"ocil:boolean_question_test_action[@id='%s']" % test_action_ref, self.ns) |
|
390
|
|
|
return XMLOcilTestAction(test_action) |
|
391
|
|
|
|
|
392
|
|
|
def find_ocil_boolean_question(self, question_id): |
|
393
|
|
|
questions = self.root.find("ocil:questions", self.ns) |
|
394
|
|
|
question = questions.find( |
|
395
|
|
|
"ocil:boolean_question[@id='%s']" % question_id, self.ns) |
|
396
|
|
|
return XMLOcilQuestion(question) |
|
397
|
|
|
|
|
398
|
|
|
def find_boolean_question(self, ocil_id): |
|
399
|
|
|
questionnaire = self.find_ocil_questionnaire(ocil_id) |
|
400
|
|
|
if questionnaire is None: |
|
401
|
|
|
raise ValueError("OCIL questionnaire %s doesn't exist" % ocil_id) |
|
402
|
|
|
test_action_ref = questionnaire.get_test_action_ref_element().text |
|
403
|
|
|
test_action = self.find_ocil_test_action(test_action_ref) |
|
404
|
|
|
if test_action is None: |
|
405
|
|
|
raise ValueError( |
|
406
|
|
|
"OCIL boolean_question_test_action %s doesn't exist" % ( |
|
407
|
|
|
test_action_ref)) |
|
408
|
|
|
question_id = test_action.get_attr("question_ref") |
|
409
|
|
|
question = self.find_ocil_boolean_question(question_id) |
|
410
|
|
|
if question is None: |
|
411
|
|
|
raise ValueError( |
|
412
|
|
|
"OCIL boolean_question %s doesn't exist" % question_id) |
|
413
|
|
|
question_text = question.get_question_test_element() |
|
414
|
|
|
return question_text.text |
|
415
|
|
|
|
|
416
|
|
|
|
|
417
|
|
|
class XMLOvalDefinition(XMLComponent): |
|
418
|
|
|
def __init__(self, root): |
|
419
|
|
|
super(XMLOvalDefinition, self).__init__(root) |
|
420
|
|
|
|
|
421
|
|
|
def get_criteria_element(self): |
|
422
|
|
|
return self.root.find("oval:criteria", self.ns) |
|
423
|
|
|
|
|
424
|
|
|
def get_elements(self): |
|
425
|
|
|
criteria = self.get_criteria_element() |
|
426
|
|
|
elements = [] |
|
427
|
|
|
for child in criteria.iter(): # iter recurses |
|
428
|
|
|
el_tag = get_element_tag_without_ns(child.tag) |
|
429
|
|
|
if el_tag == "criteria": |
|
430
|
|
|
operator = child.get("operator") |
|
431
|
|
|
elements.append(("criteria", operator)) |
|
432
|
|
|
elif el_tag == "criterion": |
|
433
|
|
|
test_id = child.get("test_ref") |
|
434
|
|
|
elements.append(("criterion", test_id)) |
|
435
|
|
|
elif el_tag == "extend_definition": |
|
436
|
|
|
extend_def_id = child.get("definition_ref") |
|
437
|
|
|
elements.append(("extend_definition", extend_def_id)) |
|
438
|
|
|
return elements |
|
439
|
|
|
|
|
440
|
|
|
|
|
441
|
|
|
class XMLOcilQuestionnaire(XMLComponent): |
|
442
|
|
|
def __init__(self, root): |
|
443
|
|
|
super(XMLOcilQuestionnaire, self).__init__(root) |
|
444
|
|
|
|
|
445
|
|
|
def get_test_action_ref_element(self): |
|
446
|
|
|
return self.root.find( |
|
447
|
|
|
"ocil:actions/ocil:test_action_ref", self.ns) |
|
448
|
|
|
|
|
449
|
|
|
|
|
450
|
|
|
class XMLOcilTestAction(XMLComponent): |
|
451
|
|
|
def __init__(self, root): |
|
452
|
|
|
super(XMLOcilTestAction, self).__init__(root) |
|
453
|
|
|
|
|
454
|
|
|
|
|
455
|
|
|
class XMLOcilQuestion(XMLComponent): |
|
456
|
|
|
def __init__(self, root): |
|
457
|
|
|
super(XMLOcilQuestion, self).__init__(root) |
|
458
|
|
|
|
|
459
|
|
|
def get_question_test_element(self): |
|
460
|
|
|
return self.root.find("ocil:question_text", self.ns) |
|
461
|
|
|
|
|
462
|
|
|
|
|
463
|
|
|
class XMLCPEPlatform(XMLElement): |
|
464
|
|
|
def __init__(self, root): |
|
465
|
|
|
super(XMLCPEPlatform, self).__init__(root) |
|
466
|
|
|
|
|
467
|
|
|
def find_all_check_fact_ref_elements(self): |
|
468
|
|
|
return self.root.findall(".//cpe-lang:check-fact-ref", self.ns) |
|
469
|
|
|
|