Passed
Push — master ( 8b9f7f...914492 )
by Marek
02:15
created

ssg.oval   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 372
Duplicated Lines 0 %

Test Coverage

Coverage 32.17%

Importance

Changes 0
Metric Value
eloc 236
dl 0
loc 372
ccs 65
cts 202
cp 0.3217
rs 5.04
c 0
b 0
f 0
wmc 57

11 Functions

Rating   Name   Duplication   Size   Complexity  
A append() 0 11 3
C _add_elements() 0 38 9
A applicable_platforms() 0 16 2
A find_testfile_or_exit() 0 9 2
A replace_external_vars() 0 24 3
C parse_affected() 0 79 11
A parse_options() 0 20 1
C main() 0 74 11
A read_ovaldefgroup_file() 0 5 2
A get_openscap_supported_oval_version() 0 9 3
C find_testfile() 0 34 10

How to fix   Complexity   

Complexity

Complex classes like ssg.oval often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 2
from __future__ import absolute_import
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2 2
from __future__ import print_function
3
4 2
import sys
5 2
import os
6 2
import re
7 2
import argparse
8 2
import tempfile
9 2
import subprocess
10
11 2
from .constants import oval_footer as footer
12 2
from .constants import oval_namespace as ovalns
13 2
from .rules import get_rule_dir_id, get_rule_dir_ovals, find_rule_dirs
14 2
from .xml import ElementTree as ET
15 2
from .xml import oval_generated_header
16 2
from .id_translate import IDTranslator
17
18 2
SHARED_OVAL = re.sub(r'ssg/.*', 'shared', __file__) + '/checks/oval/'
19 2
LINUX_OS_GUIDE = re.sub(r'ssg/.*', 'linux_os', __file__) + '/guide/'
20
21
22
# globals, to make recursion easier in case we encounter extend_definition
23 2
ET.register_namespace("oval", ovalns)
24 2
definitions = ET.Element("oval:definitions")
0 ignored issues
show
Coding Style Naming introduced by
The name definitions does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
25 2
tests = ET.Element("oval:tests")
0 ignored issues
show
Coding Style Naming introduced by
The name tests does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
26 2
objects = ET.Element("oval:objects")
0 ignored issues
show
Coding Style Naming introduced by
The name objects does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
27 2
states = ET.Element("oval:states")
0 ignored issues
show
Coding Style Naming introduced by
The name states does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
28 2
variables = ET.Element("oval:variables")
0 ignored issues
show
Coding Style Naming introduced by
The name variables does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
29 2
silent_mode = False
0 ignored issues
show
Coding Style Naming introduced by
The name silent_mode does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
30
31
32
# append new child ONLY if it's not a duplicate
33 2
def append(element, newchild):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
34
    global silent_mode
0 ignored issues
show
Coding Style Naming introduced by
The name silent_mode does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
35
    newid = newchild.get("id")
36
    existing = element.find(".//*[@id='" + newid + "']")
37
    if existing is not None:
38
        if not silent_mode:
39
            sys.stderr.write("Notification: this ID is used more than once " +
40
                             "and should represent equivalent elements: " +
41
                             newid + "\n")
42
    else:
43
        element.append(newchild)
44
45
46 2
def _add_elements(body, header):
47
    """Add oval elements to the global Elements defined above"""
48
    global definitions
0 ignored issues
show
Coding Style Naming introduced by
The name definitions does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
49
    global tests
0 ignored issues
show
Coding Style Naming introduced by
The name tests does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
50
    global objects
0 ignored issues
show
Coding Style Naming introduced by
The name objects does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
51
    global states
0 ignored issues
show
Coding Style Naming introduced by
The name states does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
52
    global variables
0 ignored issues
show
Coding Style Naming introduced by
The name variables does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
53
54
    tree = ET.fromstring(header + body + footer)
55
    tree = replace_external_vars(tree)
56
    defname = None
57
    # parse new file(string) as an etree, so we can arrange elements
58
    # appropriately
59
    for childnode in tree.findall("./{%s}def-group/*" % ovalns):
60
        # print "childnode.tag is " + childnode.tag
61
        if childnode.tag is ET.Comment:
62
            continue
63
        if childnode.tag == ("{%s}definition" % ovalns):
64
            append(definitions, childnode)
65
            defname = childnode.get("id")
66
            # extend_definition is a special case:  must include a whole other
67
            # definition
