diff options
author | Marta Rybczynska <rybczynska@gmail.com> | 2023-01-03 15:03:59 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-02-13 07:44:09 +0000 |
commit | a4eed2134159929d699520a7445d6f70a13c2578 (patch) | |
tree | c4d5ce21f783853e89814b9989db8a8fb4cf73a5 /meta/recipes-core/meta/cve-update-db-native.bb | |
parent | 4d69f690821fa2256680c078d2b477e541f6a836 (diff) | |
download | poky-a4eed2134159929d699520a7445d6f70a13c2578.tar.gz |
cve-update-db-native: avoid incomplete updates
The database update has been done on the original file. In case of
network connection issues, temporary outage of the NVD server or
a similar situation, the function could exit with incomplete data
in the database. This patch solves the issue by performing the update
on a copy of the database. It replaces the main one only if the whole
update was successful.
See https://bugzilla.yoctoproject.org/show_bug.cgi?id=14929
Reported-by: Alberto Pianon <alberto@pianon.eu>
(From OE-Core rev: 6a219c50ee12b7fb584e2db3e4dde171903acfb7)
Signed-off-by: Marta Rybczynska <marta.rybczynska@linaro.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 8efe99214d8b005f0ecac690ce5ba17b31758f92)
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core/meta/cve-update-db-native.bb')
-rw-r--r-- | meta/recipes-core/meta/cve-update-db-native.bb | 83 |
1 files changed, 61 insertions, 22 deletions
diff --git a/meta/recipes-core/meta/cve-update-db-native.bb b/meta/recipes-core/meta/cve-update-db-native.bb index e267671628..28605bc13b 100644 --- a/meta/recipes-core/meta/cve-update-db-native.bb +++ b/meta/recipes-core/meta/cve-update-db-native.bb | |||
@@ -21,6 +21,8 @@ CVE_DB_UPDATE_INTERVAL ?= "86400" | |||
21 | CVE_SOCKET_TIMEOUT ?= "60" | 21 | CVE_SOCKET_TIMEOUT ?= "60" |
22 | NVDCVE_URL ?= "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-" | 22 | NVDCVE_URL ?= "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-" |
23 | 23 | ||
24 | CVE_DB_TEMP_FILE ?= "${CVE_CHECK_DB_DIR}/temp_nvdcve_1.1.db" | ||
25 | |||
24 | python () { | 26 | python () { |
25 | if not bb.data.inherits_class("cve-check", d): | 27 | if not bb.data.inherits_class("cve-check", d): |
26 | raise bb.parse.SkipRecipe("Skip recipe when cve-check class is not loaded.") | 28 | raise bb.parse.SkipRecipe("Skip recipe when cve-check class is not loaded.") |
@@ -32,25 +34,15 @@ python do_fetch() { | |||
32 | """ | 34 | """ |
33 | import bb.utils | 35 | import bb.utils |
34 | import bb.progress | 36 | import bb.progress |
35 | import sqlite3, urllib, urllib.parse, shutil, gzip | 37 | import shutil |
36 | from datetime import date | ||
37 | 38 | ||
38 | bb.utils.export_proxies(d) | 39 | bb.utils.export_proxies(d) |
39 | 40 | ||
40 | YEAR_START = 2002 | ||
41 | |||
42 | db_file = d.getVar("CVE_CHECK_DB_FILE") | 41 | db_file = d.getVar("CVE_CHECK_DB_FILE") |
43 | db_dir = os.path.dirname(db_file) | 42 | db_dir = os.path.dirname(db_file) |
43 | db_tmp_file = d.getVar("CVE_DB_TEMP_FILE") | ||
44 | 44 | ||
45 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) | 45 | cleanup_db_download(db_file, db_tmp_file) |
46 | |||
47 | if os.path.exists("{0}-journal".format(db_file)): | ||
48 | # If a journal is present the last update might have been interrupted. In that case, | ||
49 | # just wipe any leftovers and force the DB to be recreated. | ||
50 | os.remove("{0}-journal".format(db_file)) | ||
51 | |||
52 | if os.path.exists(db_file): | ||
53 | os.remove(db_file) | ||
54 | 46 | ||
55 | # The NVD database changes once a day, so no need to update more frequently | 47 | # The NVD database changes once a day, so no need to update more frequently |
56 | # Allow the user to force-update | 48 | # Allow the user to force-update |
@@ -67,9 +59,60 @@ python do_fetch() { | |||
67 | pass | 59 | pass |
68 | 60 | ||
69 | bb.utils.mkdirhier(db_dir) | 61 | bb.utils.mkdirhier(db_dir) |
62 | if os.path.exists(db_file): | ||
63 | shutil.copy2(db_file, db_tmp_file) | ||
64 | |||
65 | if update_db_file(db_tmp_file, d) == True: | ||
66 | # Update downloaded correctly, can swap files | ||
67 | shutil.move(db_tmp_file, db_file) | ||
68 | else: | ||
69 | # Update failed, do not modify the database | ||
70 | bb.note("CVE database update failed") | ||
71 | os.remove(db_tmp_file) | ||
72 | } | ||
73 | |||
74 | do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}" | ||
75 | do_fetch[file-checksums] = "" | ||
76 | do_fetch[vardeps] = "" | ||
77 | |||
78 | def cleanup_db_download(db_file, db_tmp_file): | ||
79 | """ | ||
80 | Cleanup the download space from possible failed downloads | ||
81 | """ | ||
82 | |||
83 | # Clean up the updates done on the main file | ||
84 | # Remove it only if a journal file exists - it means a complete re-download | ||
85 | if os.path.exists("{0}-journal".format(db_file)): | ||
86 | # If a journal is present the last update might have been interrupted. In that case, | ||
87 | # just wipe any leftovers and force the DB to be recreated. | ||
88 | os.remove("{0}-journal".format(db_file)) | ||
89 | |||
90 | if os.path.exists(db_file): | ||
91 | os.remove(db_file) | ||
92 | |||
93 | # Clean-up the temporary file downloads, we can remove both journal | ||
94 | # and the temporary database | ||
95 | if os.path.exists("{0}-journal".format(db_tmp_file)): | ||
96 | # If a journal is present the last update might have been interrupted. In that case, | ||
97 | # just wipe any leftovers and force the DB to be recreated. | ||
98 | os.remove("{0}-journal".format(db_tmp_file)) | ||
99 | |||
100 | if os.path.exists(db_tmp_file): | ||
101 | os.remove(db_tmp_file) | ||
102 | |||
103 | def update_db_file(db_tmp_file, d): | ||
104 | """ | ||
105 | Update the given database file | ||
106 | """ | ||
107 | import bb.utils, bb.progress | ||
108 | from datetime import date | ||
109 | import urllib, gzip, sqlite3 | ||
110 | |||
111 | YEAR_START = 2002 | ||
112 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) | ||
70 | 113 | ||
71 | # Connect to database | 114 | # Connect to database |
72 | conn = sqlite3.connect(db_file) | 115 | conn = sqlite3.connect(db_tmp_file) |
73 | initialize_db(conn) | 116 | initialize_db(conn) |
74 | 117 | ||
75 | with bb.progress.ProgressHandler(d) as ph, open(os.path.join(d.getVar("TMPDIR"), 'cve_check'), 'a') as cve_f: | 118 | with bb.progress.ProgressHandler(d) as ph, open(os.path.join(d.getVar("TMPDIR"), 'cve_check'), 'a') as cve_f: |
@@ -87,7 +130,7 @@ python do_fetch() { | |||
87 | except urllib.error.URLError as e: | 130 | except urllib.error.URLError as e: |
88 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') | 131 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') |
89 | bb.warn("Failed to fetch CVE data (%s)" % e.reason) | 132 | bb.warn("Failed to fetch CVE data (%s)" % e.reason) |
90 | return | 133 | return False |
91 | 134 | ||
92 | if response: | 135 | if response: |
93 | for l in response.read().decode("utf-8").splitlines(): | 136 | for l in response.read().decode("utf-8").splitlines(): |
@@ -97,7 +140,7 @@ python do_fetch() { | |||
97 | break | 140 | break |
98 | else: | 141 | else: |
99 | bb.warn("Cannot parse CVE metadata, update failed") | 142 | bb.warn("Cannot parse CVE metadata, update failed") |
100 | return | 143 | return False |
101 | 144 | ||
102 | # Compare with current db last modified date | 145 | # Compare with current db last modified date |
103 | cursor = conn.execute("select DATE from META where YEAR = ?", (year,)) | 146 | cursor = conn.execute("select DATE from META where YEAR = ?", (year,)) |
@@ -118,7 +161,7 @@ python do_fetch() { | |||
118 | except urllib.error.URLError as e: | 161 | except urllib.error.URLError as e: |
119 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') | 162 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') |
120 | bb.warn("Cannot parse CVE data (%s), update failed" % e.reason) | 163 | bb.warn("Cannot parse CVE data (%s), update failed" % e.reason) |
121 | return | 164 | return False |
122 | else: | 165 | else: |
123 | bb.debug(2, "Already up to date (last modified %s)" % last_modified) | 166 | bb.debug(2, "Already up to date (last modified %s)" % last_modified) |
124 | # Update success, set the date to cve_check file. | 167 | # Update success, set the date to cve_check file. |
@@ -127,11 +170,7 @@ python do_fetch() { | |||
127 | 170 | ||
128 | conn.commit() | 171 | conn.commit() |
129 | conn.close() | 172 | conn.close() |
130 | } | 173 | return True |
131 | |||
132 | do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}" | ||
133 | do_fetch[file-checksums] = "" | ||
134 | do_fetch[vardeps] = "" | ||
135 | 174 | ||
136 | def initialize_db(conn): | 175 | def initialize_db(conn): |
137 | with conn: | 176 | with conn: |