ssg/oval_object_model/oval_document.py
from __future__ import absolute_import
import platform
import logging
from ..constants import OVAL_NAMESPACES, timestamp, xsi_namespace
from ..utils import required_key
from ..xml import ElementTree
from .oval_container import OVALContainer
from .oval_shorthand import OVALShorthand
from .oval_definition_references import OVALDefinitionReference
def _get_xml_el(tag_name, xml_el):
el = xml_el.find("./{%s}%s" % (OVAL_NAMESPACES.definition, tag_name))
return el if el is not None else ElementTree.Element("empty-element")
def _load_definitions(oval_document, oval_document_xml_el):
for definition_el in _get_xml_el("definitions", oval_document_xml_el):
oval_document.load_definition(definition_el)
def _load_tests(oval_document, oval_document_xml_el):
for test_el in _get_xml_el("tests", oval_document_xml_el):
oval_document.load_test(test_el)
def _load_objects(oval_document, oval_document_xml_el):
for object_el in _get_xml_el("objects", oval_document_xml_el):
oval_document.load_object(object_el)
def _load_states(oval_document, oval_document_xml_el):
for state_el in _get_xml_el("states", oval_document_xml_el):
oval_document.load_state(state_el)
def _load_variables(oval_document, oval_document_xml_el):
for variable_el in _get_xml_el("variables", oval_document_xml_el):
oval_document.load_variable(variable_el)
def load_oval_document(oval_document_xml_el):
generator_el = oval_document_xml_el.find(
"./{%s}generator" % OVAL_NAMESPACES.definition
)
product_name = generator_el.find("./{%s}product_name" % OVAL_NAMESPACES.oval)
schema_version = generator_el.find("./{%s}schema_version" % OVAL_NAMESPACES.oval)
product_version = generator_el.find("./{%s}product_version" % OVAL_NAMESPACES.oval)
oval_document = OVALDocument()
oval_document.product_version = product_version.text
oval_document.schema_version = schema_version.text
oval_document.product_name = product_name.text
_load_definitions(oval_document, oval_document_xml_el)
_load_tests(oval_document, oval_document_xml_el)
_load_objects(oval_document, oval_document_xml_el)
_load_states(oval_document, oval_document_xml_el)
_load_variables(oval_document, oval_document_xml_el)
return oval_document
def selection_of_oval_components_generator(component_dict, id_selection):
for component_id, component in component_dict.items():
if component_id not in id_selection:
continue
yield component_id, component
class MissingOVALComponent(Exception):
pass
class OVALDocument(OVALContainer):
schema_version = "5.11"
__product_name = "OVAL Object Model from SCAP Security Guide"
product_version = ""
__ssg_version = ""
@property
def ssg_version(self):
return self.__ssg_version
@ssg_version.setter
def ssg_version(self, __value):
self.__ssg_version = __value
self.product_version = "ssg: {}, python: {}".format(
self.__ssg_version, platform.python_version()
)
@property
def product_name(self):
return self.__product_name
@product_name.setter
def product_name(self, __value):
if "from SCAP Security Guide" in __value:
self.__product_name = __value
return
self.__product_name = "{} from SCAP Security Guide".format(__value)
@staticmethod
def _skip_if_is_none(value, component_id):
if value is None:
raise MissingOVALComponent(component_id)
return False
def load_shorthand(self, xml_string, product=None, rule_id=None):
shorthand = OVALShorthand()
shorthand.load_shorthand(xml_string)
is_valid = shorthand.validate(product, rule_id)
if is_valid:
self.add_content_of_container(shorthand)
return is_valid
def finalize_affected_platforms(self, env_yaml):
"""
Depending on your use-case of OVAL you may not need the <affected>
element. Such use-cases including using OVAL as a check engine for XCCDF
benchmarks. Since the XCCDF Benchmarks use cpe:platform with CPE IDs,
the affected element in OVAL definitions is redundant and just bloats the
files. This function removes all *irrelevant* affected platform elements
from given OVAL tree. It then adds one platform of the product we are
building.
"""
type_ = required_key(env_yaml, "type")
full_name = required_key(env_yaml, "full_name")
for definition in self.definitions.values():
definition.metadata.finalize_affected_platforms(type_, full_name)
def validate_references(self):
ref = OVALDefinitionReference()
ref.save_definitions(self.definitions)
try:
self._process_definition_references(ref)
self._process_test_references(ref)
self._process_objects_states_variables_references(ref)
except MissingOVALComponent as error:
logging.warning("Missing OVAL component: {}".format(error))
return False
return True
def get_xml_element(self, oval_definition_references=None):
definitions_selection = self.definitions.keys()
tests_selection = self.tests.keys()
objects_selection = self.objects.keys()
states_selection = self.states.keys()
variables_selection = self.variables.keys()
if oval_definition_references is not None:
definitions_selection = oval_definition_references.definitions
tests_selection = oval_definition_references.tests
objects_selection = oval_definition_references.objects
states_selection = oval_definition_references.states
variables_selection = oval_definition_references.variables
root = self._get_oval_definition_el()
root.append(self._get_generator_el())
if definitions_selection:
root.append(
self._get_component_el(
"definitions", self.definitions, definitions_selection)
)
if tests_selection:
root.append(
self._get_component_el(
"tests", self.tests, tests_selection)
)
if objects_selection:
root.append(
self._get_component_el(
"objects", self.objects, objects_selection)
)
if states_selection:
root.append(
self._get_component_el(
"states", self.states, states_selection)
)
if variables_selection:
root.append(
self._get_component_el(
"variables", self.variables, variables_selection)
)
return root
def is_empty(self):
return all([
len(self.definitions) == 0,
len(self.tests) == 0,
len(self.objects) == 0,
])
def save_as_xml(self, fd, oval_definition_references=None):
root = self.get_xml_element(oval_definition_references)
if hasattr(ElementTree, "indent"):
ElementTree.indent(root, space=" ", level=0)
ElementTree.ElementTree(root).write(fd, xml_declaration=True, encoding="utf-8")
def _get_component_el(self, tag, component_dict, id_selection):
xml_el = ElementTree.Element("{%s}%s" % (OVAL_NAMESPACES.definition, tag))
for _, component in selection_of_oval_components_generator(
component_dict, id_selection
):
xml_el.append(component.get_xml_element())
return xml_el
def _get_generator_el(self):
generator_el = ElementTree.Element("{%s}generator" % OVAL_NAMESPACES.definition)
product_name_el = ElementTree.Element("{%s}product_name" % OVAL_NAMESPACES.oval)
product_name_el.text = self.product_name
generator_el.append(product_name_el)
product_version_el = ElementTree.Element(
"{%s}product_version" % OVAL_NAMESPACES.oval
)
product_version_el.text = self.product_version
generator_el.append(product_version_el)
schema_version_el = ElementTree.Element(
"{%s}schema_version" % OVAL_NAMESPACES.oval
)
schema_version_el.text = self.schema_version
generator_el.append(schema_version_el)
timestamp_el = ElementTree.Element("{%s}timestamp" % OVAL_NAMESPACES.oval)
timestamp_el.text = str(timestamp)
generator_el.append(timestamp_el)
return generator_el
def _get_oval_definition_el(self):
oval_definition_el = ElementTree.Element(
"{%s}oval_definitions" % OVAL_NAMESPACES.definition
)
oval_definition_el.set(
ElementTree.QName(xsi_namespace, "schemaLocation"),
(
"{0} oval-common-schema.xsd {1} oval-definitions-schema.xsd"
" {1}#independent independent-definitions-schema.xsd"
" {1}#unix unix-definitions-schema.xsd"
" {1}#linux linux-definitions-schema.xsd"
).format(
OVAL_NAMESPACES.oval,
OVAL_NAMESPACES.definition,
),
)
return oval_definition_el