68
            for defchild in childnode.findall(".//{%s}extend_definition"
69
                                              % ovalns):
70
                defid = defchild.get("definition_ref")
71
                extend_ref = find_testfile_or_exit(defid)
72
                includedbody = read_ovaldefgroup_file(extend_ref)
73
                # recursively add the elements in the other file
74
                _add_elements(includedbody, header)
75
        if childnode.tag.endswith("_test"):
76
            append(tests, childnode)
77
        if childnode.tag.endswith("_object"):
78
            append(objects, childnode)
79
        if childnode.tag.endswith("_state"):
80
            append(states, childnode)
81
        if childnode.tag.endswith("_variable"):
82
            append(variables, childnode)
83
    return defname
84
85
86 2
def applicable_platforms(oval_file):
87
    """
88
    Returns the applicable platforms for a given oval file
89
    """
90
91 2
    platforms = []
92 2
    header = oval_generated_header("applicable_platforms", "5.11", "0.0.1")
93 2
    body = read_ovaldefgroup_file(oval_file)
94 2
    oval_tree = ET.fromstring(header + body + footer)
95
96 2
    element_path = "./{%s}def-group/{%s}definition/{%s}metadata/{%s}affected/{%s}platform"
97 2
    element_ns_path = element_path % (ovalns, ovalns, ovalns, ovalns, ovalns)
98 2
    for node in oval_tree.findall(element_ns_path):
99 2
        platforms.append(node.text)
100
101 2
    return platforms
102
103
104 2
def parse_affected(oval_contents):
105
    """
106
    Returns the tuple (start_affected, end_affected, platform_indents) for
107
    the passed oval file contents. start_affected is the line number of
108
    starting tag of the <affected> element, end_affected is the line number of
109
    the closing tag of the </affected> element, and platform_indents is a
110
    string containing the indenting characters before the contents of the
111
    <affected> element.
112
    """
113
114
    start_affected = list(filter(lambda x: "<affected" in oval_contents[x],
115
                                 range(0, len(oval_contents))))
116
    if len(start_affected) != 1:
117
        raise ValueError("OVAL file does not contain a single <affected> "
118
                         "element; counted %d in:\n%s\n\n" %
119
                         (len(start_affected), "\n".join(oval_contents)))
120
121
    start_affected = start_affected[0]
122
123
    end_affected = list(filter(lambda x: "</affected" in oval_contents[x],
124
                               range(0, len(oval_contents))))
125
    if len(end_affected) != 1:
126
        raise ValueError("Malformed OVAL file does not contain a single "
127
                         "closing </affected>; counted %d in:\n%s\n\n" %
128
                         (len(start_affected), "\n".join(oval_contents)))
129
    end_affected = end_affected[0]
130
131
    if start_affected >= end_affected:
132
        raise ValueError("Malformed OVAL file: start affected tag begins "
133
                         "on the same line or after ending affected tag: "
134
                         "start:%d vs end:%d:\n%s\n\n" %
135
                         (start_affected, end_affected, oval_contents))
136
137
    # Validate that start_affected contains only a starting <affected> tag;
138
    # otherwise, using this information to update the <platform> subelements
139
    # would fail.
140
    start_line = oval_contents[start_affected]
141
    start_line = start_line.strip()
142
143
    if not start_line.startswith('<affected'):
144
        raise ValueError("Malformed OVAL file: line with starting affected "
145
                         "tag contains other elements: line:%s\n%s\n\n" %
146
                         (start_line, oval_contents))
147
    if '<' in start_line[1:]:
148
        raise ValueError("Malformed OVAL file: line with starting affected "
149
                         "tag contains other elements: line:%s\n%s\n\n" %
150
                         (start_line, oval_contents))
151
152
    # Validate that end_affected contains only an ending </affected> tag;
153
    # otherwise, using this information to update the <platform> subelements
154
    # would fail.
155
    end_line = oval_contents[end_affected]
156
    end_line = end_line.strip()
157
158
    if not end_line.startswith('</affected>'):
159
        raise ValueError("Malformed OVAL file: line with ending affected "
160
                         "tag contains other elements: line:%s\n%s\n\n" %
161
                         (end_line, oval_contents))
162
    if '<' in end_line[1:]:
163
        raise ValueError("Malformed OVAL file: line with ending affected "
164
                         "tag contains other elements: line:%s\n%s\n\n" %
165
                         (end_line, oval_contents))
