Passed
Push — master ( ea7c4a...d61422 )
by Matěj
01:18 queued 12s
created

count_oval_objects.main()   B

Complexity

Conditions 7

Size

Total Lines 36
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 28
nop 0
dl 0
loc 36
ccs 0
cts 27
cp 0
crap 56
rs 7.808
c 0
b 0
f 0
1
#!/usr/bin/env python2
2
3
'''
4
count_oval_objects.py
5
6
Shows OVAL objects used by XCCDF rules.
7
8
Author: Jan Cerny <[email protected]>
9
'''
10
11
import argparse
12
import xml.etree.ElementTree as ET
13
import sys
14
import os.path
15
16
oval_files = dict()
17
xccdf_dir = None
18
19
20
def parse_args():
21
    parser = argparse.ArgumentParser(description="Show OVAL objects used by XCCDF rules.")
22
    parser.add_argument("xccdf_file", help="Path to the XCCDF file to parse")
23
    return parser.parse_args()
24
25
26
def load_xml(file_name):
27
    ''' Loads XML files to memory and parses it into element tree '''
28
    try:
29
        it = ET.iterparse(file_name)
30
        for _, el in it:
31
            el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
32
        root = it.root
33
        return root
34
    except:
35
        sys.stderr.write("Error while loading file " + file_name + ".\n")
36
        exit(-1)
37
38
39
def find_oval_objects(oval_refs):
40
    ''' Finds OVAL objects according to definitions ID '''
41
    tests = []
42
    object_refs = []
43
    objects = []
44
45
    # find tests in definitions
46
    for def_id, oval_file in oval_refs:
47
        if oval_file not in oval_files:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable oval_files does not seem to be defined.
Loading history...
48
            oval_file_path = os.path.join(xccdf_dir, oval_file)
49
            oval_files[oval_file] = load_xml(oval_file_path)
50
        oval_root = oval_files[oval_file]
51
        definition = None
52
        for d in oval_root.findall(".//definition"):
53
            if d.attrib.get('id') == def_id:
54
                definition = d
55
                break
56
        if definition is not None:
57
            for criterion in definition.findall(".//criterion"):
58
                test_ref = criterion.attrib["test_ref"]
59
                tests.append(test_ref)
60
61
    # find references to objects in tests
62
    for test in tests:
63
        test_element = None
64
        for t in oval_root.findall("tests/*"):
0 ignored issues
show
introduced by
The variable oval_root does not seem to be defined in case the for loop on line 46 is not entered. Are you sure this can never be the case?
Loading history...
65
            if t.attrib.get('id') == test:
66
                test_element = t
67
                break
68
        if test_element is not None:
69
            for object_element in test_element.findall(".//*"):
70
                if 'object_ref' in object_element.attrib:
71
                    object_ref = object_element.attrib['object_ref']
72
                    object_refs.append(object_ref)
73
74
    # find objects
75
    for r in object_refs:
76
        for obj in oval_root.findall("objects/*"):
77
            if obj.attrib.get('id') == r:
78
                objects.append(obj.tag)
79
                break
80
81
    return set(objects)
82
83
84
def print_stats(stats):
85
    ''' Print statistic of most used objects in input'''
86
    print("")
87
    print("Count of used OVAL objects:")
88
    print("=" * 50)
89
    stats = stats.items()
90
    for key, value in reversed(sorted(stats, key=lambda obj: obj[1])):
91
        print(key.ljust(40) + str(value).rjust(10))
92
93
94
def main():
95
    stats = {}
96
    global xccdf_dir
97
98
    args = parse_args()
99
    xccdf_file_name = args.xccdf_file
100
    xccdf_root = load_xml(xccdf_file_name)
101
    xccdf_dir = os.path.dirname(xccdf_file_name)
102
103
    for rule in xccdf_root.findall(".//Rule"):
104
        rule_id = rule.attrib['id']
105
        oval_refs = []
106
        for ref in rule.findall(".//check-content-ref"):
107
108
            # Skip remotely referenced OVAL checks since they won't have the
109
            # 'name' attribute set (just 'href' would be set in that case)
110
            try:
111
                oval_name = ref.attrib['name']
112
            except KeyError:
113
                if 'href' in ref.attrib:
114
                    print("\nInfo: Skipping remotely referenced OVAL:")
115
                    continue
116
                else:
117
                    print("\nError: Invalid OVAL check detected! Exiting..")
118
                    sys.exit(1)
119
120
            oval_file = ref.attrib['href']
121
            oval_refs.append((oval_name, oval_file))
122
        if oval_refs:
123
            objects = find_oval_objects(oval_refs)
124
            print(rule_id + ": " + ", ".join(objects))
125
            for o in objects:
126
                stats[o] = stats.get(o, 0) + 1
127
        else:
128
            print(rule_id + ":")
129
    print_stats(stats)
130
131
132
if __name__ == "__main__":
133
    main()
134