diff options
| -rw-r--r-- | meta/recipes-core/meta/cve-update-db-native.bb | 126 |
1 files changed, 113 insertions, 13 deletions
diff --git a/meta/recipes-core/meta/cve-update-db-native.bb b/meta/recipes-core/meta/cve-update-db-native.bb index 3a9d43943c..792252f510 100644 --- a/meta/recipes-core/meta/cve-update-db-native.bb +++ b/meta/recipes-core/meta/cve-update-db-native.bb | |||
| @@ -12,6 +12,8 @@ deltask do_install | |||
| 12 | deltask do_populate_sysroot | 12 | deltask do_populate_sysroot |
| 13 | 13 | ||
| 14 | NVDCVE_URL ?= "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-" | 14 | NVDCVE_URL ?= "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-" |
| 15 | FKIE_URL ?= "https://github.com/fkie-cad/nvd-json-data-feeds/releases/latest/download/CVE-" | ||
| 16 | |||
| 15 | # CVE database update interval, in seconds. By default: once a day (24*60*60). | 17 | # CVE database update interval, in seconds. By default: once a day (24*60*60). |
| 16 | # Use 0 to force the update | 18 | # Use 0 to force the update |
| 17 | # Use a negative value to skip the update | 19 | # Use a negative value to skip the update |
| @@ -109,6 +111,30 @@ def cleanup_db_download(db_file, db_tmp_file): | |||
| 109 | if os.path.exists(db_tmp_file): | 111 | if os.path.exists(db_tmp_file): |
| 110 | os.remove(db_tmp_file) | 112 | os.remove(db_tmp_file) |
| 111 | 113 | ||
| 114 | def db_file_names(d, year, is_nvd): | ||
| 115 | if is_nvd: | ||
| 116 | year_url = d.getVar('NVDCVE_URL') + str(year) | ||
| 117 | meta_url = year_url + ".meta" | ||
| 118 | json_url = year_url + ".json.gz" | ||
| 119 | return json_url, meta_url | ||
| 120 | year_url = d.getVar('FKIE_URL') + str(year) | ||
| 121 | meta_url = year_url + ".meta" | ||
| 122 | json_url = year_url + ".json.xz" | ||
| 123 | return json_url, meta_url | ||
| 124 | |||
| 125 | def host_db_name(d, is_nvd): | ||
| 126 | if is_nvd: | ||
| 127 | return "nvd.nist.gov" | ||
| 128 | return "github.com" | ||
| 129 | |||
| 130 | def db_decompress(d, data, is_nvd): | ||
| 131 | import gzip, lzma | ||
| 132 | |||
| 133 | if is_nvd: | ||
| 134 | return gzip.decompress(data).decode('utf-8') | ||
| 135 | # otherwise | ||
| 136 | return lzma.decompress(data) | ||
| 137 | |||
| 112 | def update_db_file(db_tmp_file, d): | 138 | def update_db_file(db_tmp_file, d): |
| 113 | """ | 139 | """ |
| 114 | Update the given database file | 140 | Update the given database file |
| @@ -119,6 +145,7 @@ def update_db_file(db_tmp_file, d): | |||
| 119 | 145 | ||
| 120 | YEAR_START = 2002 | 146 | YEAR_START = 2002 |
| 121 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) | 147 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) |
| 148 | is_nvd = d.getVar("NVD_DB_VERSION") == "NVD1" | ||
| 122 | 149 | ||
| 123 | # Connect to database | 150 | # Connect to database |
| 124 | conn = sqlite3.connect(db_tmp_file) | 151 | conn = sqlite3.connect(db_tmp_file) |
| @@ -129,9 +156,7 @@ def update_db_file(db_tmp_file, d): | |||
| 129 | for i, year in enumerate(range(YEAR_START, date.today().year + 1)): | 156 | for i, year in enumerate(range(YEAR_START, date.today().year + 1)): |
| 130 | bb.debug(2, "Updating %d" % year) | 157 | bb.debug(2, "Updating %d" % year) |
| 131 | ph.update((float(i + 1) / total_years) * 100) | 158 | ph.update((float(i + 1) / total_years) * 100) |
| 132 | year_url = (d.getVar('NVDCVE_URL')) + str(year) | 159 | json_url, meta_url = db_file_names(d, year, is_nvd) |
| 133 | meta_url = year_url + ".meta" | ||
| 134 | json_url = year_url + ".json.gz" | ||
| 135 | 160 | ||
| 136 | # Retrieve meta last modified date | 161 | # Retrieve meta last modified date |
| 137 | try: | 162 | try: |
| @@ -140,7 +165,7 @@ def update_db_file(db_tmp_file, d): | |||
| 140 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') | 165 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') |
| 141 | bb.warn("Failed to fetch CVE data (%s)" % e) | 166 | bb.warn("Failed to fetch CVE data (%s)" % e) |
| 142 | import socket | 167 | import socket |
| 143 | result = socket.getaddrinfo("nvd.nist.gov", 443, proto=socket.IPPROTO_TCP) | 168 | result = socket.getaddrinfo(host_db_name(d, is_nvd), 443, proto=socket.IPPROTO_TCP) |
| 144 | bb.warn("Host IPs are %s" % (", ".join(t[4][0] for t in result))) | 169 | bb.warn("Host IPs are %s" % (", ".join(t[4][0] for t in result))) |
| 145 | return False | 170 | return False |
| 146 | 171 | ||
| @@ -168,7 +193,7 @@ def update_db_file(db_tmp_file, d): | |||
| 168 | try: | 193 | try: |
| 169 | response = urllib.request.urlopen(json_url, timeout=cve_socket_timeout) | 194 | response = urllib.request.urlopen(json_url, timeout=cve_socket_timeout) |
| 170 | if response: | 195 | if response: |
| 171 | update_db(conn, gzip.decompress(response.read()).decode('utf-8')) | 196 | update_db(d, conn, db_decompress(d, response.read(), is_nvd)) |
| 172 | conn.execute("insert or replace into META values (?, ?)", [year, last_modified]).close() | 197 | conn.execute("insert or replace into META values (?, ?)", [year, last_modified]).close() |
| 173 | except urllib.error.URLError as e: | 198 | except urllib.error.URLError as e: |
| 174 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') | 199 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') |
| @@ -200,16 +225,22 @@ def initialize_db(conn): | |||
| 200 | 225 | ||
| 201 | c.close() | 226 | c.close() |
| 202 | 227 | ||
| 203 | def parse_node_and_insert(conn, node, cveId): | 228 | def parse_node_and_insert(conn, node, cveId, is_nvd): |
| 204 | # Parse children node if needed | 229 | # Parse children node if needed |
| 205 | for child in node.get('children', ()): | 230 | for child in node.get('children', ()): |
| 206 | parse_node_and_insert(conn, child, cveId) | 231 | parse_node_and_insert(conn, child, cveId, is_nvd) |
| 232 | |||
| 233 | def cpe_generator(is_nvd): | ||
| 234 | match_string = "cpeMatch" | ||
| 235 | cpe_string = 'criteria' | ||
| 236 | if is_nvd: | ||
| 237 | match_string = "cpe_match" | ||
| 238 | cpe_string = 'cpe23Uri' | ||
| 207 | 239 | ||
| 208 | def cpe_generator(): | 240 | for cpe in node.get(match_string, ()): |
| 209 | for cpe in node.get('cpe_match', ()): | ||
| 210 | if not cpe['vulnerable']: | 241 | if not cpe['vulnerable']: |
| 211 | return | 242 | return |
| 212 | cpe23 = cpe.get('cpe23Uri') | 243 | cpe23 = cpe.get(cpe_string) |
| 213 | if not cpe23: | 244 | if not cpe23: |
| 214 | return | 245 | return |
| 215 | cpe23 = cpe23.split(':') | 246 | cpe23 = cpe23.split(':') |
| @@ -260,9 +291,9 @@ def parse_node_and_insert(conn, node, cveId): | |||
| 260 | # Save processing by representing as -. | 291 | # Save processing by representing as -. |
| 261 | yield [cveId, vendor, product, '-', '', '', ''] | 292 | yield [cveId, vendor, product, '-', '', '', ''] |
| 262 | 293 | ||
| 263 | conn.executemany("insert into PRODUCTS values (?, ?, ?, ?, ?, ?, ?)", cpe_generator()).close() | 294 | conn.executemany("insert into PRODUCTS values (?, ?, ?, ?, ?, ?, ?)", cpe_generator(is_nvd)).close() |
| 264 | 295 | ||
| 265 | def update_db(conn, jsondata): | 296 | def update_db_nvdjson(conn, jsondata): |
| 266 | import json | 297 | import json |
| 267 | root = json.loads(jsondata) | 298 | root = json.loads(jsondata) |
| 268 | 299 | ||
| @@ -297,8 +328,77 @@ def update_db(conn, jsondata): | |||
| 297 | 328 | ||
| 298 | configurations = elt['configurations']['nodes'] | 329 | configurations = elt['configurations']['nodes'] |
| 299 | for config in configurations: | 330 | for config in configurations: |
| 300 | parse_node_and_insert(conn, config, cveId) | 331 | parse_node_and_insert(conn, config, cveId, True) |
| 332 | |||
| 333 | def update_db_fkie(conn, jsondata): | ||
| 334 | import json | ||
| 335 | root = json.loads(jsondata) | ||
| 336 | |||
| 337 | for elt in root['cve_items']: | ||
| 338 | if not 'vulnStatus' in elt or elt['vulnStatus'] == 'Rejected': | ||
| 339 | continue | ||
| 340 | |||
| 341 | if not 'configurations' in elt: | ||
| 342 | continue | ||
| 343 | |||
| 344 | accessVector = None | ||
| 345 | vectorString = None | ||
| 346 | cvssv2 = 0.0 | ||
| 347 | cvssv3 = 0.0 | ||
| 348 | cvssv4 = 0.0 | ||
| 349 | cveId = elt['id'] | ||
| 350 | cveDesc = elt['descriptions'][0]['value'] | ||
| 351 | date = elt['lastModified'] | ||
| 352 | try: | ||
| 353 | for m in elt['metrics']['cvssMetricV2']: | ||
| 354 | if m['type'] == 'Primary': | ||
| 355 | accessVector = m['cvssData']['accessVector'] | ||
| 356 | vectorString = m['cvssData']['vectorString'] | ||
| 357 | cvssv2 = m['cvssData']['baseScore'] | ||
| 358 | except KeyError: | ||
| 359 | cvssv2 = 0.0 | ||
| 360 | try: | ||
| 361 | for m in elt['metrics']['cvssMetricV30']: | ||
| 362 | if m['type'] == 'Primary': | ||
| 363 | accessVector = m['cvssData']['accessVector'] | ||
| 364 | vectorString = m['cvssData']['vectorString'] | ||
| 365 | cvssv3 = m['cvssData']['baseScore'] | ||
| 366 | except KeyError: | ||
| 367 | accessVector = accessVector or "UNKNOWN" | ||
| 368 | cvssv3 = 0.0 | ||
| 369 | try: | ||
| 370 | for m in elt['metrics']['cvssMetricV31']: | ||
| 371 | if m['type'] == 'Primary': | ||
| 372 | accessVector = m['cvssData']['accessVector'] | ||
| 373 | vectorString = m['cvssData']['vectorString'] | ||
| 374 | cvssv3 = m['cvssData']['baseScore'] | ||
| 375 | except KeyError: | ||
| 376 | accessVector = accessVector or "UNKNOWN" | ||
| 377 | cvssv3 = 0.0 | ||
| 378 | try: | ||
| 379 | for m in elt['metrics']['cvssMetricV40']: | ||
| 380 | if m['type'] == 'Primary': | ||
| 381 | accessVector = m['cvssData']['accessVector'] | ||
| 382 | vectorString = m['cvssData']['vectorString'] | ||
| 383 | cvssv4 = m['cvssData']['baseScore'] | ||
| 384 | except KeyError: | ||
| 385 | accessVector = accessVector or "UNKNOWN" | ||
| 386 | cvssv4 = 0.0 | ||
| 301 | 387 | ||
| 388 | conn.execute("insert or replace into NVD values (?, ?, ?, ?, ?, ?, ?, ?)", | ||
| 389 | [cveId, cveDesc, cvssv2, cvssv3, cvssv4, date, accessVector, vectorString]).close() | ||
| 390 | |||
| 391 | for config in elt['configurations']: | ||
| 392 | # This is suboptimal as it doesn't handle AND/OR and negate, but is better than nothing | ||
| 393 | for node in config["nodes"]: | ||
| 394 | parse_node_and_insert(conn, node, cveId, False) | ||
| 395 | |||
| 396 | |||
| 397 | def update_db(d, conn, jsondata): | ||
| 398 | if (d.getVar("NVD_DB_VERSION") == "FKIE"): | ||
| 399 | return update_db_fkie(conn, jsondata) | ||
| 400 | else: | ||
| 401 | return update_db_nvdjson(conn, jsondata) | ||
| 302 | 402 | ||
| 303 | do_fetch[nostamp] = "1" | 403 | do_fetch[nostamp] = "1" |
| 304 | 404 | ||
