All of lore.kernel.org
 help / color / mirror / Atom feed
* [Buildroot] [PATCH 00/10] CPE validation
@ 2021-01-07 13:39 Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 01/10] support/scripts/pkg-stats: improvements in is_status_*() methods Thomas Petazzoni
                   ` (10 more replies)
  0 siblings, 11 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

Hello,

We have recently merged support to allow packages to express their CPE
identifier, which is a unique identifier used by the NIST database to
identify software products, and associate CVEs to them.

This patch series extends that by ensuring that CPEs affected to
packages in Buildroot do exist in the official CPE dictionary from
NIST, and if they do not exist, assist in generating the XML snippet
that the CPE dictionary maintainers expect.

In more details:

 - PATCHes 1 to 5 are just fixes/improvements to pkg-stats.

 - PATCH 6 introduces support/scripts/cpedb.py, which is a new set of
   helper that is capable of downloading the CPE dictionary, parsing
   it, and generating XML snippets to submit new CPE entries.

 - PATCH 7 modifies support/scripts/pkg-stats to leverage the features
   of cpedb.py to include validation of CPE identifiers of Buildroot
   patches.

 - PATCH 8 adds support/scripts/gen-missing-cpe, which allows to
   generate the XML snippets expected by the CPE dictionary mainainers

 - PATCH 9 hooks up support/scripts/gen-missing-cpe as the "make
   missing-cpe" top-level make target

 - PATCH 10 extends the documentation with more details about
   Buildroot features related to vulnerability management.

This is based on work from Matt Weber, which has been further
refactored/rewritten by Gr?gory Clement and myself.

To test this:

 $ make pkg-stats
 $ firefox output/pkg-stats.html
 $ make missing-cpe
 $ ls output/cpe-updates/

This work is also available at:

  https://github.com/tpetazzoni/buildroot/commits/cpe-validation

Thomas

Matt Weber (2):
  support/scripts/cpedb.py: new CPE XML helper
  support/scripts/gen-missing-cpe: add new script

Thomas Petazzoni (8):
  support/scripts/pkg-stats: improvements in is_status_*() methods
  support/scripts/pkg-stats: fix the status reporting of CVEs
  support/scripts/pkg-stats: drop unused cpeid_name() function
  support/scripts/pkg-stats: drop unused --cpeid option
  support/scripts/pkg-stats: fix flake8 warnings
  support/scripts/pkg-stats: check CPE existence in CPE dictionnary
  Makefile: add new missing-cpe target
  docs/manual: add details about vulnerability management

 Makefile                        |   8 ++
 docs/manual/common-usage.txt    |  42 +++++++
 support/scripts/cpedb.py        | 193 ++++++++++++++++++++++++++++++++
 support/scripts/gen-missing-cpe |  65 +++++++++++
 support/scripts/pkg-stats       |  60 ++++++----
 5 files changed, 344 insertions(+), 24 deletions(-)
 create mode 100644 support/scripts/cpedb.py
 create mode 100755 support/scripts/gen-missing-cpe

-- 
2.29.2

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 01/10] support/scripts/pkg-stats: improvements in is_status_*() methods
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 02/10] support/scripts/pkg-stats: fix the status reporting of CVEs Thomas Petazzoni
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

Make is_status_ok() work when the given status name is not even listed
in the status dict. This will be necessary for following commits.

Introduced similar methods for the error and na status, which will be
used in following commits.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index 4a9ff1ffa0..900f290e11 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -276,7 +276,13 @@ class Package:
             self.status['developers'] = ("warning", "no developers")
 
     def is_status_ok(self, name):
-        return self.status[name][0] == 'ok'
+        return name in self.status and self.status[name][0] == 'ok'
+
+    def is_status_error(self, name):
+        return name in self.status and self.status[name][0] == 'error'
+
+    def is_status_na(self, name):
+        return name in self.status and self.status[name][0] == 'na'
 
     def __eq__(self, other):
         return self.path == other.path
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 02/10] support/scripts/pkg-stats: fix the status reporting of CVEs
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 01/10] support/scripts/pkg-stats: improvements in is_status_*() methods Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 03/10] support/scripts/pkg-stats: drop unused cpeid_name() function Thomas Petazzoni
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

Since commit bd665d182c8131d2deafa39be0f3d89adb43643f
("support/scripts/pkg-stats: improve rendering of CVE information"),
we have better reporting of CVE related information, based on
pkg.status['cve']. However, this commit broke pkg-stats when the
--nvd-path option is not passed, and therefore no CVE information is
available.

This commit fixes that, by making use of the is_status_ok(),
is_status_error() and is_status_na() methods recently introduced.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index 900f290e11..c084c54b60 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -881,18 +881,20 @@ def dump_html_pkg(f, pkg):
 
     # CVEs
     td_class = ["centered"]
-    if pkg.status['cve'][0] == "ok":
+    if pkg.is_status_ok("cve"):
         td_class.append("cve-ok")
-    elif pkg.status['cve'][0] == "error":
+    elif pkg.is_status_error("cve"):
         td_class.append("cve-nok")
     else:
         td_class.append("cve-unknown")
     f.write("  <td class=\"%s\">\n" % " ".join(td_class))
-    if pkg.status['cve'][0] == "error":
+    if pkg.is_status_error("cve"):
         for cve in pkg.cves:
             f.write("   <a href=\"https://security-tracker.debian.org/tracker/%s\">%s<br/>\n" % (cve, cve))
-    elif pkg.status['cve'][0] == "na":
+    elif pkg.is_status_na("cve"):
         f.write("    %s" % pkg.status['cve'][1])
+    else:
+        f.write("    N/A\n")
     f.write("  </td>\n")
 
     # CPE ID
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 03/10] support/scripts/pkg-stats: drop unused cpeid_name() function
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 01/10] support/scripts/pkg-stats: improvements in is_status_*() methods Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 02/10] support/scripts/pkg-stats: fix the status reporting of CVEs Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 04/10] support/scripts/pkg-stats: drop unused --cpeid option Thomas Petazzoni
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

The cpeid_name() function is not used anywhere, drop it.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index c084c54b60..9d28087d96 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -1060,13 +1060,6 @@ def parse_args():
     return args
 
 
-def cpeid_name(pkg):
-    try:
-        return pkg.cpeid.split(':')[1]
-    except Exception:  # cpeid may be None, or improperly formatted
-        return ''
-
-
 def __main__():
     args = parse_args()
     if args.packages:
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 04/10] support/scripts/pkg-stats: drop unused --cpeid option
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (2 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 03/10] support/scripts/pkg-stats: drop unused cpeid_name() function Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 05/10] support/scripts/pkg-stats: fix flake8 warnings Thomas Petazzoni
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

The --cpeid option was mistakenly introduced by commit
92e7089a8ca9f7dba5a5d690b7f768352cd6b983 ("support/script/pkg-stats:
show CPE ID in results") but is in fact not necessary.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 1 -
 1 file changed, 1 deletion(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index 9d28087d96..cf206e6533 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -1053,7 +1053,6 @@ def parse_args():
                           help='List of packages (comma separated)')
     parser.add_argument('--nvd-path', dest='nvd_path',
                         help='Path to the local NVD database', type=resolvepath)
-    parser.add_argument("--cpeid", action='store_true')
     args = parser.parse_args()
     if not args.html and not args.json:
         parser.error('at least one of --html or --json (or both) is required')
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 05/10] support/scripts/pkg-stats: fix flake8 warnings
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (3 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 04/10] support/scripts/pkg-stats: drop unused --cpeid option Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 06/10] support/scripts/cpedb.py: new CPE XML helper Thomas Petazzoni
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

Fixes:

support/scripts/pkg-stats:148:17: E741 ambiguous variable name 'l'
support/scripts/pkg-stats:379:9: E741 ambiguous variable name 'l'

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index cf206e6533..854ece389d 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -145,8 +145,8 @@ class Package:
         self.infras = list()
         with open(os.path.join(brpath, self.path), 'r') as f:
             lines = f.readlines()
-            for l in lines:
-                match = INFRA_RE.match(l)
+            for line in lines:
+                match = INFRA_RE.match(line)
                 if not match:
                     continue
                 infra = match.group(1)
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 06/10] support/scripts/cpedb.py: new CPE XML helper
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (4 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 05/10] support/scripts/pkg-stats: fix flake8 warnings Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary Thomas Petazzoni
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

From: Matt Weber <matthew.weber@rockwellcollins.com>

Python class which consumes a NIST CPE XML and provides helper
functions to access and search the db's data.

 - Defines the CPE as a object with operations / formats
 - Processing of CPE dictionary

Signed-off-by: Matthew Weber <matthew.weber@rockwellcollins.com>
Co-Developed-by: Gr?gory Clement <gregory.clement@bootlin.com>
Co-Developed-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/cpedb.py | 193 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 support/scripts/cpedb.py

diff --git a/support/scripts/cpedb.py b/support/scripts/cpedb.py
new file mode 100644
index 0000000000..87b171e5e3
--- /dev/null
+++ b/support/scripts/cpedb.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+
+import xml.etree.ElementTree as ET
+from xml.etree.ElementTree import Element, SubElement
+import gzip
+import os
+import pickle
+import requests
+import time
+import re
+from xml.dom import minidom
+
+VALID_REFS = ['VENDOR', 'VERSION', 'CHANGE_LOG', 'PRODUCT', 'PROJECT', 'ADVISORY']
+
+CPEDB_URL = "https://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.3.xml.gz"
+
+CPE_XMLNS = \
+    'xmlns:cpe-23="http://scap.nist.gov/schema/cpe-extension/2.3" ' \
+    'xmlns:ns6="http://scap.nist.gov/schema/scap-core/0.1" ' \
+    'xmlns="http://cpe.mitre.org/dictionary/2.0" ' \
+    'xmlns:scap-core="http://scap.nist.gov/schema/scap-core/0.3" ' \
+    'xmlns:config="http://scap.nist.gov/schema/configuration/0.1" ' \
+    'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' \
+    'xmlns:meta="http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2" ' \
+    'xsi:schemaLocation="http://scap.nist.gov/schema/cpe-extension/2.3 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary-extension_2.3.xsd http://cpe.mitre.org/dictionary/2.0 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary_2.3.xsd http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2 https://scap.nist.gov/schema/cpe/2.1/cpe-dictionary-metadata_0.2.xsd http://scap.nist.gov/schema/scap-core/0.3 https://scap.nist.gov/schema/nvd/scap-core_0.3.xsd http://scap.nist.gov/schema/configuration/0.1 https://scap.nist.gov/schema/nvd/configuration_0.1.xsd http://scap.nist.gov/schema/scap-core/0.1 https://scap.nist.gov/schema/nvd/scap-core_0.1.xsd"' # noqa E501
+
+
+ns = {
+    '': 'http://cpe.mitre.org/dictionary/2.0',
+    'cpe-23': 'http://scap.nist.gov/schema/cpe-extension/2.3',
+    'xml': 'http://www.w3.org/XML/1998/namespace'
+}
+
+
+class CPE:
+    def __init__(self, cpe_str, titles, refs):
+        self.cpe_str = cpe_str
+        self.titles = titles
+        self.references = refs
+        self.cpe_cur_ver = "".join(self.cpe_str.split(":")[5:6])
+
+    def update_xml_dict(self):
+        ET.register_namespace('', 'http://cpe.mitre.org/dictionary/2.0')
+        cpes = Element('cpe-list')
+        item = SubElement(cpes, 'cpe-item')
+        cpe_short_name = CPE.short_name(self.cpe_str)
+        cpe_new_ver = CPE.version_update(self.cpe_str)
+
+        item.set('name', 'cpe:/' + cpe_short_name)
+        self.titles[0].text.replace(self.cpe_cur_ver, cpe_new_ver)
+        for title in self.titles:
+            item.append(title)
+        if self.references:
+            item.append(self.references)
+        cpe23item = SubElement(item, 'cpe-23:cpe23-item')
+        cpe23item.set('name', self.cpe_str)
+
+        # Generate the XML as a string
+        xmlstr = ET.tostring(cpes, encoding="utf-8").decode("utf-8")
+
+        # Inject the correct namespace value
+        xmlstr = re.sub('xmlns=\"http://cpe.mitre.org/dictionary/2.0\"', CPE_XMLNS, xmlstr)
+
+        # And use minidom to pretty print the XML
+        return minidom.parseString(xmlstr).toprettyxml()
+
+    @staticmethod
+    def version(cpe):
+        return cpe.split(":")[5]
+
+    @staticmethod
+    def product(cpe):
+        return cpe.split(":")[4]
+
+    @staticmethod
+    def short_name(cpe):
+        return ":".join(cpe.split(":")[2:6])
+
+    @staticmethod
+    def version_update(cpe):
+        return ":".join(cpe.split(":")[5:6])
+
+    @staticmethod
+    def no_version(cpe):
+        return ":".join(cpe.split(":")[:5])
+
+
+class CPEDB:
+    def __init__(self, nvd_path):
+        self.all_cpes = dict()
+        self.all_cpes_no_version = dict()
+        self.nvd_path = nvd_path
+
+    def gen_cached_cpedb(self, cpedb, cache_all_cpes, cache_all_cpes_no_version):
+        print("CPE: Unzipping xml manifest...")
+        nist_cpe_file = gzip.GzipFile(fileobj=open(cpedb, 'rb'))
+        print("CPE: Converting xml manifest to dict...")
+        tree = ET.parse(nist_cpe_file)
+        all_cpedb = tree.getroot()
+        self.parse_dict(all_cpedb)
+
+        print("CPE: Caching dictionary")
+        cpes_file = open(cache_all_cpes, 'wb')
+        pickle.dump(self.all_cpes, cpes_file)
+        cpes_file.close()
+        cpes_file = open(cache_all_cpes_no_version, 'wb')
+        pickle.dump(self.all_cpes_no_version, cpes_file)
+        cpes_file.close()
+
+    def get_xml_dict(self):
+        print("CPE: Setting up NIST dictionary")
+        if not os.path.exists(os.path.join(self.nvd_path, "cpe")):
+            os.makedirs(os.path.join(self.nvd_path, "cpe"))
+
+        cpe_dict_local = os.path.join(self.nvd_path, "cpe", os.path.basename(CPEDB_URL))
+        if not os.path.exists(cpe_dict_local) or os.stat(cpe_dict_local).st_mtime < time.time() - 86400:
+            print("CPE: Fetching xml manifest from [" + CPEDB_URL + "]")
+            cpe_dict = requests.get(CPEDB_URL)
+            open(cpe_dict_local, "wb").write(cpe_dict.content)
+
+        cache_all_cpes = os.path.join(self.nvd_path, "cpe", "all_cpes.pkl")
+        cache_all_cpes_no_version = os.path.join(self.nvd_path, "cpe", "all_cpes_no_version.pkl")
+
+        if not os.path.exists(cache_all_cpes) or \
+           not os.path.exists(cache_all_cpes_no_version) or \
+           os.stat(cache_all_cpes).st_mtime < os.stat(cpe_dict_local).st_mtime or \
+           os.stat(cache_all_cpes_no_version).st_mtime < os.stat(cpe_dict_local).st_mtime:
+            self.gen_cached_cpedb(cpe_dict_local,
+                                  cache_all_cpes,
+                                  cache_all_cpes_no_version)
+
+        print("CPE: Loading CACHED dictionary")
+        cpe_file = open(cache_all_cpes, 'rb')
+        self.all_cpes = pickle.load(cpe_file)
+        cpe_file.close()
+        cpe_file = open(cache_all_cpes_no_version, 'rb')
+        self.all_cpes_no_version = pickle.load(cpe_file)
+        cpe_file.close()
+
+    def parse_dict(self, all_cpedb):
+        # Cycle through the dict and build two dict to be used for custom
+        # lookups of partial and complete CPE objects
+        # The objects are then used to create new proposed XML updates if
+        # if is determined one is required
+        # Out of the different language titles, select English
+        for cpe in all_cpedb.findall(".//cpe-item", ns):
+            cpe_titles = [title for title in cpe.findall('.//title[@xml:lang="en-US"]', ns)]
+            # Some older CPE don't include references, if they do, make
+            # sure we handle the case of one ref needing to be packed
+            # in a list
+            cpe_ref = cpe.find(".//{*}references")
+            if cpe_ref:
+                for ref in cpe_ref.findall(".//{*}reference"):
+                    ref.text = ref.text.upper()
+                    if ref.text not in VALID_REFS:
+                        ref.text = ref.text + "-- UPDATE this entry, here are some examples and just one word should be used -- " + ' '.join(VALID_REFS) # noqa E501
+
+            cpe_str = cpe.find(".//{*}cpe23-item").get('name')
+            item = CPE(cpe_str, cpe_titles, cpe_ref)
+            cpe_str_no_version = CPE.no_version(cpe_str)
+            # This dict must have a unique key for every CPE version
+            # which allows matching to the specific obj data of that
+            # NIST dict entry
+            self.all_cpes.update({cpe_str: item})
+            # This dict has one entry for every CPE (w/o version) to allow
+            # partial match (no valid version) check (the obj is saved and
+            # used as seed for suggested xml updates. By updating the same
+            # non-version'd entry, it assumes the last update here is the
+            # latest version in the NIST dict)
+            self.all_cpes_no_version.update({cpe_str_no_version: item})
+
+    def find_partial(self, cpe_str):
+        cpe_str_no_version = CPE.no_version(cpe_str)
+        if cpe_str_no_version in self.all_cpes_no_version:
+            return cpe_str_no_version
+
+    def find_partial_obj(self, cpe_str):
+        cpe_str_no_version = CPE.no_version(cpe_str)
+        if cpe_str_no_version in self.all_cpes_no_version:
+            return self.all_cpes_no_version[cpe_str_no_version]
+
+    def find_partial_latest_version(self, cpe_str_partial):
+        cpe_obj = self.find_partial_obj(cpe_str_partial)
+        return cpe_obj.cpe_cur_ver
+
+    def find(self, cpe_str):
+        if self.find_partial(cpe_str):
+            if cpe_str in self.all_cpes:
+                return cpe_str
+
+    def gen_update_xml(self, cpe_str):
+        cpe = self.find_partial_obj(cpe_str)
+        return cpe.update_xml_dict()
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (5 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 06/10] support/scripts/cpedb.py: new CPE XML helper Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 19:37   ` Matthew Weber
  2021-01-07 13:39 ` [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script Thomas Petazzoni
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

This commit extends pkg-stats to leverage the recently introduced
CPEDB class to verify that the CPEs provided by Buildroot packages are
indeed known in the official CPE dictionnary provided by NVD.

Co-Developed-by: Gr?gory Clement <gregory.clement@bootlin.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/pkg-stats | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
index 854ece389d..2c82dc96ad 100755
--- a/support/scripts/pkg-stats
+++ b/support/scripts/pkg-stats
@@ -33,7 +33,7 @@ brpath = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
 sys.path.append(os.path.join(brpath, "utils"))
 from getdeveloperlib import parse_developers  # noqa: E402
 import cve as cvecheck  # noqa: E402
-
+from cpedb import CPEDB  # noqa: E402
 
 INFRA_RE = re.compile(r"\$\(eval \$\(([a-z-]*)-package\)\)")
 URL_RE = re.compile(r"\s*https?://\S*\s*$")
@@ -226,7 +226,8 @@ class Package:
 
         if var in self.all_cpeids:
             self.cpeid = self.all_cpeids[var]
-            self.status['cpe'] = ("ok", "verified CPE identifier")
+            # Set a preliminary status, it might be overridden by check_package_cpes()
+            self.status['cpe'] = ("warning", "not checked against CPE dictionnary")
         else:
             self.status['cpe'] = ("error", "no verified CPE identifier")
 
@@ -601,6 +602,18 @@ def check_package_cves(nvd_path, packages):
                 pkg.status['cve'] = ("ok", "not affected by CVEs")
 
 
+def check_package_cpes(nvd_path, packages):
+    cpedb = CPEDB(nvd_path)
+    cpedb.get_xml_dict()
+    for p in packages:
+        if not p.cpeid:
+            continue
+        if cpedb.find(p.cpeid):
+            p.status['cpe'] = ("ok", "verified CPE identifier")
+        else:
+            p.status['cpe'] = ("error", "CPE identifier unknown in CPE database")
+
+
 def calculate_stats(packages):
     stats = defaultdict(int)
     stats['packages'] = len(packages)
@@ -899,19 +912,17 @@ def dump_html_pkg(f, pkg):
 
     # CPE ID
     td_class = ["left"]
-    if pkg.status['cpe'][0] == "ok":
+    if pkg.is_status_ok("cpe"):
         td_class.append("cpe-ok")
-    elif pkg.status['cpe'][0] == "error":
+    elif pkg.is_status_error("cpe"):
         td_class.append("cpe-nok")
     else:
         td_class.append("cpe-unknown")
     f.write("  <td class=\"%s\">\n" % " ".join(td_class))
-    if pkg.status['cpe'][0] == "ok":
+    if pkg.cpeid:
         f.write("  <code>%s</code>\n" % pkg.cpeid)
-    elif pkg.status['cpe'][0] == "error":
-        f.write("  N/A\n")
-    else:
-        f.write("  %s\n" % pkg.status['cpe'][1])
+    if not pkg.is_status_ok("cpe"):
+        f.write("  %s%s\n" % ("<br/>" if pkg.cpeid else "", pkg.status['cpe'][1]))
     f.write("  </td>\n")
 
     f.write(" </tr>\n")
@@ -1101,6 +1112,7 @@ def __main__():
     if args.nvd_path:
         print("Checking packages CVEs")
         check_package_cves(args.nvd_path, packages)
+        check_package_cpes(args.nvd_path, packages)
     print("Calculate stats")
     stats = calculate_stats(packages)
     if args.html:
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (6 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 19:33   ` Matthew Weber
  2021-01-07 13:39 ` [Buildroot] [PATCH 09/10] Makefile: add new missing-cpe target Thomas Petazzoni
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

From: Matt Weber <matthew.weber@rockwellcollins.com>

This script queries the list of CPE IDs for the packages of the
current configuration (based on the "make show-info" output), and:

 - for CPE IDs that do not have any matching entry in the CPE
   database, it emits a warning

 - for CPE IDs that do have a matching entry, but not with the same
   version, it generates a snippet of XML that can be used to propose
   an updated version to NIST.

Ref: NIST has a group email (cpe_dictionary at nist.gov) used to
recieve these version update and new entry xml files.  They do
process the XML and provide feedback. In some cases they will
propose back something different where the vendor or version is
slightly different.

Limitations
 - Currently any use of non-number version identifiers isn't
   supported by NIST as they use ranges to determine impact
   of a CVE
 - Any Linux version from a non-upstream is also not supported
   without manually adjusting the information as the custom
   kernel will more then likely not match the upstream version
   used in the dictionary

Signed-off-by: Matt Weber <matthew.weber@rockwellcollins.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 support/scripts/gen-missing-cpe | 65 +++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100755 support/scripts/gen-missing-cpe

diff --git a/support/scripts/gen-missing-cpe b/support/scripts/gen-missing-cpe
new file mode 100755
index 0000000000..22801ca488
--- /dev/null
+++ b/support/scripts/gen-missing-cpe
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+
+import argparse
+import sys
+import json
+import subprocess
+import os
+from cpedb import CPEDB, CPE
+
+
+def gen_update_xml_reports(cpes, cpedb, output):
+    cpe_need_update = []
+
+    for cpe in cpes:
+        result = cpedb.find(cpe)
+        if not result:
+            result = cpedb.find_partial(CPE.no_version(cpe))
+            if result:
+                cpe_need_update.append(cpe)
+            else:
+                print("WARNING: no match found for '%s'" % cpe)
+
+    for cpe in cpe_need_update:
+        xml = cpedb.gen_update_xml(cpe)
+        fname = CPE.product(cpe) + '-' + CPE.version(cpe) + '.xml'
+        print("Generating %s" % fname)
+        fp = open(os.path.join(output, fname), 'w+')
+        fp.write(xml)
+        fp.close()
+
+    print("Generated %d update files out of %d CPEs" % (len(cpe_need_update), len(cpes)))
+
+
+def get_cpe_ids():
+    print("Getting list of CPE for enabled packages")
+    cmd = ["make", "--no-print-directory", "show-info"]
+    js = json.loads(subprocess.check_output(cmd))
+    return set([v["cpe-id"] for k, v in js.items() if "cpe-id" in v])
+
+
+def resolvepath(path):
+    return os.path.abspath(os.path.expanduser(path))
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--output', dest='output',
+                        help='Path to the output CPE update files', type=resolvepath, required=True)
+    parser.add_argument('--nvd-path', dest='nvd_path',
+                        help='Path to the local NVD database', type=resolvepath, required=True)
+    return parser.parse_args()
+
+
+def __main__():
+    args = parse_args()
+    if not os.path.isdir(args.output):
+        print("ERROR: output directory %s does not exist" % args.output)
+        sys.exit(1)
+    cpedb = CPEDB(args.nvd_path)
+    cpedb.get_xml_dict()
+    cpes = get_cpe_ids()
+    gen_update_xml_reports(cpes, cpedb, args.output)
+
+
+__main__()
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 09/10] Makefile: add new missing-cpe target
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (7 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 13:39 ` [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management Thomas Petazzoni
  2021-01-31 11:05 ` [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

It invokes the recently introduced gen-missing-cpe script.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 Makefile | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Makefile b/Makefile
index 4d334adcd6..a3b17079fb 100644
--- a/Makefile
+++ b/Makefile
@@ -945,6 +945,14 @@ pkg-stats:
 		--html $(O)/pkg-stats.html \
 		--nvd-path $(DL_DIR)/buildroot-nvd
 
+.PHONY: missing-cpe
+missing-cpe:
+	mkdir -p $(O)/cpe-updates
+	@cd "$(CONFIG_DIR)" ; \
+	$(TOPDIR)/support/scripts/gen-missing-cpe \
+		--nvd-path $(DL_DIR)/buildroot-nvd \
+		--output $(O)/cpe-updates
+
 else # ifeq ($(BR2_HAVE_DOT_CONFIG),y)
 
 # Some subdirectories are also package names. To avoid that "make linux"
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (8 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 09/10] Makefile: add new missing-cpe target Thomas Petazzoni
@ 2021-01-07 13:39 ` Thomas Petazzoni
  2021-01-07 19:16   ` Matthew Weber
  2021-01-31 11:05 ` [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
  10 siblings, 1 reply; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-07 13:39 UTC (permalink / raw)
  To: buildroot

This addition to the documentation gives some details about CPE/CVE,
pkg-stats and missing-cpe.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 docs/manual/common-usage.txt | 42 ++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/docs/manual/common-usage.txt b/docs/manual/common-usage.txt
index 9ba87a8339..730c759cfb 100644
--- a/docs/manual/common-usage.txt
+++ b/docs/manual/common-usage.txt
@@ -417,6 +417,48 @@ effects:
   be empty and it's only at the very end of the build that they will
   be populated.
 
+[[vulnerability-management]]
+=== Vulnerability management
+
+Buildroot integrates a number of features that help tracking whether
+known security vulnerabilities are affecting some of the
+packages. Buildroot bases this logic on _CVEs_ (Common Vulnerabilities
+and Exposure) and the https://nvd.nist.gov/vuln[NVD database] provided
+by NIST.
+
+First, each package in Buildroot can be associated to a _CPE_
+identifier, by means of the various +FOO_CPE_ID_<something>+ variables
+that can be provided by each package. See
+xref:generic-package-reference[] for more details about those
+variables. When the _CPE_ identifier of a package is not defined, a
+default value is used, which may or may not be correct. This _CPE_
+identifier is used to find in the NVD database of vulnerabilities
+which _CVEs_ are affecting any given package.
+
+Based on this and the NIST databases, the +make pkg-stats+ target
+produces JSON and HTML formatted reports that indicate for each
+package of the current Buildroot configuration:
+
+* If there are known _CVEs_ for each package. Note that CVEs can
+  manually be ignored on a per-package basis using the
+  +FOO_IGNORED_CVES+ variable, for example if Buildroot has a patch
+  that fixes the vulnerability, or if the vulnerability is not
+  applicable to Buildroot for some reason.
+
+* If the _CPE_ identifier is valid and known in the
+  https://nvd.nist.gov/products/cpe[NIST CPE dictionary]. A _CPE_ is
+  valid is one of the +FOO_CPE_ID_<something>+ variable is defined, or
+  if +FOO_CPE_ID_VALID = YES+ validating that the default value of the
+  _CPE_ identifier is correct. In addition, _CPE_ are checked for
+  existence in the NIST CPE dictionary. If the _CPE_ identifier is not
+  known to the NIST CPE dictionary, then it is possible to contribute
+  to the NIST CPE dictionary: one need to submit a XML document that
+  describes the additional CPE identifier. Buildroot can help
+  generating such XML documents for missing CPE identifiers using the
+  +make missing-cpe+ target. The XML documents are generated in
+  +$(O)/cpe-updates+, and may need to be edited before submission to
+  NIST.
+
 include::eclipse-integration.txt[]
 
 include::advanced.txt[]
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management
  2021-01-07 13:39 ` [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management Thomas Petazzoni
@ 2021-01-07 19:16   ` Matthew Weber
  0 siblings, 0 replies; 17+ messages in thread
From: Matthew Weber @ 2021-01-07 19:16 UTC (permalink / raw)
  To: buildroot

Thomas,

On Thu, Jan 7, 2021 at 7:40 AM Thomas Petazzoni
<thomas.petazzoni@bootlin.com> wrote:
>
> This addition to the documentation gives some details about CPE/CVE,
> pkg-stats and missing-cpe.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> ---
>  docs/manual/common-usage.txt | 42 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
>
> diff --git a/docs/manual/common-usage.txt b/docs/manual/common-usage.txt
> index 9ba87a8339..730c759cfb 100644
> --- a/docs/manual/common-usage.txt
> +++ b/docs/manual/common-usage.txt
> @@ -417,6 +417,48 @@ effects:
>    be empty and it's only at the very end of the build that they will
>    be populated.
>
> +[[vulnerability-management]]
> +=== Vulnerability management
> +
> +Buildroot integrates a number of features that help tracking whether
> +known security vulnerabilities are affecting some of the
> +packages. Buildroot bases this logic on _CVEs_ (Common Vulnerabilities
> +and Exposure) and the https://nvd.nist.gov/vuln[NVD database] provided
> +by NIST.
> +
> +First, each package in Buildroot can be associated to a _CPE_
> +identifier, by means of the various +FOO_CPE_ID_<something>+ variables
> +that can be provided by each package. See
> +xref:generic-package-reference[] for more details about those
> +variables. When the _CPE_ identifier of a package is not defined, a
> +default value is used, which may or may not be correct. This _CPE_
> +identifier is used to find in the NVD database of vulnerabilities
> +which _CVEs_ are affecting any given package.
> +
> +Based on this and the NIST databases, the +make pkg-stats+ target
> +produces JSON and HTML formatted reports that indicate for each
> +package of the current Buildroot configuration:
> +
> +* If there are known _CVEs_ for each package. Note that CVEs can
> +  manually be ignored on a per-package basis using the
> +  +FOO_IGNORED_CVES+ variable, for example if Buildroot has a patch
> +  that fixes the vulnerability, or if the vulnerability is not
> +  applicable to Buildroot for some reason.
> +
> +* If the _CPE_ identifier is valid and known in the
> +  https://nvd.nist.gov/products/cpe[NIST CPE dictionary]. A _CPE_ is
> +  valid is one of the +FOO_CPE_ID_<something>+ variable is defined, or
> +  if +FOO_CPE_ID_VALID = YES+ validating that the default value of the
> +  _CPE_ identifier is correct. In addition, _CPE_ are checked for
> +  existence in the NIST CPE dictionary. If the _CPE_ identifier is not
> +  known to the NIST CPE dictionary, then it is possible to contribute
> +  to the NIST CPE dictionary: one need to submit a XML document that
> +  describes the additional CPE identifier. Buildroot can help
> +  generating such XML documents for missing CPE identifiers using the
> +  +make missing-cpe+ target. The XML documents are generated in
> +  +$(O)/cpe-updates+, and may need to be edited before submission to

I'd replace that last part with something like the following....
"and will need to be edited before submission to NIST.  The edits
would focus on verifying the version information and reviewing the
reference URLs are reachable with a valid type (suggests for types are
provided in the generated file).  If a previous _CPE_ entry was found
in the NIST CPE dictionary, the XML document generation tries to
minimize the entry differences between the existing versions in the
dictionary.  The XML document generation uses the XML from the last
version of that CPE as a template for the new submission."

Regards,
Matt

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script
  2021-01-07 13:39 ` [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script Thomas Petazzoni
@ 2021-01-07 19:33   ` Matthew Weber
  2021-01-31 13:11     ` Thomas Petazzoni
  0 siblings, 1 reply; 17+ messages in thread
From: Matthew Weber @ 2021-01-07 19:33 UTC (permalink / raw)
  To: buildroot

Gregory / Thomas,

On Thu, Jan 7, 2021 at 7:40 AM Thomas Petazzoni
<thomas.petazzoni@bootlin.com> wrote:
>
> From: Matt Weber <matthew.weber@rockwellcollins.com>
>
> This script queries the list of CPE IDs for the packages of the
> current configuration (based on the "make show-info" output), and:
>
>  - for CPE IDs that do not have any matching entry in the CPE
>    database, it emits a warning
>
>  - for CPE IDs that do have a matching entry, but not with the same
>    version, it generates a snippet of XML that can be used to propose
>    an updated version to NIST.
>
> Ref: NIST has a group email (cpe_dictionary at nist.gov) used to
> recieve these version update and new entry xml files.  They do
> process the XML and provide feedback. In some cases they will
> propose back something different where the vendor or version is
> slightly different.
>
> Limitations
>  - Currently any use of non-number version identifiers isn't
>    supported by NIST as they use ranges to determine impact
>    of a CVE
>  - Any Linux version from a non-upstream is also not supported
>    without manually adjusting the information as the custom
>    kernel will more then likely not match the upstream version
>    used in the dictionary
>
> Signed-off-by: Matt Weber <matthew.weber@rockwellcollins.com>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> ---
>  support/scripts/gen-missing-cpe | 65 +++++++++++++++++++++++++++++++++
>  1 file changed, 65 insertions(+)
>  create mode 100755 support/scripts/gen-missing-cpe
>
> diff --git a/support/scripts/gen-missing-cpe b/support/scripts/gen-missing-cpe
> new file mode 100755
> index 0000000000..22801ca488
> --- /dev/null
> +++ b/support/scripts/gen-missing-cpe
> @@ -0,0 +1,65 @@
> +#!/usr/bin/env python3
> +
> +import argparse
> +import sys
> +import json
> +import subprocess
> +import os
> +from cpedb import CPEDB, CPE
> +
> +
> +def gen_update_xml_reports(cpes, cpedb, output):
> +    cpe_need_update = []
> +
> +    for cpe in cpes:
> +        result = cpedb.find(cpe)
> +        if not result:
> +            result = cpedb.find_partial(CPE.no_version(cpe))
> +            if result:
> +                cpe_need_update.append(cpe)
> +            else:
> +                print("WARNING: no match found for '%s'" % cpe)

I've tested with the following defconfig and I get the following
output.  I know for sure that some of these are missing/need
update/match. i.e. busybox has a exact match and kmod is pending
update and at 25.

BR2_aarch64=y
BR2_TOOLCHAIN_EXTERNAL=y
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="4.16.7"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
BR2_TARGET_ROOTFS_EXT2=y
# BR2_TARGET_ROOTFS_TAR is not set


make missing-cpe
mkdir -p /accts/mlweber1/tmp.G9papvL0Eg-buildroot/output/cpe-updates
CPE: Setting up NIST dictionary
CPE: Loading CACHED dictionary
Getting list of CPE for enabled packages
WARNING: no match found for 'cpe:2.3:a:kernel:util-linux:2.36.1:*:*:*:*:*:*:*'
WARNING: no match found for 'cpe:2.3:a:busybox:busybox:1.32.0:*:*:*:*:*:*:*'
WARNING: no match found for 'cpe:2.3:a:kernel:kmod:27:*:*:*:*:*:*:*'
WARNING: no match found for 'cpe:2.3:a:gnu:zlib:1.2.11:*:*:*:*:*:*:*'
WARNING: no match found for 'cpe:2.3:a:linux:linux_kernel:4.16.7:*:*:*:*:*:*:*'
WARNING: no match found for 'cpe:2.3:a:openssl:openssl:1.1.1i:*:*:*:*:*:*:*'
Generated 0 update files out of 6 CPEs


Also  I noticed in this build that I get a list of 6 CPEs but pkgstats
doesn't list the same ones.  it looks like the kernel dependencies of
kmod / openssl / zlib don't get reflected in the per defconfig
pkgstats.  Just checking if that was a known  bug.


> +
> +    for cpe in cpe_need_update:
> +        xml = cpedb.gen_update_xml(cpe)
> +        fname = CPE.product(cpe) + '-' + CPE.version(cpe) + '.xml'
> +        print("Generating %s" % fname)
> +        fp = open(os.path.join(output, fname), 'w+')
> +        fp.write(xml)
> +        fp.close()
> +
> +    print("Generated %d update files out of %d CPEs" % (len(cpe_need_update), len(cpes)))
> +
> +
> +def get_cpe_ids():
> +    print("Getting list of CPE for enabled packages")
> +    cmd = ["make", "--no-print-directory", "show-info"]
> +    js = json.loads(subprocess.check_output(cmd))
> +    return set([v["cpe-id"] for k, v in js.items() if "cpe-id" in v])
> +
> +
> +def resolvepath(path):
> +    return os.path.abspath(os.path.expanduser(path))
> +
> +
> +def parse_args():
> +    parser = argparse.ArgumentParser()
> +    parser.add_argument('--output', dest='output',
> +                        help='Path to the output CPE update files', type=resolvepath, required=True)
> +    parser.add_argument('--nvd-path', dest='nvd_path',
> +                        help='Path to the local NVD database', type=resolvepath, required=True)
> +    return parser.parse_args()
> +
> +
> +def __main__():
> +    args = parse_args()
> +    if not os.path.isdir(args.output):
> +        print("ERROR: output directory %s does not exist" % args.output)
> +        sys.exit(1)
> +    cpedb = CPEDB(args.nvd_path)
> +    cpedb.get_xml_dict()
> +    cpes = get_cpe_ids()
> +    gen_update_xml_reports(cpes, cpedb, args.output)
> +
> +
> +__main__()
> --
> 2.29.2
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary
  2021-01-07 13:39 ` [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary Thomas Petazzoni
@ 2021-01-07 19:37   ` Matthew Weber
  2021-01-31 12:55     ` Thomas Petazzoni
  0 siblings, 1 reply; 17+ messages in thread
From: Matthew Weber @ 2021-01-07 19:37 UTC (permalink / raw)
  To: buildroot

Gregory/Thomas,

On Thu, Jan 7, 2021 at 7:40 AM Thomas Petazzoni
<thomas.petazzoni@bootlin.com> wrote:
>
> This commit extends pkg-stats to leverage the recently introduced
> CPEDB class to verify that the CPEs provided by Buildroot packages are
> indeed known in the official CPE dictionnary provided by NVD.
>
> Co-Developed-by: Gr?gory Clement <gregory.clement@bootlin.com>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> ---
>  support/scripts/pkg-stats | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
>
> diff --git a/support/scripts/pkg-stats b/support/scripts/pkg-stats
> index 854ece389d..2c82dc96ad 100755
> --- a/support/scripts/pkg-stats
> +++ b/support/scripts/pkg-stats
> @@ -33,7 +33,7 @@ brpath = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))
>  sys.path.append(os.path.join(brpath, "utils"))
>  from getdeveloperlib import parse_developers  # noqa: E402
>  import cve as cvecheck  # noqa: E402
> -
> +from cpedb import CPEDB  # noqa: E402
>
>  INFRA_RE = re.compile(r"\$\(eval \$\(([a-z-]*)-package\)\)")
>  URL_RE = re.compile(r"\s*https?://\S*\s*$")
> @@ -226,7 +226,8 @@ class Package:
>
>          if var in self.all_cpeids:
>              self.cpeid = self.all_cpeids[var]
> -            self.status['cpe'] = ("ok", "verified CPE identifier")
> +            # Set a preliminary status, it might be overridden by check_package_cpes()
> +            self.status['cpe'] = ("warning", "not checked against CPE dictionnary")
>          else:
>              self.status['cpe'] = ("error", "no verified CPE identifier")
>
> @@ -601,6 +602,18 @@ def check_package_cves(nvd_path, packages):
>                  pkg.status['cve'] = ("ok", "not affected by CVEs")
>
>
> +def check_package_cpes(nvd_path, packages):
> +    cpedb = CPEDB(nvd_path)
> +    cpedb.get_xml_dict()
> +    for p in packages:
> +        if not p.cpeid:
> +            continue
> +        if cpedb.find(p.cpeid):
> +            p.status['cpe'] = ("ok", "verified CPE identifier")
> +        else:
> +            p.status['cpe'] = ("error", "CPE identifier unknown in CPE database")

I noticed in the pkgstats output that busybox which has an exact match
was coming up as the following.

cpe:2.3:a:busybox:busybox:1.32.0:*:*:*:*:*:*:*
CPE identifier unknown in CPE database

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 00/10] CPE validation
  2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
                   ` (9 preceding siblings ...)
  2021-01-07 13:39 ` [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management Thomas Petazzoni
@ 2021-01-31 11:05 ` Thomas Petazzoni
  10 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-31 11:05 UTC (permalink / raw)
  To: buildroot

Hello,

On Thu,  7 Jan 2021 14:39:37 +0100
Thomas Petazzoni <thomas.petazzoni@bootlin.com> wrote:

>  - PATCHes 1 to 5 are just fixes/improvements to pkg-stats.

I have applied patches 1 to 5 which are small fixes/improvements to
pkg-stats.

For the remainder of the patches, I will soon be posting an updated
version of the patch series.

Best regards,

Thomas Petazzoni
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary
  2021-01-07 19:37   ` Matthew Weber
@ 2021-01-31 12:55     ` Thomas Petazzoni
  0 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-31 12:55 UTC (permalink / raw)
  To: buildroot

On Thu, 7 Jan 2021 13:37:59 -0600
Matthew Weber via buildroot <buildroot@busybox.net> wrote:

> > +def check_package_cpes(nvd_path, packages):
> > +    cpedb = CPEDB(nvd_path)
> > +    cpedb.get_xml_dict()
> > +    for p in packages:
> > +        if not p.cpeid:
> > +            continue
> > +        if cpedb.find(p.cpeid):
> > +            p.status['cpe'] = ("ok", "verified CPE identifier")
> > +        else:
> > +            p.status['cpe'] = ("error", "CPE identifier unknown in CPE database")  
> 
> I noticed in the pkgstats output that busybox which has an exact match
> was coming up as the following.
> 
> cpe:2.3:a:busybox:busybox:1.32.0:*:*:*:*:*:*:*
> CPE identifier unknown in CPE database

As discussed on IRC, I think this was due to the .pkl being empty to
due XML parsing issue with an older Python version. Gr?gory has worked
on this, and solved the problem. This will be in the v2 of the patch
series.

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script
  2021-01-07 19:33   ` Matthew Weber
@ 2021-01-31 13:11     ` Thomas Petazzoni
  0 siblings, 0 replies; 17+ messages in thread
From: Thomas Petazzoni @ 2021-01-31 13:11 UTC (permalink / raw)
  To: buildroot

Hello,

On Thu, 7 Jan 2021 13:33:18 -0600
Matthew Weber via buildroot <buildroot@busybox.net> wrote:

> I've tested with the following defconfig and I get the following
> output.  I know for sure that some of these are missing/need
> update/match. i.e. busybox has a exact match and kmod is pending
> update and at 25.
> 
> BR2_aarch64=y
> BR2_TOOLCHAIN_EXTERNAL=y
> BR2_LINUX_KERNEL=y
> BR2_LINUX_KERNEL_CUSTOM_VERSION=y
> BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="4.16.7"
> BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
> BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
> BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
> BR2_TARGET_ROOTFS_EXT2=y
> # BR2_TARGET_ROOTFS_TAR is not set
> 
> 
> make missing-cpe
> mkdir -p /accts/mlweber1/tmp.G9papvL0Eg-buildroot/output/cpe-updates
> CPE: Setting up NIST dictionary
> CPE: Loading CACHED dictionary
> Getting list of CPE for enabled packages
> WARNING: no match found for 'cpe:2.3:a:kernel:util-linux:2.36.1:*:*:*:*:*:*:*'
> WARNING: no match found for 'cpe:2.3:a:busybox:busybox:1.32.0:*:*:*:*:*:*:*'
> WARNING: no match found for 'cpe:2.3:a:kernel:kmod:27:*:*:*:*:*:*:*'
> WARNING: no match found for 'cpe:2.3:a:gnu:zlib:1.2.11:*:*:*:*:*:*:*'
> WARNING: no match found for 'cpe:2.3:a:linux:linux_kernel:4.16.7:*:*:*:*:*:*:*'
> WARNING: no match found for 'cpe:2.3:a:openssl:openssl:1.1.1i:*:*:*:*:*:*:*'
> Generated 0 update files out of 6 CPEs

So here as well, the issue was that your .pkl cache was empty, due to
parsing issue. This should be resolved in v2.

> Also  I noticed in this build that I get a list of 6 CPEs but pkgstats
> doesn't list the same ones.  it looks like the kernel dependencies of
> kmod / openssl / zlib don't get reflected in the per defconfig
> pkgstats.  Just checking if that was a known  bug.

gen-missing-cpe simply gets the CPE IDs visible in the "make show-info"
output, without caring whether things are a target or a host package,
so it captures all packages that have a CPE ID.

On the other hand, "make pkg-stats" looks at the package names in the
"show-info" output and these contain "host-" prefixes for host packages
which prevents those names from matching actual package names. I
proposed a patch at
http://lists.busybox.net/pipermail/buildroot/2021-January/300286.html,
but Yann/Arnout made some suggestions on how to better handle this.
It'll be part of my v2 of this series.

Thanks!

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-01-31 13:11 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-07 13:39 [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 01/10] support/scripts/pkg-stats: improvements in is_status_*() methods Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 02/10] support/scripts/pkg-stats: fix the status reporting of CVEs Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 03/10] support/scripts/pkg-stats: drop unused cpeid_name() function Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 04/10] support/scripts/pkg-stats: drop unused --cpeid option Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 05/10] support/scripts/pkg-stats: fix flake8 warnings Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 06/10] support/scripts/cpedb.py: new CPE XML helper Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 07/10] support/scripts/pkg-stats: check CPE existence in CPE dictionnary Thomas Petazzoni
2021-01-07 19:37   ` Matthew Weber
2021-01-31 12:55     ` Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 08/10] support/scripts/gen-missing-cpe: add new script Thomas Petazzoni
2021-01-07 19:33   ` Matthew Weber
2021-01-31 13:11     ` Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 09/10] Makefile: add new missing-cpe target Thomas Petazzoni
2021-01-07 13:39 ` [Buildroot] [PATCH 10/10] docs/manual: add details about vulnerability management Thomas Petazzoni
2021-01-07 19:16   ` Matthew Weber
2021-01-31 11:05 ` [Buildroot] [PATCH 00/10] CPE validation Thomas Petazzoni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.