166
167
    indents = ""
168
    if start_affected+1 == end_affected:
169
        # Since the affected element is present but empty, the indents should
170
        # be two more spaces than that of the starting <affected> element.
171
        start_index = oval_contents[start_affected].index('<')
172
        indents = oval_contents[start_affected][0:start_index]
173
        indents += "  "
174
    else:
175
        # Otherwise, grab the indents off the next line unmodified, as this is
176
        # likely a platform element tag. We don't validate here that this is
177
        # indeed the case, as other parts of the build infrastructure will
178
        # validate this for us.
179
        start_index = oval_contents[start_affected+1].index('<')
180
        indents = oval_contents[start_affected+1][0:start_index]
181
182
    return start_affected, end_affected, indents
183
184
185 2
def replace_external_vars(tree):
186
    """Replace external_variables with local_variables, so the definition can be
187
       tested independently of an XCCDF file"""
188
189
    # external_variable is a special case: we turn it into a local_variable so
190
    # we can test
191
    for node in tree.findall(".//{%s}external_variable" % ovalns):
192
        print("External_variable with id : " + node.get("id"))
193
        extvar_id = node.get("id")
194
        # for envkey, envval in os.environ.iteritems():
195
        #     print envkey + " = " + envval
196
        # sys.exit()
197
        if extvar_id not in os.environ.keys():
198
            print("External_variable specified, but no value provided via "
199
                  "environment variable", file=sys.stderr)
200
            sys.exit(2)
201
        # replace tag name: external -> local
202
        node.tag = "{%s}local_variable" % ovalns
203
        literal = ET.Element("oval:literal_component")
204
        literal.text = os.environ[extvar_id]
205
        node.append(literal)
206
        # TODO: assignment of external_variable via environment vars, for
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
207
        # testing
208
    return tree
209
210
211 2
def find_testfile_or_exit(testfile):
212
    """Find OVAL files in CWD or shared/oval and calls sys.exit if the file is not found"""
213
    _testfile = find_testfile(testfile)
214
    if _testfile is None:
215
        print("ERROR: %s does not exist! Please specify a valid OVAL file." % testfile,
216
              file=sys.stderr)
217
        sys.exit(1)
218
    else:
219
        return _testfile
220
221
222 2
def find_testfile(oval_id):
223
    """
224
    Find OVAL file by id in CWD, SHARED_OVAL, or LINUX_OS_GUIDE. Understands rule
225
    directories and defaults to returning shared.xml over {{{ product }}}.xml.
226
227
    Returns path to OVAL file or None if not found.
228
    """
229 2
    if os.path.isfile(os.path.abspath(oval_id)):
230
        return os.path.abspath(oval_id)
231
232 2
    if oval_id.endswith(".xml"):
233 2
        oval_id, _ = os.path.splitext(oval_id)
234 2
        oval_id = os.path.basename(oval_id)
235
236 2
    candidates = [oval_id, "%s.xml" % oval_id]
237
238 2
    found_file = None
239 2
    for path in ['.', SHARED_OVAL, LINUX_OS_GUIDE]:
240 2
        for root, _, _ in os.walk(path):
241 2
            for candidate in candidates:
242 2
                search_file = os.path.join(root, candidate).strip()
243 2
                if os.path.isfile(search_file):
244
                    found_file = search_file
245
                    break
246
247 2
        for rule_dir in find_rule_dirs(path):
248 2
            rule_id = get_rule_dir_id(rule_dir)
249 2
            if rule_id == oval_id:
250 2
                ovals = get_rule_dir_ovals(rule_dir, product="shared")
251 2
                if ovals:
252 2
                    found_file = ovals[0]
253 2
                    break
254
255 2
    return found_file
256
257
258 2
def read_ovaldefgroup_file(testfile):
259
    """Read oval files"""
260 2
    with open(testfile, 'r') as test_file:
261 2
        body = test_file.read()
262 2
    return body
263
264
265 2
def get_openscap_supported_oval_version():
0 ignored issues
show
Coding Style Naming introduced by
The name get_openscap_supported_oval_version does not conform to the function naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
266
    try:
267
        from openscap import oscap_get_version
268
        if [int(x) for x in str(oscap_get_version()).split(".")] >= [1, 2, 0]:
269
            return "5.11"
270
    except ImportError:
271
        pass
272
273
    return "5.10"
