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
1diff --git a/scripts/generate-oval b/scripts/generate-oval
2index bdb9902..920bba6 100755
3--- a/scripts/generate-oval
4+++ b/scripts/generate-oval
5@@ -103,6 +103,10 @@ def main():
6 'CVE OVAL')
7 parser.add_argument('--fixed-only', action='store_true',
8 help='only generate pkg oval for fixed CVEs')
9+ parser.add_argument('--expand', action='store_true',
10+ help='avoids combining all the oval data into one '
11+ 'file and expands the output oval files into different '
12+ 'releases, such as esm-apps, esm-infra, and ubuntu')
13
14 args = parser.parse_args()
15 pathnames = args.pathname or default_cves_to_process
16@@ -150,6 +154,8 @@ def main():
17 if release not in supported_releases:
18 error(f"unknown oval release {release}")
19
20+ expand = args.expand
21+
22 cache = {}
23 for release in supported_releases:
24 cve_cache = {}
25@@ -157,15 +163,15 @@ def main():
26
27 if args.pkg_oval:
28 if args.oci:
29- generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir)
30+ 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)
31 else:
32- generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only)
33+ generate_oval_package(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, expand)
34 return
35
36 if args.oci:
37- generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, ocioutdir)
38+ 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)
39 else:
40- generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only)
41+ generate_oval_cve(releases, outdir, args.cve_prefix_dir, cache, cve_cache, args.oci, args.no_progress, args.packages, pathnames, args.fixed_only, expand)
42 return
43
44
45@@ -199,7 +205,7 @@ def ignore_source_package(source):
46 return False
47
48
49-def parse_cve_file(filepath, cache, pkg_filter=None):
50+def parse_cve_file(filepath, cache, pkg_filter=None, expand=False):
51 """ parse CVE data file into a dictionary using cve_lib """
52
53 cve_header_data = {
54@@ -220,7 +226,6 @@ def parse_cve_file(filepath, cache, pkg_filter=None):
55 'Unknown-Fields': [],
56 'Source-note': filepath
57 }
58-
59 data = load_cve(filepath)
60 # first try a naive translation of fields
61 for f in cve_header_data:
62@@ -255,7 +260,7 @@ def parse_cve_file(filepath, cache, pkg_filter=None):
63 if rel not in supported_releases:
64 continue
65 state, details = data['pkgs'][pkg][rel]
66- packages[pkg]['Releases'][rel] = oval_lib.CVEPkgRelEntry.parse_package_status(rel, pkg, state, details, filepath, cache)
67+ packages[pkg]['Releases'][rel] = oval_lib.CVEPkgRelEntry.parse_package_status(rel, pkg, state, details, filepath, cache, oval_lib.find_release_codename(rel), expand)
68
69 # add supplemental packages; usually kernels only need this special case.
70 for package in [name for name in packages if name in kernel_srcs]:
71@@ -435,9 +440,21 @@ def generate_oval_usn(outdir, usn, usn_releases, cve_dir, usn_db_dir, no_progres
72
73 return True
74
75-def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None):
76+def filter_releases_for_output(releases, ov):
77+ output_releases = []
78+ for release in releases:
79+ ov._init_ids(release)
80+ if ov._eligible_for_output(release):
81+ if ov.expand == False and 'esm' in ov.release:
82+ output_releases.append(ov.release_codename)
83+ else:
84+ output_releases.append(release)
85+ return output_releases
86+
87+
88+def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None, expand=False):
89 if not no_progress:
90- print(f'[*] Generating OVAL PKG for packages in releases {", ".join(releases)}')
91+ print('[*] Initiating OVAL Generation for PKG')
92
93 ov = oval_lib.OvalGeneratorPkg(
94 releases,
95@@ -449,8 +466,15 @@ def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache
96 cve_cache=cve_cache,
97 oval_format='dpkg',
98 outdir=outdir,
99- cve_prefix_dir=cve_prefix_dir
100+ cve_prefix_dir=cve_prefix_dir,
101+ expand=expand
102 )
103+
104+ output_releases = filter_releases_for_output(releases, ov)
105+
106+ if not no_progress:
107+ print(f'[*] Generating OVAL PKG for packages in releases {", ".join(output_releases)}')
108+
109 ov.generate_oval()
110
111 if oci:
112@@ -459,11 +483,11 @@ def generate_oval_package(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache
113 ov.generate_oval()
114
115 if not no_progress:
116- print(f'[X] Done generating OVAL PKG for packages in releases {", ".join(releases)}')
117+ print(f'[X] Done generating OVAL PKG for packages in releases {", ".join(output_releases)}')
118
119-def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None):
120+def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oci, no_progress, packages, pathnames, fixed_only, ocioutdir=None, expand=False):
121 if not no_progress:
122- print(f'[*] Generating OVAL CVE for packages in releases {",".join(releases)}')
123+ print('[*] Initiating OVAL Generation for CVE')
124
125 ov = oval_lib.OvalGeneratorCVE(
126 releases,
127@@ -475,8 +499,15 @@ def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oc
128 cve_cache=cve_cache,
129 oval_format='dpkg',
130 outdir=outdir,
131- cve_prefix_dir=cve_prefix_dir
132+ cve_prefix_dir=cve_prefix_dir,
133+ expand=expand
134 )
135+
136+ output_releases = filter_releases_for_output(releases, ov)
137+
138+ if not no_progress:
139+ print(f'[*] Generating OVAL CVE for packages in releases {", ".join(output_releases)}')
140+
141 ov.generate_oval()
142
143 if oci:
144@@ -485,7 +516,7 @@ def generate_oval_cve(releases, outdir, cve_prefix_dir, pkg_cache, cve_cache, oc
145 ov.generate_oval()
146
147 if not no_progress:
148- print(f'[X] Done generating OVAL CVE for packages in releases {", ".join(releases)}')
149+ print(f'[X] Done generating OVAL CVE for packages in releases {", ".join(output_releases)}')
150
151 if __name__ == '__main__':
152 main()
153diff --git a/scripts/oval_lib.py b/scripts/oval_lib.py
154index b30a987..e018d74 100644
155--- a/scripts/oval_lib.py
156+++ b/scripts/oval_lib.py
157@@ -191,20 +191,22 @@ def get_binarypkgs(cache, source_name, release):
158 return rel, binaries_map
159
160 class CVEPkgRelEntry:
161- def __init__(self, pkg, release, cve, status, note) -> None:
162+ def __init__(self, pkg, release, cve, status, note, expand=False) -> None:
163 self.pkg = pkg
164 self.cve = cve
165 self.orig_status = status
166 self.orig_note = note
167 self.release = release
168- cve_info = CVEPkgRelEntry.parse_package_status(self.release, pkg.name, status, note, cve.number, None)
169+ self.expand = expand
170+ self.release_codename = find_release_codename(self.release)
171+ cve_info = CVEPkgRelEntry.parse_package_status(self.release, pkg.name, status, note, cve.number, None, self.release_codename, self.expand)
172
173 self.note = cve_info['note']
174 self.status = cve_info['status']
175 self.fixed_version = cve_info['fix-version'] if self.status == 'fixed' else None
176
177 @staticmethod
178- def parse_package_status(release, package, status_text, note, filepath, cache):
179+ def parse_package_status(release, package, status_text, note, filepath, cache, release_codename, expand=False):
180 """ parse ubuntu package status string format:
181 <status code> (<version/notes>)
182 outputs dictionary: {
183@@ -216,6 +218,9 @@ class CVEPkgRelEntry:
184
185 # TODO fix for CVE Generator
186
187+ if expand == False and 'esm' in release:
188+ release = release_codename
189+
190 # break out status code and detail
191 code = status_text.lower()
192 detail = note.strip('()') if note else None
193@@ -355,8 +360,8 @@ class CVE:
194 return pkgs
195
196
197- def add_pkg(self, pkg_object, release, state, note):
198- cve_pkg_entry = CVEPkgRelEntry(pkg_object, release, self, state, note)
199+ def add_pkg(self, pkg_object, release, state, note, expand=False):
200+ cve_pkg_entry = CVEPkgRelEntry(pkg_object, release, self, state, note, expand)
201 self.pkg_rel_entries[str(pkg_object)] = cve_pkg_entry
202 self.pkgs.append(pkg_object)
203 pkg_object.add_cve(self)
204@@ -497,7 +502,7 @@ class OvalGenerator:
205 supported_oval_elements = ('definition', 'test', 'object', 'state', 'variable')
206 generator_version = '2'
207 oval_schema_version = '5.11.1'
208- 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:
209+ 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:
210 self.releases = releases
211 self.output_dir = outdir
212 self.oval_format = oval_format
213@@ -508,6 +513,7 @@ class OvalGenerator:
214 self.cve_paths = cve_paths
215 self.fixed_only = fixed_only
216 self.packages, self.cves = self._load(cve_prefix_dir, packages)
217+ self.expand = expand
218
219 def _init_ids(self, release):
220 # e.g. codename for trusty/esm should be trusty
221@@ -538,8 +544,14 @@ class OvalGenerator:
222 self.definition_id = self.release_id
223 self.definition_step = 1 * 10 ** 5
224 self.criterion_step = 10
225- self.output_filepath = \
226- '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release.replace('/', '_'), self.generator_type)
227+ self.output_filepath = ''
228+ if self.expand == False and 'esm' in self.release:
229+ self.output_filepath = \
230+ '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release_codename, self.generator_type)
231+ else:
232+ self.output_filepath = \
233+ '{0}com.ubuntu.{1}.{2}.oval.xml'.format('oci.' if self.oval_format == 'oci' else '', self.release.replace('/', '_'), self.generator_type)
234+
235
236 def _add_structure(self, root) -> None:
237 structure = {}
238@@ -730,7 +742,7 @@ class OvalGenerator:
239 packages[package_name] = pkg_obj
240
241 pkg_obj = packages[package_name]
242- cve.add_pkg(pkg_obj, release, cve_data['pkgs'][package_name][release][0],cve_data['pkgs'][package_name][release][1])
243+ cve.add_pkg(pkg_obj, release, cve_data['pkgs'][package_name][release][0],cve_data['pkgs'][package_name][release][1], self.expand)
244
245 def _load(self, cve_prefix_dir, packages_filter=None) -> None:
246 cve_lib.load_external_subprojects()
247@@ -806,6 +818,14 @@ class OvalGenerator:
248 with open(os.path.join(self.output_dir, self.output_filepath), 'w') as file:
249 file.write(xmlstr)
250
251+ def _eligible_for_output(self, release):
252+ if self.expand == False:
253+ if self.release_codename == release and 'LTS' in cve_lib.release_name(release):
254+ return False
255+ elif 'esm-infra' in release:
256+ return False
257+ return True
258+
259 # Object generators
260 def _generate_criteria(self) -> etree.Element:
261 criteria = etree.Element("criteria")
262@@ -1291,8 +1311,9 @@ class OvalGenerator:
263
264
265 class OvalGeneratorPkg(OvalGenerator):
266- 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:
267- super().__init__('pkg', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format)
268+ 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:
269+ self.expand = expand
270+ super().__init__('pkg', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format, expand)
271
272 def _generate_advisory(self, package: Package) -> etree.Element:
273 advisory = etree.Element("advisory")
274@@ -1488,11 +1509,13 @@ class OvalGeneratorPkg(OvalGenerator):
275 else:
276 self._populate_pkg(all_pkgs[pkg], root_element)
277
278- self._write_oval_xml(xml_tree, root_element)
279+ if self._eligible_for_output(release):
280+ self._write_oval_xml(xml_tree, root_element)
281
282 class OvalGeneratorCVE(OvalGenerator):
283- 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:
284- super().__init__('cve', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format)
285+ 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:
286+ self.expand = expand
287+ super().__init__('cve', releases, cve_paths, packages, progress, pkg_cache, fixed_only, cve_cache, cve_prefix_dir, outdir, oval_format, expand)
288
289 # For CVE OVAL, the definition ID is generated
290 # from the CVE ID
291@@ -1737,7 +1760,8 @@ class OvalGeneratorCVE(OvalGenerator):
292 self._set_definition_id(cve_id=all_cves[cve].number)
293 self._generate_elements_from_cve(all_cves[cve], accepted_releases, root_element, running_kernel_id, pkg_cache, fixed_versions)
294
295- self._write_oval_xml(xml_tree, root_element)
296+ if self._eligible_for_output(release):
297+ self._write_oval_xml(xml_tree, root_element)
298
299 class OvalGeneratorUSNs(OvalGenerator):
300 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:
301@@ -2646,3 +2670,7 @@ class OvalGeneratorUSN():
302 # remove tmp dir if empty
303 if not os.listdir(self.tmpdir):
304 os.rmdir(self.tmpdir)
305+
306+def find_release_codename(release):
307+ return cve_lib.release_progenitor(release) if cve_lib.release_progenitor(release) else release.replace('/', '_')
308+

Subscribers

People subscribed via source and target branches