summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Burton <ross.burton@intel.com>2019-12-08 20:35:55 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2019-12-16 23:11:10 +0000
commit1a09e2a3cf3efe67f2f01e1d1d8f65b5e337e3fb (patch)
treee80b9b3098921862a8fa57ff212c6c2165db9f45
parent309153313092159d4103d4bfa396241461bc3d8d (diff)
downloadpoky-1a09e2a3cf3efe67f2f01e1d1d8f65b5e337e3fb.tar.gz
cve-check: rewrite look to fix false negatives
A previous optimisation was premature and resulted in false-negatives in the report. Rewrite the checking algorithm to first get the list of potential CVEs by vendor:product, then iterate through every matching CPE for that CVE to determine if the bounds match or not. By doing this in two stages we can know if we've checked every CPE, instead of accidentally breaking out of the scan too early. (From OE-Core rev: d61aff9e22704ad69df1f7ab0f8784f4e7cc0c69) (From OE-Core rev: 541dc24d974d3e22c45a650c34298eebc45121e8) Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> Signed-off-by: Armin Kuster <akuster808@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/classes/cve-check.bbclass63
1 files changed, 34 insertions, 29 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index 3326944d79..c1cbdbde7b 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -165,7 +165,6 @@ def check_cves(d, patched_cves):
165 """ 165 """
166 Connect to the NVD database and find unpatched cves. 166 Connect to the NVD database and find unpatched cves.
167 """ 167 """
168 import ast, csv, tempfile, subprocess, io
169 from distutils.version import LooseVersion 168 from distutils.version import LooseVersion
170 169
171 cves_unpatched = [] 170 cves_unpatched = []
@@ -187,68 +186,74 @@ def check_cves(d, patched_cves):
187 cve_whitelist = d.getVar("CVE_CHECK_WHITELIST").split() 186 cve_whitelist = d.getVar("CVE_CHECK_WHITELIST").split()
188 187
189 import sqlite3 188 import sqlite3
190 db_file = d.getVar("CVE_CHECK_DB_FILE") 189 db_file = d.expand("file:${CVE_CHECK_DB_FILE}?mode=ro")
191 conn = sqlite3.connect(db_file) 190 conn = sqlite3.connect(db_file, uri=True)
192 191
192 # For each of the known product names (e.g. curl has CPEs using curl and libcurl)...
193 for product in products: 193 for product in products:
194 c = conn.cursor()
195 if ":" in product: 194 if ":" in product:
196 vendor, product = product.split(":", 1) 195 vendor, product = product.split(":", 1)
197 c.execute("SELECT * FROM PRODUCTS WHERE PRODUCT IS ? AND VENDOR IS ?", (product, vendor))
198 else: 196 else:
199 c.execute("SELECT * FROM PRODUCTS WHERE PRODUCT IS ?", (product,)) 197 vendor = "%"
200 198
201 for row in c: 199 # Find all relevant CVE IDs.
202 cve = row[0] 200 for cverow in conn.execute("SELECT DISTINCT ID FROM PRODUCTS WHERE PRODUCT IS ? AND VENDOR LIKE ?", (product, vendor)):
203 version_start = row[3] 201 cve = cverow[0]
204 operator_start = row[4]
205 version_end = row[5]
206 operator_end = row[6]
207 202
208 if cve in cve_whitelist: 203 if cve in cve_whitelist:
209 bb.note("%s-%s has been whitelisted for %s" % (product, pv, cve)) 204 bb.note("%s-%s has been whitelisted for %s" % (product, pv, cve))
210 # TODO: this should be in the report as 'whitelisted' 205 # TODO: this should be in the report as 'whitelisted'
211 patched_cves.add(cve) 206 patched_cves.add(cve)
207 continue
212 elif cve in patched_cves: 208 elif cve in patched_cves:
213 bb.note("%s has been patched" % (cve)) 209 bb.note("%s has been patched" % (cve))
214 else: 210 continue
215 to_append = False 211
212 vulnerable = False
213 for row in conn.execute("SELECT * FROM PRODUCTS WHERE ID IS ? AND PRODUCT IS ? AND VENDOR LIKE ?", (cve, product, vendor)):
214 (_, _, _, version_start, operator_start, version_end, operator_end) = row
215 #bb.debug(2, "Evaluating row " + str(row))
216
216 if (operator_start == '=' and pv == version_start): 217 if (operator_start == '=' and pv == version_start):
217 to_append = True 218 vulnerable = True
218 else: 219 else:
219 if operator_start: 220 if operator_start:
220 try: 221 try:
221 to_append_start = (operator_start == '>=' and LooseVersion(pv) >= LooseVersion(version_start)) 222 vulnerable_start = (operator_start == '>=' and LooseVersion(pv) >= LooseVersion(version_start))
222 to_append_start |= (operator_start == '>' and LooseVersion(pv) > LooseVersion(version_start)) 223 vulnerable_start |= (operator_start == '>' and LooseVersion(pv) > LooseVersion(version_start))
223 except: 224 except:
224 bb.warn("%s: Failed to compare %s %s %s for %s" % 225 bb.warn("%s: Failed to compare %s %s %s for %s" %
225 (product, pv, operator_start, version_start, cve)) 226 (product, pv, operator_start, version_start, cve))
226 to_append_start = False 227 vulnerable_start = False
227 else: 228 else:
228 to_append_start = False 229 vulnerable_start = False
229 230
230 if operator_end: 231 if operator_end:
231 try: 232 try:
232 to_append_end = (operator_end == '<=' and LooseVersion(pv) <= LooseVersion(version_end)) 233 vulnerable_end = (operator_end == '<=' and LooseVersion(pv) <= LooseVersion(version_end))
233 to_append_end |= (operator_end == '<' and LooseVersion(pv) < LooseVersion(version_end)) 234 vulnerable_end |= (operator_end == '<' and LooseVersion(pv) < LooseVersion(version_end))
234 except: 235 except:
235 bb.warn("%s: Failed to compare %s %s %s for %s" % 236 bb.warn("%s: Failed to compare %s %s %s for %s" %
236 (product, pv, operator_end, version_end, cve)) 237 (product, pv, operator_end, version_end, cve))
237 to_append_end = False 238 vulnerable_end = False
238 else: 239 else:
239 to_append_end = False 240 vulnerable_end = False
240 241
241 if operator_start and operator_end: 242 if operator_start and operator_end:
242 to_append = to_append_start and to_append_end 243 vulnerable = vulnerable_start and vulnerable_end
243 else: 244 else:
244 to_append = to_append_start or to_append_end 245 vulnerable = vulnerable_start or vulnerable_end
245 246
246 if to_append: 247 if vulnerable:
247 bb.note("%s-%s is vulnerable to %s" % (product, pv, cve)) 248 bb.note("%s-%s is vulnerable to %s" % (product, pv, cve))
248 cves_unpatched.append(cve) 249 cves_unpatched.append(cve)
249 else: 250 break
250 bb.note("%s-%s is not vulnerable to %s" % (product, pv, cve)) 251
251 patched_cves.add(cve) 252 if not vulnerable:
253 bb.note("%s-%s is not vulnerable to %s" % (product, pv, cve))
254 # TODO: not patched but not vulnerable
255 patched_cves.add(cve)
256
252 conn.close() 257 conn.close()
253 258
254 return (list(patched_cves), cves_unpatched) 259 return (list(patched_cves), cves_unpatched)