Test Failed
Push — master ( 864b32...5d5197 )
by Matěj
01:12 queued 13s
created

oval_tester   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 153
Duplicated Lines 9.8 %

Importance

Changes 0
Metric Value
wmc 25
eloc 124
dl 15
loc 153
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A OVALTester._evaluate_oval() 0 9 1
A OVALTester._create_config_file() 0 6 3
C OVALTester._expand_shorthand() 15 50 9
A OVALTester._create_oval() 0 8 2
A OVALTester._get_result() 0 8 3
A OVALTester.finish() 0 5 2
A OVALTester.__init__() 0 4 1
A OVALTester.test() 0 31 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
#!/usr/bin/python3
2
3
from __future__ import print_function
4
5
import os
6
import re
7
import shutil
8
import subprocess
9
import sys
10
import tempfile
11
12
import ssg.build_ovals
13
import ssg.constants
14
import ssg.jinja
15
import ssg.oval
16
import ssg.rules
17
import ssg.utils
18
import ssg.yaml
19
import ssg.build_yaml
20
import ssg.rule_yaml
21
import ssg.id_translate
22
23
24
class OVALTester(object):
25
    def __init__(self, verbose):
26
        self.mock_env_yaml = {"rule_id": "test"}
27
        self.result = True
28
        self.verbose = verbose
29
30
    def _expand_shorthand(self, shorthand_path, oval_path, env_yaml):
31
        shorthand_file_content = ssg.jinja.process_file_with_macros(
32
            shorthand_path, env_yaml)
33
        wrapped_shorthand = (ssg.constants.oval_header +
34
                             shorthand_file_content +
35
                             ssg.constants.oval_footer)
36
        shorthand_tree = ssg.xml.ElementTree.fromstring(
37
            wrapped_shorthand.encode("utf-8"))
38
39
        definitions = ssg.xml.ElementTree.Element(
40
            "{%s}definitions" % ssg.constants.oval_namespace)
41
        tests = ssg.xml.ElementTree.Element(
42
            "{%s}tests" % ssg.constants.oval_namespace)
43
        objects = ssg.xml.ElementTree.Element(
44
            "{%s}objects" % ssg.constants.oval_namespace)
45
        states = ssg.xml.ElementTree.Element(
46
            "{%s}states" % ssg.constants.oval_namespace)
47
        variables = ssg.xml.ElementTree.Element(
48
            "{%s}variables" % ssg.constants.oval_namespace)
49
        for childnode in shorthand_tree.findall(".//{%s}def-group/*" %
50
                                                ssg.constants.oval_namespace):
51 View Code Duplication
            if childnode.tag is ssg.xml.ElementTree.Comment:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
52
                continue
53
            elif childnode.tag.endswith("definition"):
54
                ssg.build_ovals.append(definitions, childnode)
55
            elif childnode.tag.endswith("_test"):
56
                ssg.build_ovals.append(tests, childnode)
57
            elif childnode.tag.endswith("_object"):
58
                ssg.build_ovals.append(objects, childnode)
59
            elif childnode.tag.endswith("_state"):
60
                ssg.build_ovals.append(states, childnode)
61
            elif childnode.tag.endswith("_variable"):
62
                ssg.build_ovals.append(variables, childnode)
63
            else:
64
                sys.stderr.write(
65
                    "Warning: Unknown element '%s'\n" % (childnode.tag))
66
67
        header = ssg.xml.oval_generated_header("test", "5.11", "1.0")
68
        skeleton = header + ssg.constants.oval_footer
69
        root = ssg.xml.ElementTree.fromstring(skeleton.encode("utf-8"))
70
        root.append(definitions)
71
        root.append(tests)
72
        root.append(objects)
73
        root.append(states)
74
        if list(variables):
75
            root.append(variables)
76
        id_translator = ssg.id_translate.IDTranslator("test")
77
        root_translated = id_translator.translate(root)
78
79
        ssg.xml.ElementTree.ElementTree(root_translated).write(oval_path)
80
81
    def _get_result(self, oscap_output):
82
        pattern = re.compile(
83
            r"^Definition oval:[A-Za-z0-9_\-\.]+:def:[1-9][0-9]*: (\w+)$")
84
        for line in oscap_output.splitlines():
85
            matched = pattern.match(line)
86
            if matched:
87
                return matched.group(1)
88
        return None
89
90
    def _create_config_file(self, config_file_content, tmp_dir):
91
        config_file_path = os.path.join(tmp_dir, "config")
92
        if config_file_content:
93
            with open(config_file_path, "w") as f:
94
                f.write(config_file_content)
95
        return config_file_path
96
97
    def _create_oval(self, oval_content, config_file_path, tmp_dir):
98
        oval_content = oval_content.replace("CONFIG_FILE", config_file_path)
99
        shorthand_path = os.path.join(tmp_dir, "shorthand")
100
        with open(shorthand_path, "w") as f:
101
            f.write(oval_content)
102
        oval_path = os.path.join(tmp_dir, "oval.xml")
103
        self._expand_shorthand(shorthand_path, oval_path, self.mock_env_yaml)
104
        return oval_path
105
106
    def _evaluate_oval(self, oval_path):
107
        results_path = oval_path + ".results.xml"
108
        oscap_command = [
109
            "oscap", "oval", "eval", "--results", results_path, oval_path]
110
        oscap_process = subprocess.Popen(
111
            oscap_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
112
        oscap_stdout, oscap_stderr = oscap_process.communicate()
113
        def_result = self._get_result(oscap_stdout.decode("utf-8"))
114
        return def_result, results_path
115
116
    def test(self, description, oval_content, config_file_content,
117
             expected_result):
118
        """
119
        Execute a test.
120
        description: a very short description to be displayed in test output
121
        oval_content: content of the OVAL shorthand file written in a way you
122
                      write OVALs in SSG rules (not a valid OVAL)
123
        config_file_content: content of the text configuration file that the
124
                             OVAL will check
125
        expected_result: expected result of evaluation of the OVAL definition
126
        """
127
        try:
128
            tmp_dir = tempfile.mkdtemp()
129
            config_file_path = self._create_config_file(
130
                config_file_content, tmp_dir)
131
            oval_path = self._create_oval(
132
                oval_content, config_file_path, tmp_dir)
133
            result, results_path = self._evaluate_oval(oval_path)
134
            msg = ("OVAL Definition was evaluated as %s, but expected result "
135
                   "is %s.") % (result, expected_result)
136
            assert result == expected_result, msg
137
            if self.verbose:
138
                print("Test: %s: PASS" % (description))
139
            self.result = self.result and True
140
        except AssertionError as e:
141
            if self.verbose:
142
                print("Test: %s: FAIL" % (description))
143
                print("    " + str(e))
144
            self.result = False
145
        finally:
146
            shutil.rmtree(tmp_dir)
147
148
    def finish(self):
149
        """
150
        Exit test with an appropriate return code.
151
        """
152
        sys.exit(0 if self.result else 1)
153