274
275
276 2
def parse_options():
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
277
    usage = "usage: %(prog)s [options] definition_file.xml"
278
    parser = argparse.ArgumentParser(usage=usage)
279
    # only some options are on by default
280
281
    oscap_oval_version = get_openscap_supported_oval_version()
282
283
    parser.add_argument("--oval_version",
284
                        default=oscap_oval_version,
285
                        dest="oval_version", action="store",
286
                        help="OVAL version to use. Example: 5.11, 5.10, ... "
287
                             "If not supplied the highest version supported by "
288
                             "openscap will be used: %s" % (oscap_oval_version))
289
    parser.add_argument("-q", "--quiet", "--silent", default=False,
290
                        action="store_true", dest="silent_mode",
291
                        help="Don't show any output when testing OVAL files")
292
    parser.add_argument("xmlfile", metavar="XMLFILE", help="OVAL XML file")
293
    args = parser.parse_args()
294
295
    return args
296
297
298 2
def main():
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
299
    global definitions
0 ignored issues
show
Coding Style Naming introduced by
The name definitions does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
300
    global tests
0 ignored issues
show
Coding Style Naming introduced by
The name tests does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
301
    global objects
0 ignored issues
show
Coding Style Naming introduced by
The name objects does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
302
    global states
0 ignored issues
show
Coding Style Naming introduced by
The name states does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
303
    global variables
0 ignored issues
show
Coding Style Naming introduced by
The name variables does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
304
    global silent_mode
0 ignored issues
show
Coding Style Naming introduced by
The name silent_mode does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
305
306
    args = parse_options()
307
    silent_mode = args.silent_mode
308
    oval_version = args.oval_version
309
310
    testfile = args.xmlfile
311
    header = oval_generated_header("testoval.py", oval_version, "0.0.1")
312
    testfile = find_testfile_or_exit(testfile)
313
    body = read_ovaldefgroup_file(testfile)
314
315
    defname = _add_elements(body, header)
316
    if defname is None:
317
        print("Error while evaluating oval: defname not set; missing "
318
              "definitions section?")
319
        sys.exit(1)
320
321
    ovaltree = ET.fromstring(header + footer)
322
323
    # append each major element type, if it has subelements
324
    for element in [definitions, tests, objects, states, variables]:
325
        if list(element):
326
            ovaltree.append(element)
327
328
    # re-map all the element ids from meaningful names to meaningless
329
    # numbers
330
    testtranslator = IDTranslator("scap-security-guide.testing")
331
    ovaltree = testtranslator.translate(ovaltree)
332
    (ovalfile, fname) = tempfile.mkstemp(prefix=defname, suffix=".xml")
333
    os.write(ovalfile, ET.tostring(ovaltree))
334
    os.close(ovalfile)
335
336
    cmd = ['oscap', 'oval', 'eval', '--results', fname + '-results', fname]
337
    if not silent_mode:
338
        print("Evaluating with OVAL tempfile: " + fname)
339
        print("OVAL Schema Version: %s" % oval_version)
340
        print("Writing results to: " + fname + "-results")
341
        print("Running command: %s\n" % " ".join(cmd))
342
343
    oscap_child = subprocess.Popen(cmd, stdout=subprocess.PIPE)
344
    cmd_out = oscap_child.communicate()[0]
345
346
    if isinstance(cmd_out, bytes):
347
        cmd_out = cmd_out.decode('utf-8')
348
349
    if not silent_mode:
350
        print(cmd_out, file=sys.stderr)
351
352
    if oscap_child.returncode != 0:
353
        if not silent_mode:
354
            print("Error launching 'oscap' command: return code %d" % oscap_child.returncode)
355
        sys.exit(2)
356
357
    if 'false' in cmd_out or 'error' in cmd_out:
358
        # at least one from the evaluated OVAL definitions evaluated to
359
        # 'false' result, exit with '1' to indicate OVAL scan FAIL result
360
        sys.exit(1)
361
362
    # perhaps delete tempfile?
363
    definitions = ET.Element("oval:definitions")
364
    tests = ET.Element("oval:tests")
365
    objects = ET.Element("oval:objects")
366
    states = ET.Element("oval:states")
367
    variables = ET.Element("oval:variables")
368
369
    # 'false' keyword wasn't found in oscap's command output
370
    # exit with '0' to indicate OVAL scan TRUE result
371
    sys.exit(0)
372