File: id_translate.py

package info (click to toggle)
scap-security-guide 0.1.76-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 110,644 kB
  • sloc: xml: 241,883; sh: 73,777; python: 32,527; makefile: 27
file content (204 lines) | stat: -rw-r--r-- 7,754 bytes parent folder | download
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""
Common functions for processing ID Translations in SSG
"""

from __future__ import absolute_import
from __future__ import print_function

from .xml import ElementTree
from .constants import oval_namespace as oval_ns
from .constants import ocil_namespace as ocil_ns
from .constants import OVALTAG_TO_ABBREV, OCILTAG_TO_ABBREV
from .constants import OVALREFATTR_TO_TAG, OCILREFATTR_TO_TAG


def _split_namespace(tag):
    """
    Splits an XML tag into its namespace and name components.

    Args:
        tag (str): The XML tag to split. If the tag contains a namespace, it should be in the
                   format '{namespace}name'.

    Returns:
        tuple: A tuple containing the namespace and the tag name. If the tag does not contain a
               namespace, the namespace will be None. Any fragment identifier in the namespace
               will be removed.
    """
    if tag[0] == "{":
        namespace, name = tag[1:].split("}", 1)
        return (namespace.split("#", 1)[0], name)

    return (None, tag)


def _namespace_to_prefix(tag):
    """
    Convert a namespace in a tag to its corresponding prefix.

    Args:
        tag (str): The tag containing the namespace to be converted.

    Returns:
        str: The prefix corresponding to the namespace.

    Raises:
        RuntimeError: If the namespace in the tag is unknown.

    """
    namespace, _ = _split_namespace(tag)
    if namespace == ocil_ns:
        return "ocil"
    if namespace == oval_ns:
        return "oval"

    raise RuntimeError(
        "Error: unknown checksystem referenced in tag : %s" % tag
    )


def _tagname_to_abbrev(tag):
    """
    Convert a tag name to its abbreviated form based on its namespace.

    Args:
        tag (str): The tag name to be converted, which may include a namespace.

    Returns:
        str: The abbreviated form of the tag name.

    Raises:
        RuntimeError: If the tag's namespace is unknown.

    Notes:
        - If the tag is "extend_definition", it is returned as is.
        - The tag name is split by the last underscore to determine its type.
        - The namespace is used to look up the abbreviation in the corresponding dictionary.
    """
    namespace, tag = _split_namespace(tag)
    if tag == "extend_definition":
        return tag
    # grab the last part of the tag name to determine its type
    tag = tag.rsplit("_", 1)[-1]
    if namespace == ocil_ns:
        return OCILTAG_TO_ABBREV[tag]
    if namespace == oval_ns:
        return OVALTAG_TO_ABBREV[tag]

    raise RuntimeError(
        "Error: unknown checksystem referenced in tag : %s" % tag
    )


class IDTranslator(object):
    """
    IDTranslator is a class designed to handle the mapping of meaningful, human-readable names to
    IDs in the formats required by the SCAP checking systems, such as OVAL and OCIL.

    Attributes:
        content_id (str): The content identifier used in generating IDs.
    """
    def __init__(self, content_id):
        self.content_id = content_id

    def generate_id(self, tagname, name):
        """
        Generates a unique identifier string based on the provided tag name and name.

        Args:
            tagname (str): The tag name to be used in the identifier.
            name (str): The name to be used in the identifier.

        Returns:
            str: A unique identifier string in the format
                 "<namespace_prefix>:<content_id>-<name>:<tagname_abbrev>:1".
        """
        return "%s:%s-%s:%s:1" % (
            _namespace_to_prefix(tagname),
            self.content_id, name,
            _tagname_to_abbrev(tagname)
        )

    def translate(self, tree, store_defname=False):
        """
        Translates the IDs of elements in an XML tree to new identifiers.

        Args:
            tree (ElementTree.Element): The XML tree to be processed.
            store_defname (bool, optional): If True, stores the old name in the metadata for OVAL
                                            definitions. Defaults to False.

        Returns:
            ElementTree.Element: The processed XML tree with updated IDs.

        The function iterates through each element in the provided XML tree and performs the
        following actions based on the element's tag and attributes:
        - If the element has an "id" attribute, it generates a new ID and sets it.
        - If `store_defname` is True and the element is an OVAL definition, it stores the old ID
          in the metadata.
        - For specific tags like "filter", "var_ref", and "object_reference", it updates the text
          content with a new ID.
        - For attributes that match keys in `OVALREFATTR_TO_TAG` or `OCILREFATTR_TO_TAG`, it
          updates the attribute value with a new ID.
        - For the "test_action_ref" tag, it updates the text content with a new ID.
        """
        for element in tree.iter():
            idname = element.get("id")
            if idname:
                # store the old name if requested (for OVAL definitions)
                if store_defname and \
                        element.tag == "{%s}definition" % oval_ns:
                    metadata = element.find("{%s}metadata" % oval_ns)
                    if metadata is None:
                        metadata = ElementTree.SubElement(element, "metadata")
                    defnam = ElementTree.Element(
                        "{%s}reference" % oval_ns, ref_id=idname, source=self.content_id)
                    metadata.append(defnam)

                # set the element to the new identifier
                element.set("id", self.generate_id(element.tag, idname))
                # continue
            if element.tag == "{%s}filter" % oval_ns:
                element.text = self.generate_id("{%s}state" % oval_ns,
                                                element.text)
                continue
            if element.tag == "{%s#independent}var_ref" % oval_ns:
                element.text = self.generate_id("{%s}variable" % oval_ns,
                                                element.text)
                continue
            if element.tag == "{%s}object_reference" % oval_ns:
                element.text = self.generate_id("{%s}object" % oval_ns,
                                                element.text)
                continue
            for attr in element.keys():
                if attr in OVALREFATTR_TO_TAG.keys():
                    element.set(attr, self.generate_id(
                        "{%s}%s" % (oval_ns, OVALREFATTR_TO_TAG[attr]),
                        element.get(attr)))
                if attr in OCILREFATTR_TO_TAG.keys():
                    element.set(attr, self.generate_id(
                        "{%s}%s" % (ocil_ns, OCILREFATTR_TO_TAG[attr]),
                        element.get(attr)))
            if element.tag == "{%s}test_action_ref" % ocil_ns:
                element.text = self.generate_id("{%s}action" % ocil_ns,
                                                element.text)

        return tree

    def translate_oval_document(self, oval_document, store_defname=False):
        """
        Translates and validates an OVAL document.

        This method translates the IDs in the given OVAL document and validates its references.

        Args:
            oval_document: The OVAL document to be translated and validated.
            store_defname (bool, optional): If True, stores the definition name during
                                            translation. Defaults to False.

        Returns:
            The translated and validated OVAL document.
        """
        oval_document.translate_id(self, store_defname)
        oval_document.validate_references()
        return oval_document