[gnome-build-meta/jjardon/cve_report_3_32] Add CVE check job
- From: Javier Jardón Cabezas <jjardon src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-build-meta/jjardon/cve_report_3_32] Add CVE check job
- Date: Wed, 27 Mar 2019 17:43:44 +0000 (UTC)
commit c7300da81043794ac6cbad03808954392a400710
Author: Javier Jardón <jjardon gnome org>
Date: Wed Mar 27 16:14:15 2019 +0000
Add CVE check job
This is all taken from freedesktop-sdk
Eventually most of the scripts will be converted to buildstream plugin
so It's easier to reuse
.gitlab-ci.yml | 27 +++++++++
elements/platform-manifest.bst | 8 +++
elements/sdk-manifest.bst | 8 +++
project.conf | 1 +
utils/generate-cve-report.py | 73 +++++++++++++++++++++++++
utils/update-local-cve-database.py | 109 +++++++++++++++++++++++++++++++++++++
6 files changed, 226 insertions(+)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index eaa42121..0f3fbec6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,6 +14,7 @@ variables:
DOCKER_AARCH64: "${DOCKER_REGISTRY}/aarch64:e132e7bf9180b30c6ad0f4b057442cc2f2a0aa86"
stages:
+ - reports
- track
- build
- prepare_flatpak
@@ -200,6 +201,32 @@ before_script:
# "Real" CI jobs
#
+cve_report:
+ <<: *x86_64
+ stage: reports
+ script:
+ - pip3 install --user lxml
+
+ - ${BST} track --deps all platform-manifest.bst sdk-manifest.bst
+ - ${BST} build platform-manifest.bst
+ - ${BST} build sdk-manifest.bst
+
+ - ${BST} checkout platform-manifest.bst platform-manifest/
+ - ${BST} checkout sdk-manifest.bst sdk-manifest/
+
+ - mkdir -p "${XDG_CACHE_HOME}/cve"
+ - cd "${XDG_CACHE_HOME}/cve"
+ - python3 "${CI_PROJECT_DIR}/utils/update-local-cve-database.py"
+
+ - mkdir -p "${CI_PROJECT_DIR}/cve-reports"
+ - python3 "${CI_PROJECT_DIR}/utils/generate-cve-report.py"
"${CI_PROJECT_DIR}/sdk-manifest/usr/manifest.json" "${CI_PROJECT_DIR}/cve-reports/sdk.html"
+ - python3 "${CI_PROJECT_DIR}/utils/generate-cve-report.py"
"${CI_PROJECT_DIR}/platform-manifest/usr/manifest.json" "${CI_PROJECT_DIR}/cve-reports/platform.html"
+ artifacts:
+ paths:
+ - "${CI_PROJECT_DIR}/cve-reports"
+ only:
+ - gnome-3-32
+
track:
<<: *x86_64
stage: track
diff --git a/elements/platform-manifest.bst b/elements/platform-manifest.bst
new file mode 100644
index 00000000..4afeac16
--- /dev/null
+++ b/elements/platform-manifest.bst
@@ -0,0 +1,8 @@
+kind: collect_manifest
+
+depends:
+ - filename: sdk-platform.bst
+ type: build
+
+config:
+ path: "/usr/manifest.json"
diff --git a/elements/sdk-manifest.bst b/elements/sdk-manifest.bst
new file mode 100644
index 00000000..436ba15f
--- /dev/null
+++ b/elements/sdk-manifest.bst
@@ -0,0 +1,8 @@
+kind: collect_manifest
+
+depends:
+ - filename: sdk.bst
+ type: build
+
+config:
+ path: "/usr/manifest.json"
diff --git a/project.conf b/project.conf
index 71fffeb6..e526855c 100644
--- a/project.conf
+++ b/project.conf
@@ -295,3 +295,4 @@ plugins:
elements:
flatpak_image: 0
flatpak_repo: 0
+ collect_manifest: 0
diff --git a/utils/generate-cve-report.py b/utils/generate-cve-report.py
new file mode 100644
index 00000000..70c1b21e
--- /dev/null
+++ b/utils/generate-cve-report.py
@@ -0,0 +1,73 @@
+"""Generate and HTML output with all current CVEs for a given manifest.
+
+Usage:
+ python3 generate-cve-report.py path/to/manifest.json output.html
+
+This requires you to run update-local-cve-database.py first in the
+same current directory.
+"""
+
+import json
+import sys
+import sqlite3
+
+conn = sqlite3.connect('data-2.db')
+c = conn.cursor()
+
+with open(sys.argv[1], 'rb') as f:
+ manifest = json.load(f)
+
+with open(sys.argv[2], 'w') as out:
+ out.write('<!DOCTYPE html>\n')
+ out.write('<html><head><title>Report</title></head><body><table>\n')
+
+ entries = []
+ for module in manifest["modules"]:
+ name = module["name"]
+ sources = module["sources"]
+ cpe = module["x-cpe"]
+ product = cpe["product"]
+ version = cpe.get("version")
+ vendor = cpe.get("vendor")
+ patches = cpe.get("patches", [])
+ ignored = cpe.get("ignored", [])
+ if not version:
+ print("{} is missing a version".format(name))
+ continue
+
+ if vendor:
+ c.execute("""SELECT cve.id, cve.summary, cve.score FROM cve, product_vuln
+ WHERE cve.id=product_vuln.cve_id
+ AND product_vuln.name=?
+ AND product_vuln.version=?
+ AND product_vuln.vendor=?""",
+ (product, version, vendor))
+ else:
+ c.execute("""SELECT cve.id, cve.summary, cve.score FROM cve, product_vuln
+ WHERE cve.id=product_vuln.cve_id
+ AND product_vuln.name=?
+ AND product_vuln.version=?""",
+ (product, version))
+ while True:
+ row = c.fetchone()
+ if row is None:
+ break
+ if row[0] in patches or row[0] in ignored:
+ continue
+ entries.append((row[0], name, version, row[1], row[2]))
+
+ def by_score(entry, ):
+ ID, name, version, summary, score = entry
+ try:
+ return float(score)
+ except ValueError:
+ return float("inf")
+
+ for ID, name, version, summary, score in sorted(entries, key=by_score, reverse=True):
+ out.write('<tr>')
+ out.write('<td><a href="https://nvd.nist.gov/vuln/detail/{ID}">{ID}</a></td>'.format(ID=ID))
+ for d in [name, version, summary, score]:
+ out.write('<td>{}</td>'.format(d))
+ out.write('</tr>\n')
+
+ out.write('</table></html>\n')
diff --git a/utils/update-local-cve-database.py b/utils/update-local-cve-database.py
new file mode 100644
index 00000000..5af766a8
--- /dev/null
+++ b/utils/update-local-cve-database.py
@@ -0,0 +1,109 @@
+"""Downloads CVE database and store it as sqlite3 database.
+
+This tool does not take parameter. It will create files in the current
+directory:
+ - data.db: The sqlite3 database itself.
+ - nvdcve-2.0-*.xml.gz: The cached raw XML databases from the CVE database.
+
+Do not remove nvdcve-2.0-*.xml.gz files unless you remove
+data.db. data.db contains etags, and files would not be downloaded
+again if files are just removed.
+
+Files are not downloaded if not modified. But we still verify with the
+remote database we have the latest version of the files.
+"""
+
+import gzip
+import sys
+import sqlite3
+import datetime
+import itertools
+import urllib.request
+import urllib.parse
+from contextlib import contextmanager
+
+from lxml import etree as ET
+
+namespaces = {
+ "feed": "http://scap.nist.gov/schema/feed/vulnerability/2.0",
+ "vuln": "http://scap.nist.gov/schema/vulnerability/0.4",
+ "cvss": "http://scap.nist.gov/schema/cvss-v2/0.2",
+}
+
+def extract_vulns(tree):
+ for entry in tree.iterfind("feed:entry", namespaces=namespaces):
+ cve_id = entry.find("vuln:cve-id", namespaces=namespaces).text
+ summary = entry.find("vuln:summary", namespaces=namespaces).text
+ score = entry.find("vuln:cvss/cvss:base_metrics/cvss:score", namespaces=namespaces)
+ yield cve_id, summary, score.text if score is not None else None
+
+def extract_product_vulns(tree):
+ for entry in tree.iterfind("feed:entry", namespaces=namespaces):
+ cve_id = entry.find("vuln:cve-id", namespaces=namespaces).text
+ for vuln_software in entry.iterfind("vuln:vulnerable-software-list", namespaces=namespaces):
+ for product in vuln_software.iterfind("vuln:product", namespaces=namespaces):
+ product_name = product.text
+ try:
+ vendor, name, version = product_name.split(':')[2:5]
+ except ValueError:
+ continue
+ yield cve_id, vendor, name, version
+
+def ensure_tables(c):
+ c.execute("""CREATE TABLE IF NOT EXISTS etags
+ (year TEXT UNIQUE, etag TEXT)""")
+ c.execute("""CREATE TABLE IF NOT EXISTS cve
+ (id TEXT UNIQUE, summary TEXT, score TEXT)""")
+ c.execute("""CREATE TABLE IF NOT EXISTS product_vuln
+ (cve_id TEXT, name TEXT, vendor TEXT, version TEXT,
+ UNIQUE(cve_id, name, vendor, version))""")
+
+def update_year(c, year):
+ url = 'https://nvd.nist.gov/feeds/xml/cve/2.0/nvdcve-2.0-{}.xml.gz'.format(year)
+ c.execute("SELECT etag FROM etags WHERE year=?", (year,))
+ row = c.fetchone()
+ if row is not None:
+ etag = row[0]
+ else:
+ etag = None
+
+ request = urllib.request.Request(url)
+ if etag is not None:
+ request.add_header('If-None-Match', etag)
+ try:
+ with urllib.request.urlopen(request) as resp:
+ new_etag = resp.getheader('ETag')
+ assert new_etag is not None
+ if new_etag is not None:
+ c.execute("INSERT OR REPLACE INTO etags (year, etag) VALUES (?, ?)", (year, new_etag))
+ with open('nvdcve-2.0-{}.xml.gz'.format(year), 'wb') as f:
+ while True:
+ buf = resp.read(4096)
+ if not buf:
+ print("Downloaded {}".format(f.name))
+ break
+ f.write(buf)
+ except urllib.error.HTTPError as error:
+ if error.code != 304:
+ raise
+ print("Cached {}".format('nvdcve-2.0-{}.xml.gz'.format(year)))
+
+ with gzip.open('nvdcve-2.0-{}.xml.gz'.format(year)) as f:
+ tree = ET.parse(f)
+ for cve_id, summary, score in extract_vulns(tree):
+ c.execute("INSERT OR REPLACE INTO cve (id, summary, score) VALUES (?, ?, ?)", (cve_id, summary,
score))
+
+ for cve_id, vendor, name, version in extract_product_vulns(tree):
+ c.execute("INSERT OR REPLACE INTO product_vuln (cve_id, name, vendor, version) VALUES (?, ?, ?,
?)", (cve_id, name, vendor, version))
+
+if __name__ == '__main__':
+ conn = sqlite3.connect('data-2.db')
+ c = conn.cursor()
+ try:
+ ensure_tables(c)
+ for year in range(2002, datetime.datetime.now().year + 1):
+ update_year(c, str(year))
+ update_year(c, 'Modified')
+ conn.commit()
+ finally:
+ conn.close()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]