1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
#!/usr/bin/env python2
'''
count_oval_objects.py
Shows OVAL objects used by XCCDF rules.
Author: Jan Cerny <jcerny@redhat.com>
'''
import xml.etree.ElementTree as ET
import sys
import os.path
oval_files = dict()
xccdf_dir = None
help_text = '''Shows OVAL objects used by XCCDF rules.
Usage: ./count_oval_objects.py xccdf_file.xml'''
def get_args():
''' Parses program arguments. '''
if len(sys.argv) == 2:
if sys.argv[1] == "--help" or sys.argv[1] == "-h":
print(help_text)
exit(0)
else:
return sys.argv[1]
else:
sys.stderr.write("Bad argument. For more information, try --help.\n")
exit(-1)
def load_xml(file_name):
''' Loads XML files to memory and parses it into element tree '''
try:
it = ET.iterparse(file_name)
for _, el in it:
el.tag = el.tag.split('}', 1)[1] # strip all namespaces
root = it.root
return root
except:
sys.stderr.write("Error while loading file " + file_name + ".\n")
exit(-1)
def find_oval_objects(oval_refs):
''' Finds OVAL objects according to definitions ID '''
tests = []
object_refs = []
objects = []
# find tests in definitions
for def_id, oval_file in oval_refs:
if oval_file not in oval_files:
oval_file_path = os.path.join(xccdf_dir, oval_file)
oval_files[oval_file] = load_xml(oval_file_path)
oval_root = oval_files[oval_file]
definition = None
for d in oval_root.findall(".//definition"):
if d.attrib.get('id') == def_id:
definition = d
break
if definition is not None:
for criterion in definition.findall(".//criterion"):
test_ref = criterion.attrib["test_ref"]
tests.append(test_ref)
# find references to objects in tests
for test in tests:
test_element = None
for t in oval_root.findall("tests/*"):
if t.attrib.get('id') == test:
test_element = t
break
if test_element is not None:
for object_element in test_element.findall(".//*"):
if 'object_ref' in object_element.attrib:
object_ref = object_element.attrib['object_ref']
object_refs.append(object_ref)
# find objects
for r in object_refs:
for obj in oval_root.findall("objects/*"):
if obj.attrib.get('id') == r:
objects.append(obj.tag)
break
return set(objects)
def print_stats(stats):
''' Print statistic of most used objects in input'''
print("")
print("Count of used OVAL objects:")
print("=" * 50)
stats = stats.items()
for key, value in reversed(sorted(stats, key=lambda obj: obj[1])):
print(key.ljust(40) + str(value).rjust(10))
def main():
stats = {}
global xccdf_dir
xccdf_file_name = get_args()
xccdf_root = load_xml(xccdf_file_name)
xccdf_dir = os.path.dirname(xccdf_file_name)
for rule in xccdf_root.findall(".//Rule"):
rule_id = rule.attrib['id']
oval_refs = []
for ref in rule.findall(".//check-content-ref"):
# Skip remotely referenced OVAL checks since they won't have the
# 'name' attribute set (just 'href' would be set in that case)
try:
oval_name = ref.attrib['name']
except KeyError:
if 'href' in ref.attrib:
print("\nInfo: Skipping remotely referenced OVAL:")
continue
else:
print("\nError: Invalid OVAL check detected! Exiting..")
sys.exit(1)
oval_file = ref.attrib['href']
oval_refs.append((oval_name, oval_file))
if oval_refs:
objects = find_oval_objects(oval_refs)
print(rule_id + ": " + ", ".join(objects))
for o in objects:
stats[o] = stats.get(o, 0) + 1
else:
print(rule_id + ":")
print_stats(stats)
if __name__ == "__main__":
main()
|