Merge ~sahnaseredini/ubuntu-cve-tracker:amir-dev-oval-pkgcve into ubuntu-cve-tracker:master

Proposed by Amir Naseredini
Status: Merged
Approved by: Eduardo Barretto
Approved revision: 18d80992467cdbff057d1b66730b72028f3f8147
Merged at revision: 1ac82430d607d6b30372f1709014f6f616dd6eb7
Proposed branch: ~sahnaseredini/ubuntu-cve-tracker:amir-dev-oval-pkgcve
Merge into: ubuntu-cve-tracker:master
Diff against target: 308 lines (+89/-30)
2 files modified
scripts/generate-oval (+46/-15)
scripts/oval_lib.py (+43/-15)
Reviewer Review Type Date Requested Status
Eduardo Barretto Approve
David Fernandez Gonzalez Pending
Review via email: mp+460242@code.launchpad.net

Commit message

goals:
- to generate one file for all the different product/release
- to keep the previous functionality with an added option

Description of the change

changes:
- added an option to the code expand
  - when’s it’s off all the oval data (from ubuntu, esm-infra, and esm-apps) is
    combined in one output file
  - by default this option is off and can be turned on by adding --expand
    argument when running the code
  - adding --expand would make the code behave like before by generating
    different output files (from ubuntu, esm-infra, and esm-apps) for oval
- a function `_eligible_for_output` is added in order to check what we want to produce as the output file

To post a comment you must log in.
Revision history for this message
Eduardo Barretto (ebarretto) wrote :

Thanks, I'm running some tests and will let you know in case of issues. Nevertheless we should wait to merge this until next week so we can inform users of the upcoming change.

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

There's a small fix needed.
In the criterion comment, it is showing up, for example, as 'esm-apps/jammy'. It should show as jammy.

review: Needs Fixing
Revision history for this message
Eduardo Barretto (ebarretto) wrote :

lgtm, thanks for addressing my comment.

review: Approve
Revision history for this message
Amir Naseredini (sahnaseredini) wrote :

