Merge ~ebarretto/ubuntu-cve-tracker:oval-kernel-improv into ubuntu-cve-tracker:master
- Git
- lp:~ebarretto/ubuntu-cve-tracker
- oval-kernel-improv
- Merge into master
Proposed by
Eduardo Barretto
Status: | Merged |
---|---|
Merged at revision: | 91d5528b3c45c766108d7edb6be9367c72f97b47 |
Proposed branch: | ~ebarretto/ubuntu-cve-tracker:oval-kernel-improv |
Merge into: | ubuntu-cve-tracker:master |
Diff against target: |
776 lines (+279/-235) 1 file modified
scripts/oval_lib.py (+279/-235) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
David Fernandez Gonzalez | Approve | ||
Review via email: mp+444569@code.launchpad.net |
Commit message
Description of the change
This PR tries to simplify the kernel check and make it more in line to other package checks where the oval object is pointing to the kernel "uname" and the state is pointing to the fixed version. The way it was before, the object would point to the fixed version and the state was retrieving the kernel uname.
Also fixed some issues with OCI-based kernel regex.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/scripts/oval_lib.py b/scripts/oval_lib.py |
2 | index 2b3d79f..ed64318 100644 |
3 | --- a/scripts/oval_lib.py |
4 | +++ b/scripts/oval_lib.py |
5 | @@ -109,9 +109,9 @@ def process_kernel_binaries(binaries, oval_format): |
6 | version = ''.join(values) |
7 | values = sorted(set(map(lambda x: x[1], parts))) |
8 | flavours = '|'.join(values) |
9 | - regex = version + '\d+(' + flavours + ')' |
10 | + regex = version + '\d+(' + flavours + ')' |
11 | if oval_format == 'oci': |
12 | - regex = 'linux-image-(?:unsigned-)?' + regex |
13 | + regex = 'linux-image-(?:unsigned-)?' + version + '\d+(?:' + flavours + ')' |
14 | return regex |
15 | |
16 | return None |
17 | @@ -457,8 +457,13 @@ class CVE: |
18 | |
19 | def add_pkg(self, pkg_object, release, state, note): |
20 | cve_pkg_entry = CVEPkgRelEntry(pkg_object, release, self, state, note) |
21 | + |
22 | + if cve_pkg_entry.status in ['not-vulnerable', 'not-applicable']: |
23 | + return |
24 | + |
25 | self.pkg_rel_entries[pkg_object.name] = cve_pkg_entry |
26 | self.pkgs.append(pkg_object) |
27 | + pkg_object.cves.append(self) |
28 | |
29 | def __str__(self) -> str: |
30 | return self.number |
31 | @@ -531,9 +536,7 @@ class OvalGeneratorPkg(OvalGenerator): |
32 | version = etree.SubElement(advisory, "current_version") |
33 | |
34 | for cve in package.cves: |
35 | - if cve.pkg_rel_entries[package.name].status == 'not-vulnerable': |
36 | - continue |
37 | - elif self.fixed_only and cve.pkg_rel_entries[package.name].status != 'fixed': |
38 | + if self.fixed_only and cve.pkg_rel_entries[package.name].status != 'fixed': |
39 | continue |
40 | cve_obj = self._generate_cve_object(cve) |
41 | advisory.append(cve_obj) |
42 | @@ -739,20 +742,17 @@ class OvalGeneratorPkg(OvalGenerator): |
43 | def _add_running_kernel_checks(self, root_element): |
44 | objects = root_element.find("objects") |
45 | variables = root_element.find("variables") |
46 | - states = root_element.find("states") |
47 | |
48 | variable_local_kernel_check = self._generate_local_variable_kernel(self.definition_id, "Kernel version in evr format", self.definition_id) |
49 | obj_running_kernel = self._generate_uname_object_element(self.definition_id) |
50 | - state_kernel_version = self._generate_state_kernel_element("Kernel check", self.definition_id, self.definition_id) |
51 | |
52 | objects.append(obj_running_kernel) |
53 | variables.append(variable_local_kernel_check) |
54 | - states.append(state_kernel_version) |
55 | |
56 | def _generate_local_variable_kernel(self, id, comment, uname_obj_id): |
57 | var = etree.Element("local_variable", |
58 | attrib={ |
59 | - 'id' : f"{self.ns}:var:{id}", |
60 | + 'id': f"{self.ns}:var:{id}", |
61 | 'version': "1", |
62 | "datatype": "debian_evr_string", |
63 | "comment": comment |
64 | @@ -798,20 +798,6 @@ class OvalGeneratorPkg(OvalGenerator): |
65 | |
66 | return object |
67 | |
68 | - def _generate_variable_kernel_version(self, comment, id, version): |
69 | - var = etree.Element("constant_variable", |
70 | - attrib={ |
71 | - 'id' : f"{self.ns}:var:{id}", |
72 | - 'version': "1", |
73 | - "datatype": "debian_evr_string", |
74 | - "comment": comment |
75 | - }) |
76 | - |
77 | - item = etree.SubElement(var, "value") |
78 | - item.text = f"0:{version.rsplit('.', 1)[0]}" |
79 | - |
80 | - return var |
81 | - |
82 | def _generate_test_element_running_kernel(self, id, comment, obj_id): |
83 | test = etree.Element("unix-def:uname_test", attrib={ |
84 | "id": f"{self.ns}:tst:{id}", |
85 | @@ -846,7 +832,13 @@ class OvalGeneratorPkg(OvalGenerator): |
86 | |
87 | return object |
88 | |
89 | - def _generate_state_kernel_element(self, comment, id, var_id) -> None: |
90 | + def _generate_state_kernel_element(self, comment, id, version) -> None: |
91 | + patched = re.search('([\d|\.]+-\d+)[\.|\d]+', version) |
92 | + if patched: |
93 | + patched = patched.group(1) |
94 | + else: |
95 | + patched = version |
96 | + |
97 | state = etree.Element("ind-def:variable_state", |
98 | attrib={ |
99 | 'id' : f"{self.ns}:ste:{id}", |
100 | @@ -854,13 +846,12 @@ class OvalGeneratorPkg(OvalGenerator): |
101 | "comment": comment |
102 | }) |
103 | |
104 | - etree.SubElement(state, "ind-def:value", attrib={ |
105 | + value = etree.SubElement(state, "ind-def:value", attrib={ |
106 | "datatype": "debian_evr_string", |
107 | - "operation": "greater than", |
108 | - "var_check": "at least one", |
109 | - "var_ref": f"{self.ns}:var:{var_id}" |
110 | + "operation": "less than", |
111 | }) |
112 | |
113 | + value.text = f"0:{patched}" |
114 | return state |
115 | |
116 | def _generate_kernel_package_elements(self, package: Package, root_element, running_kernel_check_id) -> etree.Element: |
117 | @@ -884,23 +875,29 @@ class OvalGeneratorPkg(OvalGenerator): |
118 | def _add_fixed_kernel_elements(self, cve: CVE, package: Package, package_rel_entry:CVEPkgRelEntry, root_element, running_kernel_id, fixed_versions) -> etree.Element: |
119 | tests = root_element.find("tests") |
120 | objects = root_element.find("objects") |
121 | - variables = root_element.find("variables") |
122 | + states = root_element.find("states") |
123 | |
124 | - comment_version = f'Kernel {package.name} version comparison ({package_rel_entry.fixed_version})' |
125 | + comment_version = f'Kernel {package.name} version comparison' |
126 | comment_criterion = f'({cve.number}) {package.name} {package_rel_entry.note}' |
127 | |
128 | if package_rel_entry.fixed_version in fixed_versions: |
129 | criterion_version = self._generate_criterion_element(comment_criterion, fixed_versions[package_rel_entry.fixed_version]) |
130 | else: |
131 | - criterion_version = self._generate_criterion_element(comment_criterion, self.definition_id) |
132 | - test_kernel_version = self._generate_test_element(comment_version, self.definition_id, True, 'kernel', state_id=running_kernel_id) |
133 | + create_state = False |
134 | + |
135 | + if package_rel_entry.fixed_version: |
136 | + create_state = True |
137 | + ste_kernel_version = self._generate_state_kernel_element("Kernel check", self.definition_id, package_rel_entry.fixed_version) |
138 | + states.append(ste_kernel_version) |
139 | |
140 | - obj_kernel_version = self._generate_kernel_version_object_element(self.definition_id, self.definition_id) |
141 | - var_version_kernel = self._generate_variable_kernel_version(comment_version, self.definition_id, package_rel_entry.fixed_version) |
142 | + obj_kernel_version = self._generate_kernel_version_object_element(self.definition_id, running_kernel_id) |
143 | + |
144 | + test_kernel_version = self._generate_test_element(comment_version, self.definition_id, create_state, 'kernel', self.definition_id) |
145 | + |
146 | + criterion_version = self._generate_criterion_element(comment_criterion, self.definition_id) |
147 | |
148 | tests.append(test_kernel_version) |
149 | objects.append(obj_kernel_version) |
150 | - variables.append(var_version_kernel) |
151 | |
152 | fixed_versions[package_rel_entry.fixed_version] = self.definition_id |
153 | |
154 | @@ -941,17 +938,19 @@ class OvalGeneratorPkg(OvalGenerator): |
155 | if not obj_id: |
156 | object = self._generate_object_object(object_note, self.definition_id, self.definition_id) |
157 | |
158 | - binaries = package.binaries |
159 | + bins = package.binaries |
160 | + if is_kernel_binaries(package.binaries): |
161 | + regex = process_kernel_binaries(package.binaries, 'oci') |
162 | + bins = [f'{regex}'] |
163 | + |
164 | + binaries = [] |
165 | if self.oval_format == 'oci': |
166 | - if is_kernel_binaries(package.binaries): |
167 | - regex = process_kernel_binaries(package.binaries, 'oci') |
168 | - binaries = [f'^{regex}(?::\w+|)\s+(.*)$\s+(.*)'] |
169 | - else: |
170 | - variable_values = '(?::\w+|)\s+(.*)$\s+(.*)' |
171 | + variable_values = '(?::\w+|)\s+(.*)$' |
172 | + for binary in bins: |
173 | + binaries.append(f'^{binary}{variable_values}') |
174 | + else: |
175 | + binaries = bins |
176 | |
177 | - binaries = [] |
178 | - for binary in package.binaries: |
179 | - binaries.append(f'^{binary}{variable_values}') |
180 | var = self._generate_var_object(object_note, self.definition_id, binaries) |
181 | else: |
182 | object = None |
183 | @@ -972,9 +971,9 @@ class OvalGeneratorPkg(OvalGenerator): |
184 | if self.oval_format == 'oci': |
185 | if is_kernel_binaries(package.binaries): |
186 | regex = process_kernel_binaries(package.binaries, 'oci') |
187 | - binaries = [f'^{regex}(?::\w+|)\s+(.*)$\s+(.*)'] |
188 | + binaries = [f'^{regex}(?::\w+|)\s+(.*)$'] |
189 | else: |
190 | - variable_values = '(?::\w+|)\s+(.*)$\s+(.*)' |
191 | + variable_values = '(?::\w+|)\s+(.*)$' |
192 | |
193 | binaries = [] |
194 | for binary in package.binaries: |
195 | @@ -1049,18 +1048,12 @@ class OvalGeneratorPkg(OvalGenerator): |
196 | self._increase_id(is_definition=True) |
197 | |
198 | def _populate_kernel_pkg(self, package, root_element, running_kernel_id): |
199 | - tests = root_element.find("tests") |
200 | - objects = root_element.find("objects") |
201 | - variables = root_element.find("variables") |
202 | - |
203 | # Add package definition |
204 | definitions = root_element.find("definitions") |
205 | definition_element = self._generate_definition_object(package) |
206 | |
207 | # Control/cache variables |
208 | - one_time_added_id = None |
209 | fixed_versions = {} |
210 | - binaries_id = None |
211 | cve_added = False |
212 | |
213 | # Generate one-time elements |
214 | @@ -1072,29 +1065,11 @@ class OvalGeneratorPkg(OvalGenerator): |
215 | |
216 | for cve in package.cves: |
217 | pkg_rel_entry = cve.pkg_rel_entries[package.name] |
218 | - if pkg_rel_entry.status == 'vulnerable' and not self.fixed_only: |
219 | - cve_added = True |
220 | - if one_time_added_id: |
221 | - self._add_criterion(one_time_added_id, pkg_rel_entry, cve, definition_element, depth=3) |
222 | - else: |
223 | - self._add_criterion(self.definition_id, pkg_rel_entry, cve, definition_element, depth=3) |
224 | - |
225 | - test, object, var = self._generate_vulnerable_elements(package, binaries_id) |
226 | - tests.append(test) |
227 | - objects.append(object) |
228 | - |
229 | - if not binaries_id: |
230 | - variables.append(var) |
231 | - binaries_id = self.definition_id |
232 | - |
233 | - one_time_added_id = self.definition_id |
234 | - self._increase_id(is_definition=False) |
235 | - elif pkg_rel_entry.status == 'fixed': |
236 | - cve_added = True |
237 | + cve_added = True |
238 | |
239 | - kernel_version_criterion = self._add_fixed_kernel_elements(cve, package, pkg_rel_entry, root_element, running_kernel_id, fixed_versions) |
240 | - self._add_to_criteria(definition_element, kernel_version_criterion, depth=3) |
241 | - self._increase_id(is_definition=False) |
242 | + kernel_version_criterion = self._add_fixed_kernel_elements(cve, package, pkg_rel_entry, root_element, running_kernel_id, fixed_versions) |
243 | + self._add_to_criteria(definition_element, kernel_version_criterion, depth=3) |
244 | + self._increase_id(is_definition=False) |
245 | |
246 | if cve_added: |
247 | definitions.append(definition_element) |
248 | @@ -1111,9 +1086,6 @@ class OvalGeneratorPkg(OvalGenerator): |
249 | packages[package_name] = pkg_obj |
250 | |
251 | pkg_obj = packages[package_name] |
252 | - if cve not in pkg_obj.cves: |
253 | - pkg_obj.cves.append(cve) |
254 | - |
255 | cve.add_pkg(pkg_obj, release, cve_data['pkgs'][package_name][release][0],cve_data['pkgs'][package_name][release][1]) |
256 | |
257 | def _load_pkgs(self, cve_prefix_dir, packages_filter=None) -> None: |
258 | @@ -1262,7 +1234,7 @@ class OvalGeneratorCVE: |
259 | 'id_base': id_base + len(test_refs), |
260 | 'source-note': header['Source-note'] |
261 | } |
262 | - if is_kernel_binaries(pkg['binaries']) and pkg['fix-version']: |
263 | + if is_kernel_binaries(pkg['binaries']): |
264 | test_ref = self.get_running_kernel_testref(pkg) |
265 | if test_ref: |
266 | test_refs = test_refs + test_ref |
267 | @@ -1324,7 +1296,7 @@ class OvalGeneratorCVE: |
268 | ' <criterion test_ref="{0}" comment="{1}" {2}/>'.format( |
269 | test_ref['id'], |
270 | escape(test_ref['comment']), negation_attribute)) |
271 | - criteria.append(' </criteria>') |
272 | + criteria.append(' </criteria>') |
273 | else: |
274 | negation_attribute = 'negate = "true" ' \ |
275 | if 'negate' in test_ref and test_ref['negate'] else '' |
276 | @@ -1401,45 +1373,63 @@ class OvalGeneratorCVE: |
277 | </criteria> |
278 | </definition>\n""".format(**mapping)) |
279 | |
280 | - def get_running_kernel_testref(self, package): |
281 | - uname_regex = process_kernel_binaries(package['binaries'], self.oval_format) |
282 | - if uname_regex: |
283 | - if self.oval_format == 'dpkg': |
284 | - (var_id, var_id_2) = self.get_running_kernel_variable_id( |
285 | - uname_regex, |
286 | - package['id_base'], |
287 | - package['fix-version']) |
288 | - (ste_id, ste_id_2) = self.get_running_kernel_state_id( |
289 | - uname_regex, |
290 | - package['id_base'], |
291 | - var_id) |
292 | - (obj_id, obj_id_2) = self.get_running_kernel_object_id( |
293 | - package['id_base'], var_id_2) |
294 | - (test_id, test_id_2) = self.get_running_kernel_test_id( |
295 | - uname_regex, package['id_base'], package['name'], |
296 | - obj_id, ste_id, obj_id_2, ste_id_2) |
297 | - return [{'id': test_id, |
298 | - 'comment': 'Is kernel {0} running'.format(package['name']), |
299 | - 'kernel': uname_regex, 'var_id': var_id}, |
300 | - {'id': test_id_2, 'comment': 'kernel version comparison', |
301 | - 'kernelobj': True}] |
302 | - else: # OCI |
303 | - object_id = self.get_package_object_id(package['name'], |
304 | - [uname_regex], |
305 | - package['id_base']) |
306 | - state_id = self.get_package_version_state_id(package['id_base'], |
307 | - package['fix-version']) |
308 | - test_title = "Does the '{0}' package exist and is the version less than '{1}'?".format(package['name'], |
309 | - package['fix-version']) |
310 | - test_id = self.get_package_test_id(package['name'], |
311 | - package['id_base'], |
312 | - test_title, |
313 | - object_id, |
314 | - state_id) |
315 | - package['note'] = package['name'] + package['note'] |
316 | - return [{'id': test_id, 'comment': package['note']}] |
317 | + # TODO: xml lib |
318 | + def add_release_applicability_definition(self): |
319 | + """ add platform/release applicability OVAL definition for codename """ |
320 | + |
321 | + mapping = { |
322 | + 'ns': self.ns, |
323 | + 'id_base': self.id, |
324 | + 'codename': cve_lib.product_series(self.release)[1], |
325 | + 'release_name': self.release_name, |
326 | + } |
327 | + self.release_applicability_definition_id = \ |
328 | + '{ns}:def:{id_base}0'.format(**mapping) |
329 | + |
330 | + if self.oval_format == 'dpkg': |
331 | + self.queue_element('definition', """ |
332 | + <definition class="inventory" id="{ns}:def:{id_base}0" version="1"> |
333 | + <metadata> |
334 | + <title>Check that {release_name} ({codename}) is installed.</title> |
335 | + <description></description> |
336 | + </metadata> |
337 | + <criteria> |
338 | + <criterion test_ref="{ns}:tst:{id_base}0" comment="The host is part of the unix family." /> |
339 | + <criterion test_ref="{ns}:tst:{id_base}1" comment="The host is running Ubuntu {codename}." /> |
340 | + </criteria> |
341 | + </definition>\n""".format(**mapping)) |
342 | + |
343 | + self.queue_element('test', """ |
344 | + <ind-def:family_test id="{ns}:tst:{id_base}0" check="at least one" check_existence="at_least_one_exists" version="1" comment="Is the host part of the unix family?"> |
345 | + <ind-def:object object_ref="{ns}:obj:{id_base}0"/> |
346 | + <ind-def:state state_ref="{ns}:ste:{id_base}0"/> |
347 | + </ind-def:family_test> |
348 | + |
349 | + <ind-def:textfilecontent54_test id="{ns}:tst:{id_base}1" check="at least one" check_existence="at_least_one_exists" version="1" comment="Is the host running Ubuntu {codename}?"> |
350 | + <ind-def:object object_ref="{ns}:obj:{id_base}1"/> |
351 | + <ind-def:state state_ref="{ns}:ste:{id_base}1"/> |
352 | + </ind-def:textfilecontent54_test>\n""".format(**mapping)) |
353 | + |
354 | + # /etc/lsb-release has to be a single path, due to some |
355 | + # environments (namely snaps) not being allowed to list the |
356 | + # content of /etc/ |
357 | + self.queue_element('object', """ |
358 | + <ind-def:family_object id="{ns}:obj:{id_base}0" version="1" comment="The singleton family object."/> |
359 | + |
360 | + <ind-def:textfilecontent54_object id="{ns}:obj:{id_base}1" version="1" comment="The singleton release codename object."> |
361 | + <ind-def:filepath>/etc/lsb-release</ind-def:filepath> |
362 | + <ind-def:pattern operation="pattern match">^[\\s\\S]*DISTRIB_CODENAME=([a-z]+)$</ind-def:pattern> |
363 | + <ind-def:instance datatype="int">1</ind-def:instance> |
364 | + </ind-def:textfilecontent54_object>\n""".format(**mapping)) |
365 | + |
366 | + self.queue_element('state', """ |
367 | + <ind-def:family_state id="{ns}:ste:{id_base}0" version="1" comment="The singleton family object."> |
368 | + <ind-def:family>unix</ind-def:family> |
369 | + </ind-def:family_state> |
370 | |
371 | - return None |
372 | + <ind-def:textfilecontent54_state id="{ns}:ste:{id_base}1" version="1" comment="{release_name}"> |
373 | + <ind-def:subexpression>{codename}</ind-def:subexpression> |
374 | + </ind-def:textfilecontent54_state>\n""".format(**mapping)) |
375 | |
376 | def get_oval_test_for_package(self, package): |
377 | """ create OVAL test and dependent objects for this package status |
378 | @@ -1504,64 +1494,6 @@ class OvalGeneratorCVE: |
379 | return {'id': self.id_unknown_test, 'comment': package['note']} |
380 | |
381 | # TODO: xml lib |
382 | - def add_release_applicability_definition(self): |
383 | - """ add platform/release applicability OVAL definition for codename """ |
384 | - |
385 | - mapping = { |
386 | - 'ns': self.ns, |
387 | - 'id_base': self.id, |
388 | - 'codename': cve_lib.product_series(self.release)[1], |
389 | - 'release_name': self.release_name, |
390 | - } |
391 | - self.release_applicability_definition_id = \ |
392 | - '{ns}:def:{id_base}0'.format(**mapping) |
393 | - |
394 | - if self.oval_format == 'dpkg': |
395 | - self.queue_element('definition', """ |
396 | - <definition class="inventory" id="{ns}:def:{id_base}0" version="1"> |
397 | - <metadata> |
398 | - <title>Check that {release_name} ({codename}) is installed.</title> |
399 | - <description></description> |
400 | - </metadata> |
401 | - <criteria> |
402 | - <criterion test_ref="{ns}:tst:{id_base}0" comment="The host is part of the unix family." /> |
403 | - <criterion test_ref="{ns}:tst:{id_base}1" comment="The host is running Ubuntu {codename}." /> |
404 | - </criteria> |
405 | - </definition>\n""".format(**mapping)) |
406 | - |
407 | - self.queue_element('test', """ |
408 | - <ind-def:family_test id="{ns}:tst:{id_base}0" check="at least one" check_existence="at_least_one_exists" version="1" comment="Is the host part of the unix family?"> |
409 | - <ind-def:object object_ref="{ns}:obj:{id_base}0"/> |
410 | - <ind-def:state state_ref="{ns}:ste:{id_base}0"/> |
411 | - </ind-def:family_test> |
412 | - |
413 | - <ind-def:textfilecontent54_test id="{ns}:tst:{id_base}1" check="at least one" check_existence="at_least_one_exists" version="1" comment="Is the host running Ubuntu {codename}?"> |
414 | - <ind-def:object object_ref="{ns}:obj:{id_base}1"/> |
415 | - <ind-def:state state_ref="{ns}:ste:{id_base}1"/> |
416 | - </ind-def:textfilecontent54_test>\n""".format(**mapping)) |
417 | - |
418 | - # /etc/lsb-release has to be a single path, due to some |
419 | - # environments (namely snaps) not being allowed to list the |
420 | - # content of /etc/ |
421 | - self.queue_element('object', """ |
422 | - <ind-def:family_object id="{ns}:obj:{id_base}0" version="1" comment="The singleton family object."/> |
423 | - |
424 | - <ind-def:textfilecontent54_object id="{ns}:obj:{id_base}1" version="1" comment="The singleton release codename object."> |
425 | - <ind-def:filepath>/etc/lsb-release</ind-def:filepath> |
426 | - <ind-def:pattern operation="pattern match">^[\\s\\S]*DISTRIB_CODENAME=([a-z]+)$</ind-def:pattern> |
427 | - <ind-def:instance datatype="int">1</ind-def:instance> |
428 | - </ind-def:textfilecontent54_object>\n""".format(**mapping)) |
429 | - |
430 | - self.queue_element('state', """ |
431 | - <ind-def:family_state id="{ns}:ste:{id_base}0" version="1" comment="The singleton family object."> |
432 | - <ind-def:family>unix</ind-def:family> |
433 | - </ind-def:family_state> |
434 | - |
435 | - <ind-def:textfilecontent54_state id="{ns}:ste:{id_base}1" version="1" comment="{release_name}"> |
436 | - <ind-def:subexpression>{codename}</ind-def:subexpression> |
437 | - </ind-def:textfilecontent54_state>\n""".format(**mapping)) |
438 | - |
439 | - # TODO: xml lib |
440 | def get_package_object_id(self, name, bin_pkgs, id_base, version=1): |
441 | """ create unique object for each package and return its OVAL id """ |
442 | if not hasattr(self, 'package_objects'): |
443 | @@ -1589,10 +1521,10 @@ class OvalGeneratorCVE: |
444 | </linux-def:dpkginfo_object>\n""".format(object_id, version, name, variable_id)) |
445 | |
446 | else: |
447 | - variable_values = '(?::\w+|)\s+(.*)$\s+(.*)</value>\n <value>^'.join(bin_pkgs) |
448 | + variable_values = '(?::\w+|)\s+(.*)$</value>\n <value>^'.join(bin_pkgs) |
449 | self.queue_element('variable', """ |
450 | <constant_variable id="{0}" version="{1}" datatype="string" comment="'{2}' package binaries"> |
451 | - <value>^{3}(?::\w+|)\s+(.*)$\s+(.*)</value> |
452 | + <value>^{3}(?::\w+|)\s+(.*)$</value> |
453 | </constant_variable>\n""".format(variable_id, version, name, variable_values)) |
454 | |
455 | # create an object that references the variable |
456 | @@ -1613,10 +1545,10 @@ class OvalGeneratorCVE: |
457 | </linux-def:dpkginfo_object>\n""".format(object_id, version, name, bin_pkgs[0])) |
458 | else: |
459 | variable_id = '{0}:var:{1}0'.format(self.ns, id_base) |
460 | - variable_values = '(?::\w+|)\s+(.*)$\s+(.*)</value>\n <value>^'.join(bin_pkgs) |
461 | + variable_values = '(?::\w+|)\s+(.*)$</value>\n <value>^'.join(bin_pkgs) |
462 | self.queue_element('variable', """ |
463 | <constant_variable id="{0}" version="{1}" datatype="string" comment="'{2}' package binaries"> |
464 | - <value>^{3}(?::\w+|)\s+(.*)$\s+(.*)</value> |
465 | + <value>^{3}(?::\w+|)\s+(.*)$</value> |
466 | </constant_variable>\n""".format(variable_id, version, name, variable_values)) |
467 | self.queue_element('object', """ |
468 | <ind-def:textfilecontent54_object id="{0}" version="{1}" comment="The '{2}' package binary."> |
469 | @@ -1679,8 +1611,80 @@ class OvalGeneratorCVE: |
470 | |
471 | return self.package_tests[key] |
472 | |
473 | + def get_running_kernel_testref(self, package): |
474 | + if package['status'] == 'not-applicable': |
475 | + # if the package status is not-applicable, skip it! |
476 | + return |
477 | + elif package['status'] == 'not-vulnerable': |
478 | + # if the packaget status is not-vulnerable, skip it! |
479 | + return |
480 | + |
481 | + testref = [] |
482 | + uname_regex = process_kernel_binaries(package['binaries'], self.oval_format) |
483 | + if uname_regex: |
484 | + if self.oval_format == 'dpkg': |
485 | + var_id = self.get_running_kernel_variable_id( |
486 | + uname_regex, |
487 | + package['id_base']) |
488 | + ste_id = self.get_running_kernel_state_id( |
489 | + uname_regex, |
490 | + package['id_base'], |
491 | + var_id) |
492 | + obj_id = self.get_running_kernel_object_id( |
493 | + package['id_base']) |
494 | + test_id = self.get_running_kernel_test_id( |
495 | + uname_regex, package['id_base'], package['name'], |
496 | + obj_id, ste_id) |
497 | + testref.append({'id': test_id, |
498 | + 'comment': 'Is kernel {0} running'.format(package['name']), |
499 | + 'kernel': uname_regex, |
500 | + 'var_id': var_id, |
501 | + } |
502 | + ) |
503 | + |
504 | + # even if a cve was not fixed, we should add the test and object |
505 | + # but not the state as there won't be a fixed version to compare |
506 | + # with |
507 | + ste_id = None |
508 | + if package['fix-version']: |
509 | + ste_id = self.get_patched_kernel_state_id( |
510 | + package['id_base'], |
511 | + package['fix-version'] |
512 | + ) |
513 | + |
514 | + obj_id = self.get_patched_kernel_object_id(package['id_base'], |
515 | + var_id) |
516 | + test_id = self.get_patched_kernel_test_id( |
517 | + package['id_base'], |
518 | + package['fix-version'], |
519 | + obj_id, ste_id |
520 | + ) |
521 | + testref.append({'id': test_id, |
522 | + 'comment': 'kernel version comparison', |
523 | + 'kernelobj': True}) |
524 | + else: # OCI |
525 | + object_id = self.get_package_object_id(package['name'], |
526 | + [uname_regex], |
527 | + package['id_base']) |
528 | + state_id = None |
529 | + test_title = "Does the '{0}' package exist?".format(package['name']) |
530 | + if package['fix-version']: |
531 | + state_id = self.get_package_version_state_id(package['id_base'], |
532 | + package['fix-version']) |
533 | + test_title = "Does the '{0}' package exist and is the version less than '{1}'?".format(package['name'], |
534 | + package['fix-version']) |
535 | + test_id = self.get_package_test_id(package['name'], |
536 | + package['id_base'], |
537 | + test_title, |
538 | + object_id, |
539 | + state_id) |
540 | + package['note'] = package['name'] + package['note'] |
541 | + return [{'id': test_id, 'comment': package['note']}] |
542 | + |
543 | + return testref |
544 | + |
545 | # TODO: xml lib |
546 | - def get_running_kernel_object_id(self, id_base, var_id, version=1): |
547 | + def get_running_kernel_object_id(self, id_base, version=1): |
548 | """ creates a uname_object so we can use the value from uname -r for |
549 | mainly two things: |
550 | 1. compare with the return uname is of the same version and flavour |
551 | @@ -1702,15 +1706,7 @@ class OvalGeneratorCVE: |
552 | |
553 | self.kernel_uname_obj_id = object_id |
554 | |
555 | - object_id_2 = '{0}:obj:{1}0'.format(self.ns, id_base + 1) |
556 | - |
557 | - self.queue_element('object', """ |
558 | - <ind-def:variable_object id="{0}" version="{1}"> |
559 | - <ind-def:var_ref>{2}</ind-def:var_ref> |
560 | - </ind-def:variable_object>\n""".format(object_id_2, version, var_id)) |
561 | - |
562 | - |
563 | - return (self.kernel_uname_obj_id, object_id_2) |
564 | + return self.kernel_uname_obj_id |
565 | |
566 | # TODO: xml lib |
567 | def get_running_kernel_state_id(self, uname_regex, id_base, var_id, version=1): |
568 | @@ -1722,9 +1718,6 @@ class OvalGeneratorCVE: |
569 | if not hasattr(self, 'uname_states'): |
570 | self.uname_states = {} |
571 | |
572 | - if not hasattr(self, 'kernel_state_id'): |
573 | - self.kernel_state_id = None |
574 | - |
575 | if uname_regex not in self.uname_states: |
576 | state_id = '{0}:ste:{1}0'.format(self.ns, id_base) |
577 | self.queue_element('state', """ |
578 | @@ -1734,19 +1727,10 @@ class OvalGeneratorCVE: |
579 | |
580 | self.uname_states[uname_regex] = state_id |
581 | |
582 | - if not self.kernel_state_id: |
583 | - state_id_2 = '{0}:ste:{1}0'.format(self.ns, id_base + 1) |
584 | - self.queue_element('state', """ |
585 | - <ind-def:variable_state id="{0}" version="{1}"> |
586 | - <ind-def:value operation="greater than" datatype="debian_evr_string" var_ref="{2}" var_check="at least one" /> |
587 | - </ind-def:variable_state>\n""".format(state_id_2, version, var_id)) |
588 | - |
589 | - self.kernel_state_id = state_id_2 |
590 | - |
591 | - return (self.uname_states[uname_regex], self.kernel_state_id) |
592 | + return self.uname_states[uname_regex] |
593 | |
594 | # TODO: xml lib |
595 | - def get_running_kernel_variable_id(self, uname_regex, id_base, fixed_version, version=1): |
596 | + def get_running_kernel_variable_id(self, uname_regex, id_base, version=1): |
597 | """ creates a local variable to store running kernel version in devian evr string""" |
598 | if not hasattr(self, 'uname_variables'): |
599 | self.uname_variables = {} |
600 | @@ -1765,21 +1749,10 @@ class OvalGeneratorCVE: |
601 | |
602 | self.uname_variables['local_variable'] = var_id |
603 | |
604 | - var_id_2 = '{0}:var:{1}0'.format(self.ns, id_base + 1) |
605 | - patched = re.search('([\d|\.]+-\d+)[\.|\d]+', fixed_version) |
606 | - if patched: |
607 | - patched = patched.group(1) |
608 | - else: |
609 | - patched = fixed_version |
610 | - self.queue_element('variable', """ |
611 | - <constant_variable id="{0}" version="{1}" datatype="debian_evr_string" comment="patched kernel"> |
612 | - <value>0:{2}</value> |
613 | - </constant_variable>""".format(var_id_2, version, patched)) |
614 | - |
615 | - return (self.uname_variables['local_variable'], var_id_2) |
616 | + return self.uname_variables['local_variable'] |
617 | |
618 | # TODO: xml lib |
619 | - def get_running_kernel_test_id(self, uname_regex, id_base, name, object_id, state_id, object_id_2, state_id_2, version=1): |
620 | + def get_running_kernel_test_id(self, uname_regex, id_base, name, object_id, state_id, version=1): |
621 | """ create uname test and return its OVAL id """ |
622 | if not hasattr(self, 'uname_tests'): |
623 | self.uname_tests = {} |
624 | @@ -1794,16 +1767,94 @@ class OvalGeneratorCVE: |
625 | |
626 | self.uname_tests[uname_regex] = test_id |
627 | |
628 | - test_id_2 = '{0}:tst:{1}0'.format(self.ns, id_base + 1) |
629 | + return self.uname_tests[uname_regex] |
630 | + |
631 | + def get_patched_kernel_variable_id(self, id_base, fixed_version, version=1): |
632 | + """ creates a local variable to store the patched kernel version """ |
633 | + if not hasattr(self, 'patched_kernel_variables'): |
634 | + self.patched_kernel_variables = {} |
635 | + |
636 | + patched = re.search('([\d|\.]+-\d+)[\.|\d]+', fixed_version) |
637 | + if patched: |
638 | + patched = patched.group(1) |
639 | + else: |
640 | + patched = fixed_version |
641 | + |
642 | + if patched not in self.patched_kernel_variables: |
643 | + var_id = '{0}:var:{1}0'.format(self.ns, id_base + 1) |
644 | + |
645 | + self.queue_element('variable', """ |
646 | + <constant_variable id="{0}" version="{1}" datatype="debian_evr_string" comment="patched kernel"> |
647 | + <value>0:{2}</value> |
648 | + </constant_variable>""".format(var_id, version, patched)) |
649 | + |
650 | + self.patched_kernel_variables[patched] = var_id |
651 | + |
652 | + return self.patched_kernel_variables[patched] |
653 | + |
654 | + def get_patched_kernel_object_id(self, id_base, var_id, version=1): |
655 | + """ create variable object that points to kernel version |
656 | + in evr format in local_variable |
657 | + """ |
658 | + |
659 | + object_id = '{0}:obj:{1}0'.format(self.ns, id_base + 1) |
660 | + |
661 | + self.queue_element('object', """ |
662 | + <ind-def:variable_object id="{0}" version="{1}"> |
663 | + <ind-def:var_ref>{2}</ind-def:var_ref> |
664 | + </ind-def:variable_object>\n""".format(object_id, version, var_id)) |
665 | + |
666 | + return object_id |
667 | + |
668 | + # TODO: xml lib |
669 | + def get_patched_kernel_state_id(self, id_base, fixed_version, version=1): |
670 | + """ create state to compare to the running kernel |
671 | + Return its OVAL id |
672 | + """ |
673 | + if not hasattr(self, 'patched_kernel_states'): |
674 | + self.patched_kernel_states = {} |
675 | + |
676 | + patched = re.search('([\d|\.]+-\d+)[\.|\d]+', fixed_version) |
677 | + if patched: |
678 | + patched = patched.group(1) |
679 | + else: |
680 | + patched = fixed_version |
681 | + |
682 | + if patched not in self.patched_kernel_states: |
683 | + state_id = '{0}:ste:{1}0'.format(self.ns, id_base + 1) |
684 | |
685 | - self.queue_element('test', """ |
686 | + self.queue_element('state', """ |
687 | + <ind-def:variable_state id="{0}" version="{1}"> |
688 | + <ind-def:value datatype="debian_evr_string" operation="less than">{2}</ind-def:value> |
689 | + </ind-def:variable_state>\n""".format(state_id, version, patched)) |
690 | + |
691 | + self.patched_kernel_states[patched] = state_id |
692 | + |
693 | + return self.patched_kernel_states[patched] |
694 | + |
695 | + def get_patched_kernel_test_id(self, id_base, fixed_version, object_id, state_id, version=1): |
696 | + """ create patched kernel test and return its OVAL id """ |
697 | + if not hasattr(self, 'patched_kernel_tests'): |
698 | + self.patched_kernel_tests = {} |
699 | + |
700 | + if fixed_version not in self.patched_kernel_tests: |
701 | + test_id = '{0}:tst:{1}0'.format(self.ns, id_base + 1) |
702 | + |
703 | + if state_id: |
704 | + self.queue_element('test', """ |
705 | <ind-def:variable_test id="{0}" version="1" check="all" check_existence="all_exist" comment="kernel version comparison"> |
706 | <ind-def:object object_ref="{1}"/> |
707 | <ind-def:state state_ref="{2}"/> |
708 | - </ind-def:variable_test>\n""".format(test_id_2, object_id_2, state_id_2)) |
709 | + </ind-def:variable_test>\n""".format(test_id, object_id, state_id)) |
710 | + else: |
711 | + self.queue_element('test', """ |
712 | + <ind-def:variable_test id="{0}" version="1" check="all" check_existence="all_exist" comment="kernel version comparison"> |
713 | + <ind-def:object object_ref="{1}"/> |
714 | + </ind-def:variable_test>\n""".format(test_id, object_id)) |
715 | |
716 | + self.patched_kernel_tests[fixed_version] = test_id |
717 | |
718 | - return (self.uname_tests[uname_regex], test_id_2) |
719 | + return self.patched_kernel_tests[fixed_version] |
720 | |
721 | def queue_element(self, element, xml): |
722 | """ add an OVAL element to an output queue file """ |
723 | @@ -2247,11 +2298,12 @@ class OvalGeneratorUSN(): |
724 | <unix:uname_object id="{ns}:obj:{id}" version="1"/>""".format(**mapping) |
725 | |
726 | elif 'kernelobj' in test_ref: |
727 | + mapping['varid'] = test_ref['kernelobj'] |
728 | |
729 | _object = \ |
730 | """ |
731 | <ind:variable_object id="{ns}:obj:{id}" version="1"> |
732 | - <ind:var_ref>{ns}:var:{id}</ind:var_ref> |
733 | + <ind:var_ref>{ns}:var:{varid}</ind:var_ref> |
734 | </ind:variable_object>""".format(**mapping) |
735 | |
736 | elif self.pocket == "livepatch": |
737 | @@ -2309,11 +2361,14 @@ class OvalGeneratorUSN(): |
738 | </unix:uname_state>""".format(**mapping) |
739 | |
740 | elif 'kernelobj' in test_ref: |
741 | - mapping['varid'] = test_ref['kernelobj'] |
742 | + binary_version = test_ref['version'] |
743 | + binary_version = re.search('([\d|\.]+-\d+)[\.|\d]+', binary_version) |
744 | + mapping['bversion'] = "0:" + binary_version.group(1) |
745 | + |
746 | state = \ |
747 | """ |
748 | <ind:variable_state id="{ns}:ste:{id}" version="1"> |
749 | - <ind:value operation="greater than" datatype="debian_evr_string" var_ref="{ns}:var:{varid}" var_check="at least one" /> |
750 | + <ind:value datatype="debian_evr_string" operation="less than">{bversion}</ind:value> |
751 | </ind:variable_state>""".format(**mapping) |
752 | |
753 | elif self.pocket == "livepatch": |
754 | @@ -2374,21 +2429,10 @@ class OvalGeneratorUSN(): |
755 | </regex_capture> |
756 | </concat> |
757 | </local_variable>""".format(**mapping) |
758 | - |
759 | return variable |
760 | |
761 | elif 'kernelobj' in test_ref: |
762 | - binary_version = test_ref['version'] |
763 | - binary_version = re.search('([\d|\.]+-\d+)[\.|\d]+', binary_version) |
764 | - mapping['bversion'] = "0:" + binary_version.group(1) |
765 | - |
766 | - variable = \ |
767 | - """ |
768 | - <constant_variable id="{ns}:var:{id}" version="1" datatype="debian_evr_string" comment="patched kernel"> |
769 | - <value>{bversion}</value> |
770 | - </constant_variable>""".format(**mapping) |
771 | - |
772 | - return variable |
773 | + return |
774 | |
775 | else: |
776 | for binary in binaries_list: |
LGTM! Thanks for taking care of this