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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
"""
Common functions for processing OVAL in SSG
"""
from __future__ import absolute_import
from __future__ import print_function
import sys
import os
from .constants import oval_footer as footer
from .constants import oval_namespace as ovalns
from .xml import ElementTree as ET
from .xml import oval_generated_header
from .jinja import process_file_with_macros
from .products import _get_implied_properties
ASSUMED_OVAL_VERSION_STRING = "5.11"
# globals, to make recursion easier in case we encounter extend_definition
try:
ET.register_namespace("oval", ovalns)
except AttributeError:
# Legacy Python 2.6 fix, see e.g.
# https://www.programcreek.com/python/example/57552/xml.etree.ElementTree._namespace_map
from xml.etree import ElementTree as ET
ET._namespace_map[ovalns] = "oval"
def applicable_platforms(oval_file, oval_version_string=None):
"""
Returns the applicable platforms for a given OVAL file.
This function processes an OVAL file to extract the platforms it applies to. It uses a
specified OVAL version string or a default version if none is provided. The function
constructs an XML tree from the OVAL file and extracts platform information from it.
Args:
oval_file (str): The path to the OVAL file to be processed.
oval_version_string (str, optional): The OVAL version string to be used.
If not provided, a default version is used.
Returns:
list: A list of platforms that the OVAL file applies to.
Raises:
Exception: If there is an error while parsing the OVAL file.
"""
platforms = []
if not oval_version_string:
oval_version_string = ASSUMED_OVAL_VERSION_STRING
header = oval_generated_header("applicable_platforms", oval_version_string, "0.0.1")
oval_version_list = [int(num) for num in oval_version_string.split(".")]
subst_dict = dict(target_oval_version=oval_version_list)
oval_filename_components = oval_file.split(os.path.sep)
if len(oval_filename_components) > 3:
subst_dict["rule_id"] = oval_filename_components[-3]
else:
msg = "Unable to get rule ID from OVAL path '{path}'".format(path=oval_file)
print(msg, file=sys.stderr)
subst_dict = _get_implied_properties(subst_dict)
subst_dict['target_oval_version'] = [999, 999.999]
body = process_file_with_macros(oval_file, subst_dict)
try:
oval_tree = ET.fromstring(header + body + footer)
except Exception as e:
msg = "Error while loading " + oval_file
print(msg, file=sys.stderr)
raise e
element_path = "./{%s}def-group/{%s}definition/{%s}metadata/{%s}affected/{%s}platform"
element_ns_path = element_path % (ovalns, ovalns, ovalns, ovalns, ovalns)
for node in oval_tree.findall(element_ns_path):
platforms.append(node.text)
return platforms
def parse_affected(oval_contents):
"""
Returns the tuple (start_affected, end_affected, platform_indents) for the passed OVAL file contents.
Args:
oval_contents (list of str): The contents of the OVAL file, where each element is a line from
the file.
Returns:
tuple: A tuple containing:
- start_affected (int): The line number of the starting <affected> tag.
- end_affected (int): The line number of the closing </affected> tag.
- platform_indents (str): The indenting characters before the contents of the
<affected> element.
Raises:
ValueError: If the OVAL file does not contain a single <affected> element, if the start
tag is after the end tag, or if the tags contain other elements.
"""
start_affected = list(filter(lambda x: "<affected" in oval_contents[x],
range(0, len(oval_contents))))
if len(start_affected) != 1:
raise ValueError("OVAL file does not contain a single <affected> "
"element; counted %d in:\n%s\n\n" %
(len(start_affected), "\n".join(oval_contents)))
start_affected = start_affected[0]
end_affected = list(filter(lambda x: "</affected" in oval_contents[x],
range(0, len(oval_contents))))
if len(end_affected) != 1:
raise ValueError("Malformed OVAL file does not contain a single "
"closing </affected>; counted %d in:\n%s\n\n" %
(len(start_affected), "\n".join(oval_contents)))
end_affected = end_affected[0]
if start_affected >= end_affected:
raise ValueError("Malformed OVAL file: start affected tag begins "
"on the same line or after ending affected tag: "
"start:%d vs end:%d:\n%s\n\n" %
(start_affected, end_affected, oval_contents))
# Validate that start_affected contains only a starting <affected> tag;
# otherwise, using this information to update the <platform> subelements
# would fail.
start_line = oval_contents[start_affected]
start_line = start_line.strip()
if not start_line.startswith('<affected'):
raise ValueError("Malformed OVAL file: line with starting affected "
"tag contains other elements: line:%s\n%s\n\n" %
(start_line, oval_contents))
if '<' in start_line[1:]:
raise ValueError("Malformed OVAL file: line with starting affected "
"tag contains other elements: line:%s\n%s\n\n" %
(start_line, oval_contents))
# Validate that end_affected contains only an ending </affected> tag;
# otherwise, using this information to update the <platform> subelements
# would fail.
end_line = oval_contents[end_affected]
end_line = end_line.strip()
if not end_line.startswith('</affected>'):
raise ValueError("Malformed OVAL file: line with ending affected "
"tag contains other elements: line:%s\n%s\n\n" %
(end_line, oval_contents))
if '<' in end_line[1:]:
raise ValueError("Malformed OVAL file: line with ending affected "
"tag contains other elements: line:%s\n%s\n\n" %
(end_line, oval_contents))
indents = ""
if start_affected+1 == end_affected:
# Since the affected element is present but empty, the indents should
# be two more spaces than that of the starting <affected> element.
start_index = oval_contents[start_affected].index('<')
indents = oval_contents[start_affected][0:start_index]
indents += " "
else:
# Otherwise, grab the indents off the next line unmodified, as this is
# likely a platform element tag. We don't validate here that this is
# indeed the case, as other parts of the build infrastructure will
# validate this for us.
start_index = oval_contents[start_affected+1].index('<')
indents = oval_contents[start_affected+1][0:start_index]
return start_affected, end_affected, indents
|