-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathproduct_tree.py
163 lines (129 loc) · 6.13 KB
/
product_tree.py
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
""" Module containing ProductTree class """
import logging
from ..common.common import SectionHandler
# pylint: disable=too-few-public-methods
class ProductTree(SectionHandler):
""" Responsible for converting the ProductTree section:
- /cvrf:cvrfdoc/prod:ProductTree
"""
branch_type_mapping = {
"Vendor": 'vendor',
"Product Family": 'product_family',
"Product Name": 'product_name',
"Product Version": 'product_version',
"Patch Level": 'patch_level',
"Service Pack": 'service_pack',
"Architecture": 'architecture',
"Language": 'language',
"Legacy": 'legacy',
"Specification": 'specification',
"Host Name": 'host_name',
# These types do not exist in CSAF, agreed to convert it to product_name
# https://github.com/csaf-tools/CVRF-CSAF-Converter/pull/54#discussion_r805860658
"Realm": 'product_name',
"Resource": 'product_name',
}
relation_type_mapping = {
'Default Component Of': 'default_component_of',
'Optional Component Of': 'optional_component_of',
'External Component Of': 'external_component_of',
'Installed On': 'installed_on',
'Installed With': 'installed_with',
}
# pylint: disable=useless-super-delegation
def __init__(self):
super().__init__()
def _process_mandatory_elements(self, root_element):
""" There are no mandatory elements in the ProductTree section """
def _process_optional_elements(self, root_element):
self._handle_full_product_names(root_element)
self._handle_relationships(root_element)
self._handle_product_groups(root_element)
branches = self._handle_branches_recursive(root_element)
if branches is not None:
self.csaf['branches'] = branches
@staticmethod
def _get_full_product_name(fpn_elem) -> dict:
fpn = {
'product_id': fpn_elem.attrib['ProductID'],
'name': fpn_elem.text
}
if fpn_elem.attrib.get('CPE'):
fpn['product_identification_helper'] = {'cpe': fpn_elem.attrib['CPE']}
return fpn
@classmethod
def _get_branch_type(cls, branch_type: str):
if branch_type in ['Realm', 'Resource']:
logging.warning('Input branch type %s is no longer supported in CSAF. '
'Converting to product_name', branch_type)
return cls.branch_type_mapping[branch_type]
def _handle_full_product_names(self, root_element):
if not hasattr(root_element, 'FullProductName'):
return
full_product_names = []
for fpn_elem in root_element.FullProductName:
full_product_names.append(self._get_full_product_name(fpn_elem))
self.csaf['full_product_names'] = full_product_names
def _handle_relationships(self, root_element):
if not hasattr(root_element, 'Relationship'):
return
relationships = []
for rel_elem in root_element.Relationship:
first_prod_name = rel_elem.FullProductName[0]
if len(rel_elem.FullProductName) > 1:
# To be compliant with 9.1.5 Conformance Clause 5: CVRF CSAF converter
# https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html
logging.warning('Input line %s: Relationship contains more '
'FullProductNames. Taking only the first one, since CSAF expects '
'only 1 value here', rel_elem.sourceline)
rel_to_add = {
'category': self.relation_type_mapping[rel_elem.attrib['RelationType']],
'product_reference': rel_elem.attrib['ProductReference'],
'relates_to_product_reference': rel_elem.attrib['RelatesToProductReference'],
'full_product_name': self._get_full_product_name(first_prod_name)
}
relationships.append(rel_to_add)
self.csaf['relationships'] = relationships
def _handle_product_groups(self, root_element):
if not hasattr(root_element, 'ProductGroups'):
return
product_groups = []
for pg_elem in root_element.ProductGroups.Group:
product_ids = [x.text for x in pg_elem.ProductID]
pg_to_add = {
'group_id': pg_elem.attrib['GroupID'],
'product_ids': product_ids,
}
if hasattr(pg_elem, 'Description'):
pg_to_add['summary'] = pg_elem.Description.text
product_groups.append(pg_to_add)
self.csaf['product_groups'] = product_groups
def _handle_branches_recursive(self, root_element):
""" Recursive method for handling the branches,
branch can have either list of another branches, or a single FullProductName inside
"""
if not hasattr(root_element, 'Branch') and not hasattr(root_element, 'FullProductName'):
# The ProductTree section doesn't contain Branches at all
return None
if 'Branch' in root_element.tag and hasattr(root_element, 'FullProductName'):
# Make sure we are inside a Branch (and not in the top ProductTree element,
# where FullProductName can occur) then root_element is the leaf branch
leaf_branch = {
'name': root_element.attrib['Name'],
'category': self._get_branch_type(root_element.attrib['Type']),
'product': self._get_full_product_name(root_element.FullProductName)
}
return leaf_branch
if hasattr(root_element, 'Branch'):
branches = []
for branch_elem in root_element.Branch:
if hasattr(branch_elem, 'FullProductName'):
branches.append(self._handle_branches_recursive(branch_elem))
else:
branches.append({
'name': branch_elem.attrib['Name'],
'category': self._get_branch_type(branch_elem.attrib['Type']),
'branches': self._handle_branches_recursive(branch_elem)
})
return branches
return None