Thanks for the review Eduardo.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/scripts/generate-oval b/scripts/generate-oval
index bdb9902..920bba6 100755
--- a/scripts/generate-oval
+++ b/scripts/generate-oval
@@ -103,6 +103,10 @@ def main():
103 'CVE OVAL')103 'CVE OVAL')
104 parser.add_argument('--fixed-only', action='store_true',104 parser.add_argument('--fixed-only', action='store_true',
105 help='only generate pkg oval for fixed CVEs')105 help='only generate pkg oval for fixed CVEs')
106 parser.add_argument('--expand', action='store_true',
107 help='avoids combining all the oval data into one '
108 'file and expands the output oval files into different '
109 'releases, such as esm-apps, esm-infra, and ubuntu')
106110
107 args = parser.parse_args()111 args = parser.parse_args()
108 pathnames = args.pathname or default_cves_to_process112 pathnames = args.pathname or default_cves_to_process
@@ -150,6 +154,8 @@ def main():
150 if release not in supported_releases:154 if release not in supported_releases:
151 error(f"unknown oval release {release}")155 error(f"unknown oval release {release}")
152156
157 expand = args.expand
158
153 cache = {}159 cache = {}
154 for release in supported_releases:160 for release in supported_releases:
155 cve_cache = {}161 cve_cache = {}
@@ -157,15 +163,15 @@ def main():
157163
158 if args.pkg_oval:164 if args.pkg_oval:
159 if args.oci:165 if args.oci:
160 generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir)166 generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir, expand)
161 else:167 else:
162 generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only)168 generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, expand)
163 return169 return
164170
165 if args.oci:171 if args.oci:
166 generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir)172 generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir, expand)
167 else:173 else:
168 generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only)174 generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, expand)
169 return175 return
170176
171177
@@ -199,7 +205,7 @@ def ignore_source_package(source):
199 return False205 return False
200206
201207
202def parse_cve_file(filepath, cache, pkg_filter=None):208def parse_cve_file(filepath, cache, pkg_filter=None, expand=False):
203 """ parse CVE data file into a dictionary using cve_lib """209 """ parse CVE data file into a dictionary using cve_lib """
204210
205 cve_header_data = {211 cve_header_data = {
@@ -220,7 +226,6 @@ def parse_cve_file(filepath, cache, pkg_filter=None):
220 'Unknown-Fields': [],226 'Unknown-Fields': [],
221 'Source-note': filepath227 'Source-note': filepath
222 }228 }
223
224 data = load_cve(filepath)229 data = load_cve(filepath)
225 # first try a naive translation of fields230 # first try a naive translation of fields
226 for f in cve_header_data:231 for f in cve_header_data:
@@ -255,7 +260,7 @@ def parse_cve_file(filepath, cache, pkg_filter=None):
255 if rel not in supported_releases:260 if rel not in supported_releases:
256 continue261 continue
257 state, details = data['pkgs'][pkg][rel]262 state, details = data['pkgs'][pkg][rel]
258 packages[pkg]['Releases'][rel] = oval_lib.CVEPkgRelEntry.parse_package_status(rel, pkg, state, details, filepath, cache)263 packages[pkg]['Releases'][rel] = oval_lib.CVEPkgRelEntry.parse_package_status(rel, pkg, state, details, filepath, cache, oval_lib.find_release_codename(rel), expand)
259264
260 # add supplemental packages; usually kernels only need this special case.265 # add supplemental packages; usually kernels only need this special case.
261 for package in [name for name in packages if name in kernel_srcs]:266 for package in [name for name in packages if name in kernel_srcs]:
@@ -435,9 +440,21 @@ def generate_oval_usn(outdir, usn, usn_releases, cve_dir, usn_db_dir, no_progres
435440
436 return True441 return True
437442
438def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None):443def filter_releases_for_output(releases, ov):
444 output_releases = []
445 for release in releases:
446 ov._init_ids(release)
447 if ov._eligible_for_output(release):
448 if ov.expand == False and 'esm' in ov.release:
449 output_releases.append(ov.release_codename)
450 else:
451 output_releases.append(release)
452 return output_releases
453
454
455def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None, expand=False):
439 if not no_progress:456 if not no_progress:
440 print(f'[*] Generating OVAL PKG for packages in releases {", ".join(releases)}')457 print('[*] Initiating OVAL Generation for PKG')
441458
442 ov = oval_lib.OvalGeneratorPkg(459 ov = oval_lib.OvalGeneratorPkg(
443 releases, 460 releases,
@@ -449,8 +466,15 @@ def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache
449 cve_cache=cve_cache, 466 cve_cache=cve_cache,
450 oval_format='dpkg',467 oval_format='dpkg',
451 outdir=outdir, 468 outdir=outdir,
452 cve_prefix_dir=cve_prefix_dir469 cve_prefix_dir=cve_prefix_dir,
470 expand=expand
453 )471 )
472
473 output_releases = filter_releases_for_output(releases, ov)
474
475 if not no_progress:
476 print(f'[*] Generating OVAL PKG for packages in releases {", ".join(output_releases)}')
477
454 ov.generate_oval()478 ov.generate_oval()
455479
456 if oci:480 if oci:
@@ -459,11 +483,11 @@ def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache
459 ov.generate_oval()483 ov.generate_oval()
460484
461 if not no_progress:485 if not no_progress:
462 print(f'[X] Done generating OVAL PKG for packages in releases {", ".join(releases)}')486 print(f'[X] Done generating OVAL PKG for packages in releases {", ".join(output_releases)}')
463487
464def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None):488def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None, expand=False):
465 if not no_progress:489 if not no_progress:
466 print(f'[*] Generating OVAL CVE for packages in releases {",".join(releases)}')490 print('[*] Initiating OVAL Generation for CVE')
467491
468 ov = oval_lib.OvalGeneratorCVE(492 ov = oval_lib.OvalGeneratorCVE(
469 releases, 493 releases,
@@ -475,8 +499,15 @@ def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oc
475 cve_cache=cve_cache, 499 cve_cache=cve_cache,
476 oval_format='dpkg',500 oval_format='dpkg',
477 outdir=outdir, 501 outdir=outdir,
478 cve_prefix_dir=cve_prefix_dir502 cve_prefix_dir=cve_prefix_dir,
503 expand=expand
479 )504 )
505
506 output_releases = filter_releases_for_output(releases, ov)
507
508 if not no_progress:
509 print(f'[*] Generating OVAL CVE for packages in releases {", ".join(output_releases)}')
510
480 ov.generate_oval()511 ov.generate_oval()
481512
482 if oci:513 if oci:
@@ -485,7 +516,7 @@ def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oc
485 ov.generate_oval()516 ov.generate_oval()
486517
487 if not no_progress:518 if not no_progress:
488 print(f'[X] Done generating OVAL CVE for packages in releases {", ".join(releases)}')519 print(f'[X] Done generating OVAL CVE for packages in releases {", ".join(output_releases)}')
489520
490if __name__ == '__main__':521if __name__ == '__main__':
491 main()522 main()
diff --git a/scripts/oval_lib.py b/scripts/oval_lib.py
index b30a987..e018d74 100644
--- a/scripts/oval_lib.py
+++ b/scripts/oval_lib.py
@@ -191,20 +191,22 @@ def get_binarypkgs(cache, source_name, release):
191 return rel, binaries_map191 return rel, binaries_map
192192
193class CVEPkgRelEntry:193class CVEPkgRelEntry:
194 def __init__(self, pkg, release, cve, status, note) -> None:194 def __init__(self, pkg, release, cve, status, note, expand=False) -> None:
195 self.pkg = pkg195 self.pkg = pkg
196 self.cve = cve196 self.cve = cve
197 self.orig_status = status197 self.orig_status = status
198 self.orig_note = note198 self.orig_note = note
199 self.release = release199 self.release = release
200 cve_info = CVEPkgRelEntry.parse_package_status(self.release, pkg.name, status, note, cve.number, None)200 self.expand = expand
201 self.release_codename = find_release_codename(self.release)
202 cve_info = CVEPkgRelEntry.parse_package_status(self.release, pkg.name, status, note, cve.number, None, self.release_codename, self.expand)
201203
202 self.note = cve_info['note']204 self.note = cve_info['note']
203 self.status = cve_info['status']205 self.status = cve_info['status']
204 self.fixed_version = cve_info['fix-version'] if self.status == 'fixed' else None206 self.fixed_version = cve_info['fix-version'] if self.status == 'fixed' else None
205207
206 @staticmethod208 @staticmethod
207 def parse_package_status(release, package, status_text, note, filepath, cache):209 def parse_package_status(release, package, status_text, note, filepath, cache, release_codename, expand=False):
208 """ parse ubuntu package status string format:210 """ parse ubuntu package status string format:
209 <status code> (<version/notes>)211 <status code> (<version/notes>)
210 outputs dictionary: {212 outputs dictionary: {
@@ -216,6 +218,9 @@ class CVEPkgRelEntry:
216218
217 # TODO fix for CVE Generator219 # TODO fix for CVE Generator
218220
221 if expand == False and 'esm' in release:
222 release = release_codename
223
219 # break out status code and detail224 # break out status code and detail
220 code = status_text.lower()225 code = status_text.lower()
221 detail = note.strip('()') if note else None226 detail = note.strip('()') if note else None
@@ -355,8 +360,8 @@ class CVE:
355 return pkgs360 return pkgs
356361
357362
358 def add_pkg(self, pkg_object, release, state, note):363 def add_pkg(self, pkg_object, release, state, note, expand=False):
359 cve_pkg_entry = CVEPkgRelEntry(pkg_object, release, self, state, note)364 cve_pkg_entry = CVEPkgRelEntry(pkg_object, release, self, state, note, expand)
360 self.pkg_rel_entries[str(pkg_object)] = cve_pkg_entry365 self.pkg_rel_entries[str(pkg_object)] = cve_pkg_entry
361 self.pkgs.append(pkg_object)366 self.pkgs.append(pkg_object)
362 pkg_object.add_cve(self)367 pkg_object.add_cve(self)
@@ -497,7 +502,7 @@ class OvalGenerator:
497 supported_oval_elements = ('definition', 'test', 'object', 'state', 'variable')502 supported_oval_elements = ('definition', 'test', 'object', 'state', 'variable')
498 generator_version = '2'503 generator_version = '2'
499 oval_schema_version = '5.11.1'504 oval_schema_version = '5.11.1'
500 def __init__(self, type, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg') -> None:505 def __init__(self, type, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg', expand=False) -> None:
501 self.releases = releases506 self.releases = releases
502 self.output_dir = outdir507 self.output_dir = outdir
503 self.oval_format = oval_format508 self.oval_format = oval_format
@@ -508,6 +513,7 @@ class OvalGenerator:
508 self.cve_paths = cve_paths513 self.cve_paths = cve_paths
509 self.fixed_only = fixed_only514 self.fixed_only = fixed_only
510 self.packages, self.cves = self._load(cve_prefix_dir, packages)515 self.packages, self.cves = self._load(cve_prefix_dir, packages)
516 self.expand = expand
511517
512 def _init_ids(self, release):518 def _init_ids(self, release):
513 # e.g. codename for trusty/esm should be trusty519 # e.g. codename for trusty/esm should be trusty
@@ -538,8 +544,14 @@ class OvalGenerator:
538 self.definition_id = self.release_id544 self.definition_id = self.release_id
539 self.definition_step = 1 * 10 ** 5545 self.definition_step = 1 * 10 ** 5
540 self.criterion_step = 10546 self.criterion_step = 10
541 self.output_filepath = \547 self.output_filepath = ''
542 '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release.replace('/', '_'), self.generator_type)548 if self.expand == False and 'esm' in self.release:
549 self.output_filepath = \
550 '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release_codename, self.generator_type)
551 else:
552 self.output_filepath = \
553 '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release.replace('/', '_'), self.generator_type)
554
543 555
544 def _add_structure(self, root) -> None:556 def _add_structure(self, root) -> None:
545 structure = {}557 structure = {}
@@ -730,7 +742,7 @@ class OvalGenerator:
730 packages[package_name] = pkg_obj742 packages[package_name] = pkg_obj
731743
732 pkg_obj = packages[package_name]744 pkg_obj = packages[package_name]
733 cve.add_pkg(pkg_obj, release, cve_data['pkgs'][package_name][release][0],cve_data['pkgs'][package_name][release][1])745 cve.add_pkg(pkg_obj, release, cve_data['pkgs'][package_name][release][0],cve_data['pkgs'][package_name][release][1], self.expand)
734746
735 def _load(self, cve_prefix_dir, packages_filter=None) -> None:747 def _load(self, cve_prefix_dir, packages_filter=None) -> None:
736 cve_lib.load_external_subprojects()748 cve_lib.load_external_subprojects()
@@ -806,6 +818,14 @@ class OvalGenerator:
806 with open(os.path.join(self.output_dir, self.output_filepath), 'w') as file:818 with open(os.path.join(self.output_dir, self.output_filepath), 'w') as file:
807 file.write(xmlstr)819 file.write(xmlstr)
808820
821 def _eligible_for_output(self, release):
822 if self.expand == False:
823 if self.release_codename == release and 'LTS' in cve_lib.release_name(release):
824 return False
825 elif 'esm-infra' in release:
826 return False
827 return True
828
809 # Object generators829 # Object generators
810 def _generate_criteria(self) -> etree.Element:830 def _generate_criteria(self) -> etree.Element:
811 criteria = etree.Element("criteria")831 criteria = etree.Element("criteria")
@@ -1291,8 +1311,9 @@ class OvalGenerator:
12911311
12921312
1293class OvalGeneratorPkg(OvalGenerator):1313class OvalGeneratorPkg(OvalGenerator):
1294 def __init__(self, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg') -> None:1314 def __init__(self, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg',expand=False) -> None:
1295 super().__init__('pkg', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format)1315 self.expand = expand
1316 super().__init__('pkg', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format, expand)
12961317
1297 def _generate_advisory(self, package: Package) -> etree.Element:1318 def _generate_advisory(self, package: Package) -> etree.Element:
1298 advisory = etree.Element("advisory")1319 advisory = etree.Element("advisory")
@@ -1488,11 +1509,13 @@ class OvalGeneratorPkg(OvalGenerator):
1488 else:1509 else:
1489 self._populate_pkg(all_pkgs[pkg], root_element)1510 self._populate_pkg(all_pkgs[pkg], root_element)
14901511
1491 self._write_oval_xml(xml_tree, root_element)1512 if self._eligible_for_output(release):
1513 self._write_oval_xml(xml_tree, root_element)
14921514
1493class OvalGeneratorCVE(OvalGenerator):1515class OvalGeneratorCVE(OvalGenerator):
1494 def __init__(self, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg') -> None:1516 def __init__(self, releases, cve_paths, packages, progress, pkg_cache, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg', expand=False) -> None:
1495 super().__init__('cve', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format)1517 self.expand = expand
1518 super().__init__('cve', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format, expand)
14961519
1497 # For CVE OVAL, the definition ID is generated1520 # For CVE OVAL, the definition ID is generated
1498 # from the CVE ID1521 # from the CVE ID
@@ -1737,7 +1760,8 @@ class OvalGeneratorCVE(OvalGenerator):
1737 self._set_definition_id(cve_id=all_cves[cve].number)1760 self._set_definition_id(cve_id=all_cves[cve].number)
1738 self._generate_elements_from_cve(all_cves[cve], accepted_releases, root_element, running_kernel_id, pkg_cache, fixed_versions)1761 self._generate_elements_from_cve(all_cves[cve], accepted_releases, root_element, running_kernel_id, pkg_cache, fixed_versions)
17391762
1740 self._write_oval_xml(xml_tree, root_element)1763 if self._eligible_for_output(release):
1764 self._write_oval_xml(xml_tree, root_element)
17411765
1742class OvalGeneratorUSNs(OvalGenerator):1766class OvalGeneratorUSNs(OvalGenerator):
1743 def __init__(self, release, release_name, cve_paths, packages, progress, pkg_cache, usn_db_dir, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg') -> None:1767 def __init__(self, release, release_name, cve_paths, packages, progress, pkg_cache, usn_db_dir, fixed_only=True, cve_cache=None, cve_prefix_dir=None, outdir='./', oval_format='dpkg') -> None:
@@ -2646,3 +2670,7 @@ class OvalGeneratorUSN():
2646 # remove tmp dir if empty2670 # remove tmp dir if empty
2647 if not os.listdir(self.tmpdir):2671 if not os.listdir(self.tmpdir):
2648 os.rmdir(self.tmpdir)2672 os.rmdir(self.tmpdir)
2673
2674def find_release_codename(release):
2675 return cve_lib.release_progenitor(release) if cve_lib.release_progenitor(release) else release.replace('/', '_')
2676

Subscribers

People subscribed via source and target branches