Merge ~litios/ubuntu-cve-tracker:feature/data-lib-rel-storage into ubuntu-cve-tracker:master
- Git
- lp:~litios/ubuntu-cve-tracker
- feature/data-lib-rel-storage
- Merge into master
Status: | Merged |
---|---|
Merge reported by: | David Fernandez Gonzalez |
Merged at revision: | c50841adf9f42538dced48ea01465abe484f4ddc |
Proposed branch: | ~litios/ubuntu-cve-tracker:feature/data-lib-rel-storage |
Merge into: | ubuntu-cve-tracker:master |
Diff against target: |
561 lines (+229/-74) 4 files modified
dev/null (+0/-20) scripts/datalib/storage.py (+46/-0) scripts/datalib/uct_models.py (+12/-16) scripts/datalib/uct_storage.py (+171/-38) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eduardo Barretto | Approve | ||
Nick Galanis | Pending | ||
Review via email:
|
Commit message
Description of the change
Add ReleaseStorage class to datalib.
This class will handle the Release objects, avoiding duplication and handling the links among themselves.
Furthermore, it will provide convenient features for retrieving specific sets of releases.
As an extra, package cache loading is not sequential by default, due to the high memory consumption when performed in parallel.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
David Fernandez Gonzalez (litios) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
David Fernandez Gonzalez (litios) wrote : | # |
Output without subprojects:
Trusty release loaded as "trusty" ubuntu/trusty
Trusty release loaded as "ubuntu/trusty" ubuntu/trusty
* ESM Apps None
* ESM Infra esm/trusty
* ESM Infra parent ubuntu/trusty
Bionic release loaded as "bionic" ubuntu/bionic
Bionic release loaded as "ubuntu/bionic" ubuntu/bionic
* ESM Apps esm-apps/bionic
* ESM Infra esm-infra/bionic
* ESM Infra parent ubuntu/bionic
* ESM Apps parent esm-infra/bionic
Jammy release loaded as "jammy" ubuntu/jammy
Jammy release loaded as "ubuntu/jammy" ubuntu/jammy
* ESM Apps esm-apps/jammy
* ESM Infra None
* ESM Apps parent ubuntu/jammy
All supported releases [esm-apps/jammy, fips-updates/focal, esm/trusty, esm-apps/focal, ros-esm/melodic, fips/xenial, esm-apps/bionic, fips/bionic, ubuntu/jammy, esm-apps/xenial, esm-infra/xenial, ubuntu/mantic, esm-infra/bionic, fips-updates/
ESM Apps releases [esm-apps/jammy, esm-apps/noble, esm-apps/focal, esm-apps/bionic, esm-apps/xenial]
ESM Infra releases [esm-infra/bionic, esm/trusty, esm-infra/xenial]
Ubuntu product releases [ubuntu/warty, ubuntu/hoary, ubuntu/breezy, ubuntu/dapper, ubuntu/edgy, ubuntu/feisty, ubuntu/gutsy, ubuntu/hardy, ubuntu/intrepid, ubuntu/jaunty, ubuntu/karmic, ubuntu/lucid, ubuntu/maverick, ubuntu/natty, ubuntu/oneiric, ubuntu/precise, ubuntu/quantal, ubuntu/raring, ubuntu/saucy, ubuntu/trusty, ubuntu/utopic, ubuntu/vivid, ubuntu/wily, ubuntu/xenial, ubuntu/yakkety, ubuntu/zesty, ubuntu/artful, ubuntu/bionic, ubuntu/cosmic, ubuntu/disco, ubuntu/eoan, ubuntu/focal, ubuntu/groovy, ubuntu/hirsute, ubuntu/impish, ubuntu/jammy, ubuntu/kinetic, ubuntu/lunar, ubuntu/mantic, ubuntu/noble, ubuntu/oracular]
Ubuntu product releases -- supported only [ubuntu/jammy, ubuntu/mantic, ubuntu/noble, ubuntu/focal]
Devel release: ubuntu/oracular
Loading 50236 CVEs
Loading 21 releases
Couldn't find package cache for release ros-esm/melodic with filename ros-esm_melodic
Couldn't find package cache for release ros-esm/kinetic with filename ros-esm_kinetic
Couldn't find package cache for release snap with filename snap
Loading 5171 USNs
Total loading time: 27.690572500228882
------------------
Showing CVE-2023-1625
------------------
CVE-2023-1625
Description: An information leak was discovered in OpenStack heat. This issue could allow a remote, authenticated attacker to use the 'stack show' command to reveal parameters which are supposed to remain hidden. This has a low impact to the confidentiality, integrity, and availability of the system.
References: ['https:/
Notes: [['mdeslaur', 'fixed in jammy-updates and kinetic-updates as a SRU, but not\nyet in the -security pocket.']]
- CVE-2023-1625 heat ubuntu/focal released 1:14.2.0-0ubuntu1.1
- CVE-2023-1625 heat ubuntu/jammy released 1:18.0.1-0ubuntu1.1
- CVE-2023-1625 heat ubuntu/mantic not-affected 1:20.0.0-0ubuntu1
- CVE-2023-1625 heat ubuntu/noble not-affected 1:20.0.0-0ubuntu1
------------------
Loading jammy into the ...
- 9bafe15... by David Fernandez Gonzalez
-
[DATALIB] Fix release loading to include parents
Signed-off-by: David Fernandez Gonzalez <email address hidden>
- c6132b2... by David Fernandez Gonzalez
-
[DATALIB] Don't include embargoed dir
Signed-off-by: David Fernandez Gonzalez <email address hidden>
- 915557b... by David Fernandez Gonzalez
-
[DATALIB] Infer USN ESM release by pocket instead of version
Signed-off-by: David Fernandez Gonzalez <email address hidden>
- c50841a... by David Fernandez Gonzalez
-
[DATALIB] Add DirtyCache class
This commits add a cache system to prevent relinking objects
if the storage classes haven't been updated since the last time
they were linked.Signed-off-by: David Fernandez Gonzalez <email address hidden>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
David Fernandez Gonzalez (litios) wrote : | # |
More changes:
* Fix release loading so that when asking for a release, the parents are also loaded.
* Prevent the embargoed CVE dir from being loaded by mistake.
* Since USNs don't specify ESM releases, do best effort to actually figure out the ESM release based on binary pockets
* Implement cache system to avoid relinking objects if the storage classes have not changed since the object was linked before. When loading all USNs, total time went from +70 sec to 30sec.
Testing:
Loading 50228 CVEs
Loading 3 releases
Loading 5219 USNs
*** Done loading
*** Loading USN
Cache:: Marking everything dirty
USN 6812-1 :: Dirty - relink
Cache:: Marking everything dirty
CVE CVE-2024-21012 :: Dirty - relink
CVE CVE-2024-21011 :: Dirty - relink
CVE CVE-2024-21094 :: Dirty - relink
CVE CVE-2024-21068 :: Dirty - relink
*** Loading CVE
CVE CVE-2023-1625 :: Dirty - relink
*** Loading again; cache should reuse
*** Loading USN
USN 6812-1 :: Clean - reuse
*** Loading CVE
CVE CVE-2023-1625 :: Clean - reuse
*** Loading jammy
Loading 1 releases
*** Again with new release; it should relink
*** Loading USN
Cache:: Marking everything dirty
USN 6812-1 :: Dirty - relink
Cache:: Marking everything dirty
CVE CVE-2024-21012 :: Dirty - relink
CVE CVE-2024-21011 :: Dirty - relink
CVE CVE-2024-21094 :: Dirty - relink
CVE CVE-2024-21068 :: Dirty - relink
*** Loading CVE
CVE CVE-2023-1625 :: Dirty - relink
CVE CVE-2024-21011 :: Clean - reuse
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Eduardo Barretto (ebarretto) wrote : | # |
lgtm, thanks!
Preview Diff
1 | diff --git a/scripts/datalib/storage.py b/scripts/datalib/storage.py |
2 | index fc5c271..308be59 100644 |
3 | --- a/scripts/datalib/storage.py |
4 | +++ b/scripts/datalib/storage.py |
5 | @@ -12,13 +12,50 @@ |
6 | from datalib.uct_models import SourcePackage |
7 | from .models import * |
8 | |
9 | +class DirtyCache: |
10 | + """Cache class for handling modifications""" |
11 | + dirty_cache: dict[str: bool] |
12 | + last_updated: int |
13 | + others_last_updated: list[int] |
14 | + |
15 | + def __init__(self) -> None: |
16 | + self.dirty_cache = {} |
17 | + self.others_last_updated = [] |
18 | + self.last_updated = 0 |
19 | + |
20 | + def is_dirty(self, id: str, others_last_updated: list[int]) -> bool: |
21 | + if not self.others_last_updated: |
22 | + self.mark_dirty() |
23 | + else: |
24 | + for index in range(len(others_last_updated)): |
25 | + if self.others_last_updated[index] < others_last_updated[index]: |
26 | + self.mark_dirty() |
27 | + break |
28 | + self.others_last_updated = others_last_updated |
29 | + return self.dirty_cache[id] |
30 | + |
31 | + def add(self, id: str) -> None: |
32 | + self.dirty_cache[id] = True |
33 | + |
34 | + def make_clean(self, id: str) -> None: |
35 | + self.dirty_cache[id] = False |
36 | + |
37 | + def mark_dirty(self) -> None: |
38 | + for item in self.dirty_cache: |
39 | + self.dirty_cache[item] = True |
40 | + |
41 | + def increment_last_updated(self) -> None: |
42 | + self.last_updated += 1 |
43 | + |
44 | class PackageStorage: |
45 | """ |
46 | Storage class for handling packages. |
47 | """ |
48 | packages: dict[str: SourcePackage] |
49 | + dirty_cache: DirtyCache |
50 | |
51 | def __init__(self) -> None: |
52 | + self.dirty_cache = DirtyCache() |
53 | self.packages = {} |
54 | |
55 | def _order_packages(self) -> None: |
56 | @@ -34,11 +71,13 @@ class CVEStorage: |
57 | """ |
58 | cves: dict[str: CVE] |
59 | package_storage: PackageStorage |
60 | + dirty_cache: DirtyCache |
61 | |
62 | # TODO: add a changed cache so we can cache CVEs and only update when something got loaded |
63 | |
64 | def __init__(self) -> None: |
65 | self.cves = {} |
66 | + self.dirty_cache = DirtyCache() |
67 | |
68 | def link_pkg_storage(self, package_storage: PackageStorage) -> None: |
69 | self.package_storage = package_storage |
70 | @@ -50,6 +89,7 @@ class CVEStorage: |
71 | def _init_cve(self, cve: CVE, pkg_data: dict) -> None: |
72 | """Internal function to add CVE object to the class storage""" |
73 | self.cves[cve.id] = (cve, pkg_data) |
74 | + self.dirty_cache.add(cve.id) |
75 | |
76 | def load_cve(self, cve_id: str) -> None: |
77 | """Load a CVE into the storage""" |
78 | @@ -68,9 +108,15 @@ class SNStorage: |
79 | sns: dict[str: SN] |
80 | package_storage: PackageStorage |
81 | cve_storage: CVEStorage |
82 | + dirty_cache: DirtyCache |
83 | |
84 | def __init__(self) -> None: |
85 | self.sns = {} |
86 | + self.dirty_cache = DirtyCache() |
87 | + |
88 | + def _init_sn(self, sn_object: SN, cves: set, pkgs: dict): |
89 | + self.sns[sn_object.id] = (sn_object, cves, pkgs) |
90 | + self.dirty_cache.add(sn_object.id) |
91 | |
92 | def _order_sns(self) -> None: |
93 | self.sns = dict(sorted(self.sns.items(), |
94 | diff --git a/scripts/datalib/uct_models.py b/scripts/datalib/uct_models.py |
95 | index 7b9fe01..31568f1 100644 |
96 | --- a/scripts/datalib/uct_models.py |
97 | +++ b/scripts/datalib/uct_models.py |
98 | @@ -20,10 +20,13 @@ class Release: |
99 | codename: str |
100 | product: str |
101 | series: str |
102 | + is_devel: bool |
103 | is_esm: bool |
104 | is_active: bool |
105 | requires_oval: bool |
106 | parent: "Release" |
107 | + esm_infra_release: "Release" |
108 | + esm_apps_release: "Release" |
109 | |
110 | def __init__(self, name: str) -> None: |
111 | canon, product, series, details = get_subproject_details(name) |
112 | @@ -39,19 +42,27 @@ class Release: |
113 | self.parent = None |
114 | self.version = None |
115 | self.codename = None |
116 | + self.esm_infra_release = None |
117 | + self.esm_apps_release = None |
118 | + self.is_devel = False |
119 | if not details: return |
120 | if 'oval' in details: |
121 | self.requires_oval = details['oval'] |
122 | else: |
123 | self.requires_oval = True |
124 | if 'parent' in details: |
125 | - self.parent = Release(details['parent']) |
126 | + self.parent = details['parent'] |
127 | if 'version' in details: |
128 | self.version = details['version'] |
129 | if 'codename' in details: |
130 | self.codename = details['codename'] |
131 | + if 'codename' in details: |
132 | + self.codename = details['codename'] |
133 | + if 'devel' in details: |
134 | + self.is_devel = details['devel'] |
135 | |
136 | def __eq__(self, value: object) -> bool: |
137 | + if not hasattr(value, 'name'): return False |
138 | return self.name == value.name |
139 | |
140 | def __repr__(self) -> str: |
141 | @@ -60,21 +71,6 @@ class Release: |
142 | def __hash__(self): |
143 | return hash(self.name) |
144 | |
145 | - def get_esm_release(self, component: str) -> "Release": |
146 | - esm_releases = get_active_esm_releases() |
147 | - potential_releases = list(filter(lambda release: self.series in release, esm_releases)) |
148 | - if len(potential_releases) == 1: |
149 | - return Release(potential_releases[0]) |
150 | - elif len(potential_releases) == 2: |
151 | - if component in ['main', 'restricted']: |
152 | - target = 'esm-infra' |
153 | - elif component in ['universe', 'multiverse']: |
154 | - target = 'esm-apps' |
155 | - |
156 | - if target in potential_releases[0]: return Release(potential_releases[0]) |
157 | - else: return Release(potential_releases[1]) |
158 | - |
159 | - return None |
160 | def is_parent_release(self, release: "Release") -> bool: |
161 | return release in self.get_all_parents() |
162 | |
163 | diff --git a/scripts/datalib/uct_storage.py b/scripts/datalib/uct_storage.py |
164 | index 14f7b98..28cacea 100644 |
165 | --- a/scripts/datalib/uct_storage.py |
166 | +++ b/scripts/datalib/uct_storage.py |
167 | @@ -12,7 +12,6 @@ |
168 | from .storage import * |
169 | from .uct_models import * |
170 | from .config import PKG_CACHE_DIR, USN_DATABASE |
171 | -from .utils import get_active_ubuntu_releases |
172 | from .config import UCT |
173 | |
174 | import json |
175 | @@ -20,6 +19,77 @@ import os |
176 | import cve_lib |
177 | from multiprocessing import Pool |
178 | |
179 | +class ReleaseStorage(): |
180 | + releases: list[str: Release] |
181 | + supported_releases: list[Release] |
182 | + esm_apps_releases: list[Release] |
183 | + esm_infra_releases: list[Release] |
184 | + |
185 | + def __init__(self) -> None: |
186 | + self.releases = {} |
187 | + self.supported_releases = [] |
188 | + self.esm_apps_releases = [] |
189 | + self.esm_infra_releases = [] |
190 | + |
191 | + for release in cve_lib.all_releases: |
192 | + rel_obj = Release(release) |
193 | + self.releases[str(rel_obj)] = rel_obj |
194 | + |
195 | + # Link parents |
196 | + for release in self.releases.values(): |
197 | + if release.parent: release.parent = self.releases[release.parent] |
198 | + |
199 | + esm_releases = get_active_esm_releases() |
200 | + supported_releases_names = list(set(cve_lib.all_releases).difference(set(cve_lib.eol_releases)).difference(set([cve_lib.devel_release]))) |
201 | + esm_infra_releases_names = list(filter(lambda release: release == 'trusty/esm' or 'esm-infra/' in release, esm_releases)) |
202 | + esm_apps_releases_names = list(filter(lambda release: 'esm-apps/' in release, esm_releases)) |
203 | + |
204 | + for release in supported_releases_names: |
205 | + self.supported_releases.append(self.get_release(release)) |
206 | + for release in esm_infra_releases_names: |
207 | + rel_obj = self.get_release(release) |
208 | + self.esm_infra_releases.append(rel_obj) |
209 | + rel_obj.get_oldest_parent().esm_infra_release = rel_obj |
210 | + for release in esm_apps_releases_names: |
211 | + rel_obj = self.get_release(release) |
212 | + self.esm_apps_releases.append(rel_obj) |
213 | + rel_obj.get_oldest_parent().esm_apps_release = rel_obj |
214 | + |
215 | + def get_all_releases(self) -> list[Release]: |
216 | + return list(self.releases.values()) |
217 | + |
218 | + def get_release(self, release_name: str) -> Release: |
219 | + if release_name == 'devel': |
220 | + return self.get_devel_release() |
221 | + |
222 | + release = self.releases.get(release_name, None) |
223 | + if not release: |
224 | + release = self.releases.get(release_name.split('/')[0], None) |
225 | + |
226 | + if not release: |
227 | + for cve_lib_rel in cve_lib.subprojects: |
228 | + if 'alias' in cve_lib.subprojects[cve_lib_rel] and \ |
229 | + cve_lib.subprojects[cve_lib_rel]['alias'] == release_name: |
230 | + release = self.releases.get(cve_lib_rel, None) |
231 | + break |
232 | + |
233 | + return release |
234 | + |
235 | + def get_product_releases(self, product: str, supported_only: bool = False) -> list [Release]: |
236 | + """Get all releases that belong to a product""" |
237 | + if supported_only: |
238 | + releases = list(filter(lambda release: release.product == product, self.supported_releases)) |
239 | + else: |
240 | + releases = list(filter(lambda release: release.product == product, self.releases.values())) |
241 | + return releases |
242 | + |
243 | + def get_devel_release(self) -> Release: |
244 | + for release in self.releases.values(): |
245 | + if release.is_devel: |
246 | + return release |
247 | + |
248 | + return None |
249 | + |
250 | class UCTPackageStorage(PackageStorage): |
251 | """ |
252 | Storage class for handling UCT packages. |
253 | @@ -27,41 +97,59 @@ class UCTPackageStorage(PackageStorage): |
254 | packages: dict[str: UbuntuSourcePackage] |
255 | latest_date_created: dict[Release:datetime.datetime] |
256 | loaded_releases: set[Release] |
257 | + release_storage: ReleaseStorage |
258 | |
259 | def __init__(self) -> None: |
260 | self.loaded_releases = set() |
261 | self.latest_date_created = {} |
262 | + self.release_storage = None |
263 | super().__init__() |
264 | |
265 | - def load(self, filter_releases: list = []) -> None: |
266 | - final_releases = [] |
267 | - active_ubuntu_releases = get_active_ubuntu_releases() |
268 | - for release in active_ubuntu_releases: |
269 | - if filter_releases and release not in filter_releases: continue |
270 | - final_releases.append(release) |
271 | - |
272 | - with Pool(processes=os.cpu_count()) as pool: |
273 | - print(f'Loading {len(final_releases)} releases') |
274 | - package_data = pool.map(self._load_release_from_cache, final_releases) |
275 | + def load(self, filter_releases: list[Release] = [], multithreading: bool = False) -> None: |
276 | + self.dirty_cache.increment_last_updated() |
277 | + releases = self.release_storage.supported_releases if not filter_releases else filter_releases |
278 | + final_releases = set() |
279 | + |
280 | + for release in releases: |
281 | + final_releases.add(release) |
282 | + while release.parent: |
283 | + release = release.parent |
284 | + if release not in final_releases: |
285 | + final_releases.add(release) |
286 | + |
287 | + print(f'Loading {len(final_releases)} releases') |
288 | + if multithreading: |
289 | + with Pool(processes=os.cpu_count()) as pool: |
290 | + package_data = pool.map(self._load_release_from_cache, final_releases) |
291 | + else: |
292 | + package_data = [] |
293 | + for release in final_releases: |
294 | + package_data.append(self._load_release_from_cache(release)) |
295 | |
296 | for release, release_data, last_date_created in package_data: |
297 | self._init_release(release, release_data, last_date_created) |
298 | |
299 | self._order_packages() |
300 | |
301 | - def _load_release_from_cache(self, release: str) -> dict: |
302 | + def _load_release_from_cache(self, release: Release) -> dict: |
303 | data = {} |
304 | + pkg_cache_filename = [] |
305 | + if release.name == 'esm/trusty': |
306 | + pkg_cache_filename = 'trusty_esm' |
307 | + elif release.product == 'ubuntu': |
308 | + pkg_cache_filename = release.series |
309 | + else: |
310 | + pkg_cache_filename = release.name.replace('/', '_') |
311 | |
312 | try: |
313 | - with open(os.path.join(os.path.expanduser(PKG_CACHE_DIR), release.replace('/', '_') + '-pkg-cache.json')) as f: |
314 | + with open(os.path.join(os.path.expanduser(PKG_CACHE_DIR), pkg_cache_filename + '-pkg-cache.json')) as f: |
315 | data = json.load(f) |
316 | - except FileNotFoundError as ex: |
317 | - print(ex) |
318 | + except FileNotFoundError: |
319 | + print(f'Couldn\'t find package cache for release {release.name} with filename {pkg_cache_filename}') |
320 | return (release, None, None) |
321 | |
322 | release_data = {} |
323 | latest_date_created = None |
324 | - release_obj = Release(release) |
325 | for src_pkg_name, src_pkg_versions in data.items(): |
326 | if src_pkg_name == 'latest_date_created': |
327 | latest_date_created = datetime.datetime.fromtimestamp(src_pkg_versions) |
328 | @@ -71,7 +159,7 @@ class UCTPackageStorage(PackageStorage): |
329 | release_data[src_pkg_name] = source_package |
330 | for src_pkg_version, src_pkg_version_data in src_pkg_versions.items(): |
331 | src_pkg_version_obj = UbuntuVersion(src_pkg_version) |
332 | - source_package.add_source_version(src_pkg_version_obj, release_obj, |
333 | + source_package.add_source_version(src_pkg_version_obj, release, |
334 | src_pkg_version_data['component'], |
335 | src_pkg_version_data['pocket']) |
336 | |
337 | @@ -82,7 +170,7 @@ class UCTPackageStorage(PackageStorage): |
338 | source_package, |
339 | binary_data['component'])) |
340 | |
341 | - return (release_obj, release_data, latest_date_created) |
342 | + return (release, release_data, latest_date_created) |
343 | |
344 | def _init_release(self, release: Release, release_data: dict, latest_date_created: str) -> None: |
345 | if not release_data: return |
346 | @@ -98,13 +186,13 @@ class UCTPackageStorage(PackageStorage): |
347 | def get_package(self, pkg_name: str) -> UbuntuSourcePackage: |
348 | return super().get_package(pkg_name) |
349 | |
350 | - def load_release(self, release:str) -> None: |
351 | - if Release(release) in self.loaded_releases: return |
352 | + def load_release(self, release: Release) -> None: |
353 | + if self.release_storage.get_release(release) in self.loaded_releases: return |
354 | release_obj, data, date = self._load_release_from_cache(release) |
355 | self._init_release(release_obj, data, date) |
356 | |
357 | - def unload_release(self, release: str) -> None: |
358 | - release = Release(release) |
359 | + def unload_release(self, release: Release) -> None: |
360 | + release = self.release_storage.get_release(release) |
361 | if release not in self.loaded_releases: return |
362 | self.loaded_releases.remove(release) |
363 | |
364 | @@ -113,6 +201,9 @@ class UCTPackageStorage(PackageStorage): |
365 | for version in original_source_versions: |
366 | ver_rel = package.get_version_release(version) |
367 | if ver_rel == release: package.remove_version(version) |
368 | + |
369 | + def link_release_storage(self, release_storage: ReleaseStorage) -> None: |
370 | + self.release_storage = release_storage |
371 | |
372 | class UCTCVEStorage(CVEStorage): |
373 | """ |
374 | @@ -120,10 +211,18 @@ class UCTCVEStorage(CVEStorage): |
375 | """ |
376 | cves: dict[str: UbuntuCVE] |
377 | package_storage: UCTPackageStorage |
378 | + release_storage: ReleaseStorage |
379 | |
380 | # TODO: add a changed cache so we can cache CVEs and only update when something got loaded |
381 | + def __init__(self) -> None: |
382 | + self.release_storage = None |
383 | + super().__init__() |
384 | + |
385 | + def link_release_storage(self, release_storage: ReleaseStorage) -> None: |
386 | + self.release_storage = release_storage |
387 | |
388 | def load(self, cves_filter: list = []) -> None: |
389 | + self.dirty_cache.increment_last_updated() |
390 | final_cves = [] |
391 | cves_ids = self.get_uct_cve_ids() |
392 | for cve_id in cves_ids: |
393 | @@ -171,11 +270,15 @@ class UCTCVEStorage(CVEStorage): |
394 | |
395 | return (cve, data['pkgs']) |
396 | |
397 | - def get_uct_cve_ids(self) -> list: |
398 | + def get_uct_cve_ids(self, embargoed: bool = False) -> list: |
399 | """Return all CVE IDs identified from UCT""" |
400 | |
401 | cve_ids = [] |
402 | - for dir in cve_lib.cve_dirs: |
403 | + cve_dirs = [cve_lib.active_dir, cve_lib.retired_dir, cve_lib.ignored_dir] |
404 | + if embargoed: |
405 | + cve_dirs.append(cve_lib.embargoed_dir) |
406 | + |
407 | + for dir in cve_dirs: |
408 | for (_, _, filenames) in os.walk(dir): |
409 | for filename in filenames: |
410 | if not re.search(CVE.ID_REGEX, filename): continue |
411 | @@ -212,7 +315,8 @@ class UCTCVEStorage(CVEStorage): |
412 | package = self.package_storage.get_package(package_name) |
413 | if not package: continue |
414 | for release, status in package_entries.items(): |
415 | - release = Release(release) |
416 | + release = self.release_storage.get_release(release) |
417 | + if not release: continue |
418 | if not package.release_exists(release, include_parents=True): continue |
419 | cve.add_cve_entry(UbuntuCVEPkgEntry(package, cve, status[0], status[1], release)) |
420 | |
421 | @@ -228,23 +332,36 @@ class UCTCVEStorage(CVEStorage): |
422 | if cve_id not in self.cves: return None |
423 | |
424 | cve, _ = self.cves[cve_id] |
425 | + if not self.dirty_cache.is_dirty(cve_id, [self.package_storage.dirty_cache.last_updated]): |
426 | + return cve |
427 | + |
428 | cve.pkg_entries = list() |
429 | if not with_pkg_links: return cve |
430 | self.populate_cve_entries(cve_id) |
431 | |
432 | + self.dirty_cache.make_clean(cve_id) |
433 | return cve |
434 | |
435 | class USNStorage(SNStorage): |
436 | sns: dict[str: tuple[USN, list, dict]] |
437 | package_storage: UCTPackageStorage |
438 | cve_storage: UCTCVEStorage |
439 | + release_storage: ReleaseStorage |
440 | + |
441 | + def __init__(self) -> None: |
442 | + self.release_storage = None |
443 | + super().__init__() |
444 | + |
445 | + def link_release_storage(self, release_storage: ReleaseStorage) -> None: |
446 | + self.release_storage = release_storage |
447 | |
448 | def load(self) -> None: |
449 | + self.dirty_cache.increment_last_updated() |
450 | usns_data = self._get_usn_database_data() |
451 | print(f'Loading {len(usns_data)} USNs') |
452 | for usn_id, usn_data in usns_data.items(): |
453 | _, _, cves, lp_bugs, pkgs = self.process_usn_data((usn_id, usn_data)) |
454 | - self.add_usn(usn_id, usn_data, cves, lp_bugs, pkgs) |
455 | + self._init_sn(USN(usn_id, usn_data, set(), dict(), lp_bugs), cves, pkgs) |
456 | |
457 | self._order_sns() |
458 | |
459 | @@ -265,7 +382,21 @@ class USNStorage(SNStorage): |
460 | # Get package |
461 | for pkg, pkg_info in info['sources'].items(): |
462 | pkgs.setdefault(pkg, []) |
463 | - pkgs[pkg].append((pkg_info['version'], rel)) |
464 | + pocket = None |
465 | + # Best effort, there are too many corner cases |
466 | + # since the format is not always the same for all |
467 | + # USNs |
468 | + if 'allbinaries' in info: |
469 | + for binary_data in info['allbinaries'].values(): |
470 | + if 'source' in binary_data and binary_data['source'] != pkg: continue |
471 | + if 'pocket' not in binary_data: continue |
472 | + if pocket and binary_data['pocket'] != pocket: |
473 | + # Different pocket in binaries, default to parent |
474 | + pocket = None |
475 | + break |
476 | + elif not pocket: |
477 | + pocket = binary_data['pocket'] |
478 | + pkgs[pkg].append((pkg_info['version'], pocket, rel)) |
479 | |
480 | # CVE loading |
481 | for cve_text in usn_data['cves']: |
482 | @@ -276,9 +407,6 @@ class USNStorage(SNStorage): |
483 | cves.add(cve_text) |
484 | |
485 | return usn_id, usn_data, cves, lp_bugs, pkgs |
486 | - |
487 | - def add_usn(self, usn_id: str, usn_data: dict, cves: set, lp_bugs: set, pkgs: dict) -> None: |
488 | - self.sns[usn_id] = (USN(usn_id, usn_data, set(), dict(), lp_bugs), cves, pkgs) |
489 | |
490 | def get_usns_by_cve(self, cve_id: str) -> list[USN]: |
491 | target_usns = [] |
492 | @@ -291,6 +419,11 @@ class USNStorage(SNStorage): |
493 | def get_usn(self, usn_id: str) -> USN: |
494 | if usn_id not in self.sns: return None |
495 | usn, cves, pkgs = self.sns[usn_id] |
496 | + if not self.dirty_cache.is_dirty(usn_id, |
497 | + [self.cve_storage.dirty_cache.last_updated, |
498 | + self.package_storage.dirty_cache.last_updated]): |
499 | + return usn |
500 | + |
501 | cve_mapping = set() |
502 | pkg_mapping = dict() |
503 | |
504 | @@ -302,22 +435,22 @@ class USNStorage(SNStorage): |
505 | for pkg_name in pkgs: |
506 | pkg = self.package_storage.get_package(pkg_name) |
507 | if not pkg: continue |
508 | - for version, release in pkgs[pkg_name]: |
509 | - release = Release(release) |
510 | + for version, pocket, release in pkgs[pkg_name]: |
511 | + release = self.release_storage.get_release(release) |
512 | + if not release: continue |
513 | if not pkg.release_exists(release, include_parents=True): continue |
514 | pkg_mapping.setdefault(pkg, []) |
515 | version = UbuntuVersion(version) |
516 | - if version.is_esm(): |
517 | - for pkg_version in pkg.source_versions: |
518 | - if pkg.get_version_release(pkg_version) == release: |
519 | - release = release.get_esm_release(pkg.get_version_component(pkg_version)) |
520 | - break |
521 | + if pocket == 'esm-apps': |
522 | + release = release.esm_apps_release |
523 | + elif pocket == 'esm-infra': |
524 | + release = release.esm_infra_release |
525 | |
526 | pkg_mapping[pkg].append((version, release)) |
527 | |
528 | usn.cves = cve_mapping |
529 | usn.package_fixed_versions = pkg_mapping |
530 | |
531 | + self.dirty_cache.make_clean(usn_id) |
532 | return usn |
533 | |
534 | - |
535 | diff --git a/scripts/datalib/utils.py b/scripts/datalib/utils.py |
536 | deleted file mode 100644 |
537 | index cc072e0..0000000 |
538 | --- a/scripts/datalib/utils.py |
539 | +++ /dev/null |
540 | @@ -1,20 +0,0 @@ |
541 | -#!/usr/bin/python3 |
542 | -# -*- coding: utf-8 -*- |
543 | -# Module containing classes that represent CVE and Package data |
544 | -# |
545 | -# Author: David Fernandez Gonzalez <david.fernandezgonzalez@canonical.com> |
546 | -# Copyright (C) 2024 Canonical Ltd. |
547 | -# |
548 | -# This script is distributed under the terms and conditions of the GNU General |
549 | -# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html |
550 | -# for details. |
551 | -# |
552 | -import cve_lib |
553 | - |
554 | -def get_active_ubuntu_releases(): |
555 | - active_ubuntu_releases = [] |
556 | - for release in cve_lib.all_releases: |
557 | - if release not in cve_lib.eol_releases or release in cve_lib.get_active_releases_with_esm(): |
558 | - active_ubuntu_releases.append(release) |
559 | - |
560 | - return active_ubuntu_releases |
561 | \ No newline at end of file |
Testing script:
from datalib import *
release_storage = ReleaseStorage()
trusty = release_ storage. get_release( 'trusty' ) storage. get_release( 'ubuntu/ trusty' ) trusty" ',trusty) esm_apps_ release) esm_infra_ release) esm_infra_ release. parent)
print('Trusty release loaded as "trusty"', trusty)
trusty = release_
print('Trusty release loaded as "ubuntu/
print(' * ESM Apps', trusty.
print(' * ESM Infra', trusty.
print(' * ESM Infra parent', trusty.
bionic = release_ storage. get_release( 'bionic' ) storage. get_release( 'ubuntu/ bionic' ) bionic" ',bionic) esm_apps_ release) esm_infra_ release) esm_infra_ release. parent) esm_apps_ release. parent)
print('Bionic release loaded as "bionic"',bionic)
bionic = release_
print('Bionic release loaded as "ubuntu/
print(' * ESM Apps', bionic.
print(' * ESM Infra', bionic.
print(' * ESM Infra parent', bionic.
print(' * ESM Apps parent', bionic.
jammy = release_ storage. get_release( 'jammy' ) storage. get_release( 'ubuntu/ jammy') jammy"' ,jammy) apps_release) infra_release) apps_release. parent)
print('Jammy release loaded as "jammy"',jammy)
jammy = release_
print('Jammy release loaded as "ubuntu/
print(' * ESM Apps', jammy.esm_
print(' * ESM Infra', jammy.esm_
print(' * ESM Apps parent', jammy.esm_
print('All supported releases', release_ storage. supported_ releases) storage. esm_apps_ releases) storage. esm_infra_ releases)
print('ESM Apps releases', release_
print('ESM Infra releases', release_
print('Ubuntu product releases', release_ storage. get_product_ releases( 'ubuntu' )) storage. get_product_ releases( 'ubuntu' , supported_ only=True) )
print('Ubuntu product releases -- supported only', release_
print('Devel release:', release_ storage. get_devel_ release( ))
import time
now = time.time()
cve_storage = UCTCVEStorage() link_release_ storage( release_ storage)
cve_storage.
cve_storage.load()
package_storage = UCTPackageStorage() storage. link_release_ storage( release_ storage) storage. load(filter_ releases= release_ storage. supported_ releases)
package_
package_
usn_storage = USNStorage() link_release_ storage( release_ storage)
usn_storage.
usn_storage.load()
cve_storage. link_pkg_ storage( package_ storage) link_pkg_ storage( package_ storage) link_cve_ storage( cve_storage)
usn_storage.
usn_storage.
later = time.time()
print('Total loading time: ', later - now)
# gpac = package_ storage. get_package( 'python- idna') versions: info[version] ['release' ], version) version] :
# for version in gpac.source_
# print(gpac, gpac.source_
# for binary in gpac.binaries[
# print(f' - {binary.name} {binary.version} {binary.arches}')
# print(list( usn_storage. sns.keys( )))
print() ------- ------- ----') ------- ------- ----') get_cve( 'CVE-2023- 1625') Description: ', cve.description, '\n') References: ', cve.references, '\n')
print('
print('Showing CVE-2023-1625')
print('
cve = cve_storage.
print(cve.id, '\n')
print('
print('
print('Notes:', cve.notes,'\n')
for entry in cve.pkg_entries:
print(' -', entry.cve, entry.pkg, entry.release, entry.status, entry.note)
print() ------- ------- ----') storage. load_release( 'jammy' ) get_cve( 'CVE-2024- 3651') ------- ------- ----')
print('
print('Loading jammy into the storage')
package_
cve = cve_storage.
print('
print('Showing CVE entries now')
for entry in ...