Skip to content

Commit

Permalink
Merge pull request ComplianceAsCode#11041 from Honny1/oval-object-model
Browse files Browse the repository at this point in the history
OVAL object model
  • Loading branch information
jan-cerny authored Sep 19, 2023
2 parents 65e2469 + 4202324 commit 9d151bc
Show file tree
Hide file tree
Showing 14 changed files with 1,704 additions and 2 deletions.
21 changes: 21 additions & 0 deletions ssg/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,24 @@
DEFAULT_CHRONY_CONF_PATH = '/etc/chrony.conf'
DEFAULT_AUDISP_CONF_PATH = '/etc/audit'
DEFAULT_SYSCTL_REMEDIATE_DROP_IN_FILE = 'false'


# Constants for OVAL object model
STR_TO_BOOL = {
"false": False,
"False": False,
"true": True,
"True": True,
}

BOOL_TO_STR = {True: "true", False: "false"}


class OvalNamespaces:
oval = "http://oval.mitre.org/XMLSchema/oval-common-5"
definition = oval_namespace
independent = "http://oval.mitre.org/XMLSchema/oval-definitions-5#independent"
linux = "http://oval.mitre.org/XMLSchema/oval-definitions-5#linux"


OVAL_NAMESPACES = OvalNamespaces()
19 changes: 19 additions & 0 deletions ssg/oval_object_model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from .general import (
ExceptionEmptyNote,
Notes,
OVALBaseObject,
OVALComponent,
OVALEntity,
OVALEntityProperty,
load_oval_entity_property,
load_notes,
)
from .oval_document import (
ExceptionDuplicateOVALEntity,
OVALDocument,
load_oval_document,
)
from .oval_entities import (
ExceptionDuplicateObjectReferenceInTest,
ExceptionMissingObjectReferenceInTest,
)
181 changes: 181 additions & 0 deletions ssg/oval_object_model/general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import re
from ..constants import BOOL_TO_STR, xsi_namespace
from ..xml import ElementTree


# ----- General functions


def required_attribute(_xml_el, _key):
if _key in _xml_el.attrib:
return _xml_el.get(_key)
raise ValueError(
"%s is required but was not found in:\n%s" % (_key, repr(_xml_el.attrib))
)


# ----- General Objects


class OVALBaseObject(object):
__namespace = ""
tag = ""

def __init__(self, tag):
match_ns = re.match(r"\{.*\}", tag)
self.namespace = match_ns.group(0) if match_ns else ""
self.tag = tag.replace(self.namespace, "")

@property
def namespace(self):
return self.__namespace

@namespace.setter
def namespace(self, __value):
if isinstance(__value, str):
if not __value.startswith("{"):
__value = "{" + __value
if not __value.endswith("}"):
__value = __value + "}"
self.__namespace = __value

def __eq__(self, __value):
return self.__dict__ == __value.__dict__

def __repr__(self):
return str(self.__dict__)

def __str__(self):
return str(self.__dict__)

def get_xml_element(self):
raise NotImplementedError


class OVALComponent(OVALBaseObject):
deprecated = False
notes = None
version = "0"

def __init__(self, tag, id_):
super(OVALComponent, self).__init__(tag)
self.id_ = id_

def get_xml_element(self):
el = ElementTree.Element("{}{}".format(self.namespace, self.tag))
el.set("id", self.id_)
el.set("version", self.version)
if self.deprecated:
el.set("deprecated", BOOL_TO_STR[self.deprecated])
if self.notes:
el.append(self.notes.get_xml_element())
return el


class OVALEntity(OVALComponent):
comment = ""

def __init__(self, tag, id_, properties):
super(OVALEntity, self).__init__(tag, id_)
self.properties = properties

def get_xml_element(self, **attributes):
el = super(OVALEntity, self).get_xml_element()

for key, value in attributes.items():
if "xsi" in key:
key = ElementTree.QName(xsi_namespace, key.split(":")[-1])
el.set(key, value)

if self.comment:
el.set("comment", self.comment)

for property_ in self.properties:
el.append(property_.get_xml_element())

return el


# ----- OVAL Objects


def load_notes(oval_notes_xml_el):
if oval_notes_xml_el is None:
return None
notes = []
for note_el in oval_notes_xml_el:
notes.append(note_el.text)
return Notes(oval_notes_xml_el.tag, note_el.tag, notes)


class ExceptionEmptyNote(Exception):
pass


class Notes(OVALBaseObject):
def __init__(self, tag, note_tag, notes):
super(Notes, self).__init__(tag)
self.note_tag = note_tag
if len(notes) == 0:
raise ExceptionEmptyNote(
"Element notes should contain at least one element note."
)
self.notes = notes

def get_xml_element(self):
notes_el = ElementTree.Element("{}{}".format(self.namespace, self.tag))
for note in self.notes:
note_el = ElementTree.Element(self.note_tag)
note_el.text = note
notes_el.append(note_el)
return notes_el


# -----


def load_property_and_notes_of_oval_entity(oval_entity_el):
notes = None
object_property = []
for child_node_el in oval_entity_el:
if "notes" in child_node_el.tag:
notes = load_notes(child_node_el)
else:
object_property.append(load_oval_entity_property(child_node_el))
return object_property, notes


def load_oval_entity_property(end_point_property_el):
data = OVALEntityProperty(end_point_property_el.tag)
data.attributes = (
end_point_property_el.attrib if end_point_property_el.attrib else None
)
data.text = end_point_property_el.text
for child_end_point_property_el in end_point_property_el:
data.add_child_property(load_oval_entity_property(child_end_point_property_el))
return data


class OVALEntityProperty(OVALBaseObject):
attributes = None
text = None

def __init__(self, tag):
super(OVALEntityProperty, self).__init__(tag)
self.properties = []

def add_child_property(self, property_):
self.properties.append(property_)

def get_xml_element(self):
property_el = ElementTree.Element("{}{}".format(self.namespace, self.tag))
for key, val in self.attributes.items() if self.attributes is not None else {}:
property_el.set(key, val)

if self.text is not None:
property_el.text = self.text

for child in self.properties:
property_el.append(child.get_xml_element())

return property_el
Loading

0 comments on commit 9d151bc

Please sign in to comment.