Test Failed
Push — master ( bc6cae...f1f983 )
by Jan
02:48 queued 13s
created

ssg.oval.append()   A

Complexity

Conditions 3

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 8.6667

Importance

Changes 0
Metric Value
cc 3
eloc 10
nop 2
dl 0
loc 11
ccs 1
cts 7
cp 0.1429
crap 8.6667
rs 9.9
c 0
b 0
f 0
1 2
from __future__ import absolute_import
2 2
from __future__ import print_function
3
4 2
import sys
5 2
import os
6
7 2
from .constants import oval_footer as footer
8 2
from .constants import oval_namespace as ovalns
9 2
from .xml import ElementTree as ET
10 2
from .xml import oval_generated_header
11
12 2
from .jinja import process_file_with_macros
13 2
from .products import _get_implied_properties
14
15
16 2
ASSUMED_OVAL_VERSION_STRING = "5.11"
17
# globals, to make recursion easier in case we encounter extend_definition
18 2
try:
19 2
    ET.register_namespace("oval", ovalns)
20
except AttributeError:
21
    # Legacy Python 2.6 fix, see e.g.
22
    # https://www.programcreek.com/python/example/57552/xml.etree.ElementTree._namespace_map
23
    from xml.etree import ElementTree as ET
24
    ET._namespace_map[ovalns] = "oval"
25
26
27 2
def applicable_platforms(oval_file, oval_version_string=None):
28
    """
29
    Returns the applicable platforms for a given oval file
30
    """
31
32 2
    platforms = []
33
34 2
    if not oval_version_string:
35 2
        oval_version_string = ASSUMED_OVAL_VERSION_STRING
36 2
    header = oval_generated_header("applicable_platforms", oval_version_string, "0.0.1")
37
38 2
    oval_version_list = [int(num) for num in oval_version_string.split(".")]
39 2
    subst_dict = dict(target_oval_version=oval_version_list)
40
41 2
    oval_filename_components = oval_file.split(os.path.sep)
42 2
    if len(oval_filename_components) > 3:
43 2
        subst_dict["rule_id"] = oval_filename_components[-3]
44
    else:
45
        msg = "Unable to get rule ID from OVAL path '{path}'".format(path=oval_file)
46
        print(msg, file=sys.stderr)
47
48 2
    subst_dict = _get_implied_properties(subst_dict)
49 2
    subst_dict['target_oval_version'] = [999, 999.999]
50
51 2
    body = process_file_with_macros(oval_file, subst_dict)
52
53 2
    try:
54 2
        oval_tree = ET.fromstring(header + body + footer)
55
    except Exception as e:
56
        msg = "Error while loading " + oval_file
57
        print(msg, file=sys.stderr)
58
        raise e
59
60 2
    element_path = "./{%s}def-group/{%s}definition/{%s}metadata/{%s}affected/{%s}platform"
61 2
    element_ns_path = element_path % (ovalns, ovalns, ovalns, ovalns, ovalns)
62 2
    for node in oval_tree.findall(element_ns_path):
63 2
        platforms.append(node.text)
64
65 2
    return platforms
66
67
68 2
def parse_affected(oval_contents):
69
    """
70
    Returns the tuple (start_affected, end_affected, platform_indents) for
71
    the passed oval file contents. start_affected is the line number of
72
    starting tag of the <affected> element, end_affected is the line number of
73
    the closing tag of the </affected> element, and platform_indents is a
74
    string containing the indenting characters before the contents of the
75
    <affected> element.
76
    """
77
78
    start_affected = list(filter(lambda x: "<affected" in oval_contents[x],
79
                                 range(0, len(oval_contents))))
80
    if len(start_affected) != 1:
81
        raise ValueError("OVAL file does not contain a single <affected> "
82
                         "element; counted %d in:\n%s\n\n" %
83
                         (len(start_affected), "\n".join(oval_contents)))
84
85
    start_affected = start_affected[0]
86
87
    end_affected = list(filter(lambda x: "</affected" in oval_contents[x],
88
                               range(0, len(oval_contents))))
89
    if len(end_affected) != 1:
90
        raise ValueError("Malformed OVAL file does not contain a single "
91
                         "closing </affected>; counted %d in:\n%s\n\n" %
92
                         (len(start_affected), "\n".join(oval_contents)))
93
    end_affected = end_affected[0]
94
95
    if start_affected >= end_affected:
96
        raise ValueError("Malformed OVAL file: start affected tag begins "
97
                         "on the same line or after ending affected tag: "
98
                         "start:%d vs end:%d:\n%s\n\n" %
99
                         (start_affected, end_affected, oval_contents))
100
101
    # Validate that start_affected contains only a starting <affected> tag;
102
    # otherwise, using this information to update the <platform> subelements
103
    # would fail.
104
    start_line = oval_contents[start_affected]
105
    start_line = start_line.strip()
106
107
    if not start_line.startswith('<affected'):
108
        raise ValueError("Malformed OVAL file: line with starting affected "
109
                         "tag contains other elements: line:%s\n%s\n\n" %
110
                         (start_line, oval_contents))
111
    if '<' in start_line[1:]:
112
        raise ValueError("Malformed OVAL file: line with starting affected "
113
                         "tag contains other elements: line:%s\n%s\n\n" %
114
                         (start_line, oval_contents))
115
116
    # Validate that end_affected contains only an ending </affected> tag;
117
    # otherwise, using this information to update the <platform> subelements
118
    # would fail.
119
    end_line = oval_contents[end_affected]
120
    end_line = end_line.strip()
121
122
    if not end_line.startswith('</affected>'):
123
        raise ValueError("Malformed OVAL file: line with ending affected "
124
                         "tag contains other elements: line:%s\n%s\n\n" %
125
                         (end_line, oval_contents))
126
    if '<' in end_line[1:]:
127
        raise ValueError("Malformed OVAL file: line with ending affected "
128
                         "tag contains other elements: line:%s\n%s\n\n" %
129
                         (end_line, oval_contents))
130
131
    indents = ""
132
    if start_affected+1 == end_affected:
133
        # Since the affected element is present but empty, the indents should
134
        # be two more spaces than that of the starting <affected> element.
135
        start_index = oval_contents[start_affected].index('<')
136
        indents = oval_contents[start_affected][0:start_index]
137
        indents += "  "
138
    else:
139
        # Otherwise, grab the indents off the next line unmodified, as this is
140
        # likely a platform element tag. We don't validate here that this is
141
        # indeed the case, as other parts of the build infrastructure will
142
        # validate this for us.
143
        start_index = oval_contents[start_affected+1].index('<')
144
        indents = oval_contents[start_affected+1][0:start_index]
145
146
    return start_affected, end_affected, indents
147