Test Failed
Pull Request — master (#3170)
by Matěj
02:13
created

ssg.oval.replace_external_vars()   A

Complexity

Conditions 3

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 9.933

Importance

Changes 0
Metric Value
cc 3
eloc 13
nop 1
dl 0
loc 24
ccs 1
cts 12
cp 0.0833
crap 9.933
rs 9.75
c 0
b 0
f 0
1 1
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 1
from __future__ import print_function
3
4 1
import sys
5 1
import os
6 1
import re
7 1
import argparse
8 1
import tempfile
9 1
import subprocess
10
11 1
from .constants import oval_footer as footer
12 1
from .constants import oval_namespace as ovalns
13 1
from .xml import ElementTree as ET
14 1
from .xml import oval_generated_header
15 1
from .id_translate import IDTranslator
16
17 1
SHARED_OVAL = re.sub(r'ssg/.*', 'shared', __file__) + '/checks/oval/'
18
19
20
# globals, to make recursion easier in case we encounter extend_definition
21 1
definitions = ET.Element("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...
22 1
tests = ET.Element("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...
23 1
objects = ET.Element("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...
24 1
states = ET.Element("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...
25 1
variables = ET.Element("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...
26 1
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...
27
28
29
# append new child ONLY if it's not a duplicate
30 1
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...
31
    global silent_mode
0 ignored issues
show
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...
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...
32
    newid = newchild.get("id")
33
    existing = element.find(".//*[@id='" + newid + "']")
34
    if existing is not None:
35
        if not silent_mode:
36
            sys.stderr.write("Notification: this ID is used more than once " +
37
                             "and should represent equivalent elements: " +
38
                             newid + "\n")
39
    else:
40
        element.append(newchild)
41
42
43 1
def _add_elements(body, header):
44
    """Add oval elements to the global Elements defined above"""
45
    global definitions
0 ignored issues
show
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...
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...
46
    global tests
0 ignored issues
show
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...
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...
47
    global objects
0 ignored issues
show
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...
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...
48
    global states
0 ignored issues
show
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...
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...
49
    global variables
0 ignored issues
show
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...
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...
50
51
    tree = ET.fromstring(header + body + footer)
52
    tree = replace_external_vars(tree)
53
    defname = None
54
    # parse new file(string) as an etree, so we can arrange elements
55
    # appropriately
56
    for childnode in tree.findall("./{%s}def-group/*" % ovalns):
57
        # print "childnode.tag is " + childnode.tag
58
        if childnode.tag is ET.Comment:
59
            continue
60
        if childnode.tag == ("{%s}definition" % ovalns):
61
            append(definitions, childnode)
62
            defname = childnode.get("id")
63
            # extend_definition is a special case:  must include a whole other
64
            # definition
65
            for defchild in childnode.findall(".//{%s}extend_definition"
66
                                              % ovalns):
67
                defid = defchild.get("definition_ref")
68
                extend_ref = find_testfile_or_exit(defid+".xml")
69
                includedbody = read_ovaldefgroup_file(extend_ref)
70
                # recursively add the elements in the other file
71
                _add_elements(includedbody, header)
72
        if childnode.tag.endswith("_test"):
73
            append(tests, childnode)
74
        if childnode.tag.endswith("_object"):
75
            append(objects, childnode)
76
        if childnode.tag.endswith("_state"):
77
            append(states, childnode)
78
        if childnode.tag.endswith("_variable"):
79
            append(variables, childnode)
80
    return defname
81
82
83 1
def replace_external_vars(tree):
84
    """Replace external_variables with local_variables, so the definition can be
85
       tested independently of an XCCDF file"""
86
87
    # external_variable is a special case: we turn it into a local_variable so
88
    # we can test
89
    for node in tree.findall(".//{%s}external_variable" % ovalns):
90
        print("External_variable with id : " + node.get("id"))
91
        extvar_id = node.get("id")
92
        # for envkey, envval in os.environ.iteritems():
93
        #     print envkey + " = " + envval
94
        # sys.exit()
95
        if extvar_id not in os.environ.keys():
96
            print("External_variable specified, but no value provided via "
97
                  "environment variable", file=sys.stderr)
98
            sys.exit(2)
99
        # replace tag name: external -> local
100
        node.tag = "{%s}local_variable" % ovalns
101
        literal = ET.Element("literal_component")
102
        literal.text = os.environ[extvar_id]
103
        node.append(literal)
104
        # 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...
105
        # testing
106
    return tree
107
108
109 1
def find_testfile_or_exit(testfile):
110
    """Find OVAL files in CWD or shared/oval and calls sys.exit if the file is not found"""
111
    testfile = find_testfile(testfile)
112
    if testfile is None:
113
        print(
114
            "ERROR: {0} does not exist! Please specify a valid OVAL file."
115
            .format(testfile), file=sys.stderr)
116
        sys.exit(1)
117
    else:
118
        return testfile
119
120
121 1
def find_testfile(testfile):
122
    """Find OVAL files in CWD or shared/oval and returns None if the file is not found"""
123
    found_file = None
124
    for path in ['.', SHARED_OVAL]:
125
        for root, _, _ in os.walk(path):
126
            searchfile = os.path.join(root, testfile).strip()
127
            if os.path.isfile(searchfile):
128
                found_file = searchfile
129
                break
130
131
    return found_file
132
133
134 1
def read_ovaldefgroup_file(testfile):
135
    """Read oval files"""
136
    with open(testfile, 'r') as test_file:
137
        body = test_file.read()
138
    return body
139
140
141 1
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...
142
    try:
143
        from openscap import oscap_get_version
144
        if [int(x) for x in str(oscap_get_version()).split(".")] >= [1, 2, 0]:
145
            return "5.11"
146
    except ImportError:
147
        pass
148
149
    return "5.10"
150
151
152 1
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...
153
    usage = "usage: %(prog)s [options] definition_file.xml"
154
    parser = argparse.ArgumentParser(usage=usage)
155
    # only some options are on by default
156
157
    oscap_oval_version = get_openscap_supported_oval_version()
158
159
    parser.add_argument("--oval_version",
160
                        default=oscap_oval_version,
161
                        dest="oval_version", action="store",
162
                        help="OVAL version to use. Example: 5.11, 5.10, ... "
163
                             "If not supplied the highest version supported by "
164
                             "openscap will be used: %s" % (oscap_oval_version))
165
    parser.add_argument("-q", "--quiet", "--silent", default=False,
166
                        action="store_true", dest="silent_mode",
167
                        help="Don't show any output when testing OVAL files")
168
    parser.add_argument("xmlfile", metavar="XMLFILE", help="OVAL XML file")
169
    args = parser.parse_args()
170
171
    return args
172
173
174 1
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...
175
    global definitions
0 ignored issues
show
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...
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...
176
    global tests
0 ignored issues
show
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...
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...
177
    global objects
0 ignored issues
show
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...
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...
178
    global states
0 ignored issues
show
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...
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...
179
    global variables
0 ignored issues
show
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...
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...
180
    global silent_mode
0 ignored issues
show
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...
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...
181
182
    args = parse_options()
183
    silent_mode = args.silent_mode
184
    oval_version = args.oval_version
185
186
    testfile = args.xmlfile
187
    header = oval_generated_header("testoval.py", oval_version, "0.0.1")
188
    testfile = find_testfile_or_exit(testfile)
189
    body = read_ovaldefgroup_file(testfile)
190
    defname = _add_elements(body, header)
191
    if defname is None:
192
        print("Error while evaluating oval: defname not set; missing "
193
              "definitions section?")
194
        sys.exit(1)
195
196
    ovaltree = ET.fromstring(header + footer)
197
198
    # append each major element type, if it has subelements
199
    for element in [definitions, tests, objects, states, variables]:
200
        if list(element) > 0:
201
            ovaltree.append(element)
202
    # re-map all the element ids from meaningful names to meaningless
203
    # numbers
204
    testtranslator = IDTranslator("scap-security-guide.testing")
205
    ovaltree = testtranslator.translate(ovaltree)
206
    (ovalfile, fname) = tempfile.mkstemp(prefix=defname, suffix=".xml")
207
    os.write(ovalfile, ET.tostring(ovaltree))
208
    os.close(ovalfile)
209
    if not silent_mode:
210
        print("Evaluating with OVAL tempfile: " + fname)
211
        print("OVAL Schema Version: %s" % oval_version)
212
        print("Writing results to: " + fname + "-results")
213
    cmd = "oscap oval eval --results " + fname + "-results " + fname
214
    oscap_child = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
215
    cmd_out = oscap_child.communicate()[0]
216
    if not silent_mode:
217
        print(cmd_out)
218
    if oscap_child.returncode != 0:
219
        if not silent_mode:
220
            print("Error launching 'oscap' command: \n\t" + cmd)
221
        sys.exit(2)
222
    if 'false' in cmd_out:
223
        # at least one from the evaluated OVAL definitions evaluated to
224
        # 'false' result, exit with '1' to indicate OVAL scan FAIL result
225
        sys.exit(1)
226
    # perhaps delete tempfile?
227
    definitions = ET.Element("definitions")
228
    tests = ET.Element("tests")
229
    objects = ET.Element("objects")
230
    states = ET.Element("states")
231
    variables = ET.Element("variables")
232
233
    # 'false' keyword wasn't found in oscap's command output
234
    # exit with '0' to indicate OVAL scan TRUE result
235
    sys.exit(0)
236