24 Commits

Author SHA1 Message Date
Thomas Perale
6762c42e74 support/scripts/cve-check: add cve-check script
Enriches the input CycloneDX SBOM with vulnerability information and
analysis from the NVD database.

The NVD database is cloned using a mirror of it and the content is compared
locally. By default the path 'dl/buildroot-nvd' is used.

Example usage to analyse vulnerabilities of an input CycloneDX SBOM:

$ make show-info | utils/generate-cyclonedx | support/script/cve-check

The 'cve-check' can also be used to only enrich the vulnerabilities
present on the input SBOM with a set metadata (description, cvss,
references, ...) without applying an analysis.

With the following command the vulnerabilities ignored by Buildroot
present in the CycloneDX SBOM are enriched with description, cvss, etc
...

$ make show-info | utils/generate-cyclonedx | support/script/cve-check --enrich-only

Signed-off-by: Thomas Perale <thomas.perale@mind.be>
[Peter: fix minor flake8 issues]
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2025-11-20 17:13:40 +01:00
Thomas Perale
867017e736 support/scripts/cve.py: don't call download_nvd
This patch move the 'download_nvd' call to the 'pkg-stats' script
instead of automatically calling 'read_nvd_dir'.

Since the cve.py file can be used as a library it's up to the caller to
decide whether or not to update the NVD database.

Signed-off-by: Thomas Perale <thomas.perale@mind.be>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2025-11-20 17:03:39 +01:00
Thomas Perale
35f376d88e support/scripts/cve.py: fix CPE matching
Given the following criteria: `cpe:2.3:a:oneidentitty:syslog-ng:*:*:*:*:-:*:*:*`.
The former `cpe_matches` implementation would match with the following
CPE: `cpe:2.3:a:oneidentitty:syslog-ng:4.71:*:*:*:premium:*:*:*`.

The 'hyphen' ('-') meaning is "Not Attributed" (NA) a criteria with no
attributed software edition shouldn't match with a CPE with an attributed
software edition:

https://csrc.nist.gov/pubs/ir/7695/final

This patch also create a distinct 'CPE' object that aggregate the
function specifics to CPEs like it's done for 'CVE'.

Signed-off-by: Thomas Perale <thomas.perale@mind.be>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2025-11-04 22:26:15 +01:00
Thomas Perale
4b318dea17 support/scripts/cve.py: remove print from cve.py library
The `support/script/cve.py` file is used as a library. Depending on how
you use this library you might not want to write content to stdout when
calling its function.

This patch move the 'updating' log to the 'pkg-stats' script and write
the alert when LooseVersion doesn't have a version to stderr.

Signed-off-by: Thomas Perale <thomas.perale@mind.be>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2025-11-04 22:19:07 +01:00
Thomas Petazzoni
cff52ab07c support/scripts/cve.py: handle CVEs with 'configurations' but no 'nodes' inside
The each_cpe() method is careful that some CVEs have no
"configurations", but some CVEs such as
https://nvd.nist.gov/vuln/detail/CVE-2025-32915 apparently have a
"configurations" node, but no "nodes" inside the "configurations",
causing an exception:

Traceback (most recent call last):
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 1382, in <module>
    __main__()
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 1371, in __main__
    check_package_cves(args.nvd_path, packages)
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 679, in check_package_cves
    check_package_cve_affects(cve, cpe_product_pkgs)
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 638, in check_package_cve_affects
    for product in cve.affected_products:
                   ^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildroot/buildroot-stats/support/scripts/cve.py", line 185, in affected_products
    return set(cpe_product(p['id']) for p in self.each_cpe())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildroot/buildroot-stats/support/scripts/cve.py", line 185, in <genexpr>
    return set(cpe_product(p['id']) for p in self.each_cpe())
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildroot/buildroot-stats/support/scripts/cve.py", line 173, in each_cpe
    for node in nodes['nodes']:
                ~~~~~^^^^^^^^^
KeyError: 'nodes'

Fixes:
  54f8d97c91 ("support/scripts/pkg-stats: adapt to NVD v2 json format")

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
[Romain:
  - add reference to buildroot commit introducing the issue
  - a similar patch was sent by Daniel Lang (thanks!)
  - needed on master to fix "Daily results" email]
(cherry picked from commit 67422b9d9c)
Signed-off-by: Romain Naour <romain.naour@smile.fr>
2025-08-23 20:12:44 +02:00
Raphaël Mélotte
56ea5a0226 support/scripts/pkg-stats: add support for reporting stale CVE entries
The NVD database contains some CPEs that are wrongly not associated
with any version number. They are for example sometimes associated
with very old CVEs.
Those CPEs are annoying, because they pollute our pkg-stat CVE results
with CVE entries which actually don't affect us.

The proper way to solve it is, and should remain, to fix the NVD
database by reporting these issues. Having to deal with a lot of
CVEs/CPEs, the NVD database is however slow to be updated.

To reduce the noise in our pkg-stats results in the meantime, one
possibility is to add <PKG_IGNORE_CVES> entries for those CVEs.  This
however comes with the downside that even once the NVD database gets
fixed, those ignored entries risk remaining in Buildroot forever
because they are undetected.

This commit tries to address this downside by checking for and
reporting CVEs that are ignored in Buildroot, but where the
NVD reports our package version as unaffected. Those CVEs will appear
in the 'CVEs Ignored' column as '(stale)', and the cell will be
colored the same way warnings are. This should allow us to detect and
remove those entries.

It can be tested for example by adding the following variable to the
apache package (for a CVE that was recently fixed in the NVD database):
APACHE_IGNORE_CVES = CVE-1999-0236

Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2025-05-18 10:53:10 +02:00
Raphaël Mélotte
d0a7a46813 support/scripts/cve.py: remove unused each_product()
The last usage of each_product() was removed in commit
52ae092046 ("support/scripts/cve.py: use
the JSON data in 1.1 schema").

Since it's now unused, remove it.

Signed-off-by: Raphaël Mélotte <raphael.melotte@mind.be>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2025-05-17 17:28:38 +02:00
Peter Korsgaard
da0d6d5834 support/scripts/cve.py: fix 'useful' typo
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
2024-09-21 20:39:20 +02:00
Thomas Petazzoni
2cb286c8a2 Revert "scripts/support/cve.py: avoid a complete clone of the CVE git repository"
This reverts commit e0ed05cc00.

Since this commit has been applied, the update of the NVD data fails:

Checking packages CVEs
Updating from https://github.com/fkie-cad/nvd-json-data-feeds/
Traceback (most recent call last):
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 1346, in <module>
    __main__()
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 1335, in __main__
    check_package_cves(args.nvd_path, packages)
  File "/home/buildroot/buildroot-stats/./support/scripts/pkg-stats", line 660, in check_package_cves
    for cve in cvecheck.CVE.read_nvd_dir(nvd_path):
  File "/home/buildroot/buildroot-stats/support/scripts/cve.py", line 105, in read_nvd_dir
    CVE.download_nvd(nvd_git_dir)
  File "/home/buildroot/buildroot-stats/support/scripts/cve.py", line 74, in download_nvd
    subprocess.check_call(
  File "/usr/lib/python3.11/subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['git', 'pull', '--depth', '1']' returned non-zero exit status 128.

Since we couldn't immediately figure out what's going on, let's revert
for now until this get investigated and fixed.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2024-09-12 12:39:20 +02:00
Michael Trimarchi
e0ed05cc00 scripts/support/cve.py: avoid a complete clone of the CVE git repository
Just a simple clone and pull with --depth 1 should be enough to parse
the CVE data and generate the pkg-stats report.

From a full clone and a depth-1 clone, and the size delta is 2.9GiB
vs. 2.2GiB.

The download size does change: from 983.55MiB down to 270.78MiB.

It's a net time win too: 2m17s vs 1min7s (on a 100Mbps link).

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2024-09-03 20:44:52 +02:00
Yann E. MORIN
fee7efafd0 support/scripts: use FKIE git tree
Currently, we grab the per-year CVE feeds, in two passes: first, we grab
the meta files, and check whether something has changed since last we
downloaded it; second, we download the feed proper, unless the meta file
has not changed, in which case we use the locally cached feed.

However, it has appeared that the FKIE releases no longer provide the
meta files, which means that (once again), our daily reports are broken.

The obvious fix would be to drop the use of the meta file, and always
and unconditionally download the feeds. That's relatively trivial to do,
but the feeds are relatively big (even as xz-xompressed).

However, the CVE database from FKIE is available as a git tree. Git is
pretty good at only sending delta when updating a local copy. In
addition, the git tree, contains each CVE as an individual file, so it
is relatively easier to scan and parse.

Switch to using a local git clone.

Slightly surprisingly (but not so much either), parsing the CVE files is
much faster when using the git working copy, than it is when parsing the
per-year feeds: indeed, the per-year feeds are xz-compressed, and even
if python is slow-ish to scan a directory and opening files therein, it
is still much faster than to decompress xz files. The timing delta [0]
is ~100s before and ~10s now, about a ten time improvement, over the
whole package set.

The drawback, however, is that the git tree is much bigger on-disk, from
~55MiB for the per-year compressed feeds, to 2.1GiB for the git tree
(~366MiB) and a working copy (~1.8GiB)... Given very few people are
going to use that, that's considered acceptable...

Eventually, with a bit of hacking [1], the two pkg-stats, before and
after this change, yield the same data (except for the date and commit
hash).

[0] hacking support/scripts/pkg-stats to display the time before/after
the CVE scan, and hacking support/scripts/cve.py to do no download so
that only the CVE scan happens (and also because the meta files are no
longer available).

[1] sorting the CVE lists in json, sorting the json keys, and using the
commit from the FKIE git tree that was used for the current per-year
feeds.

Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
2024-03-20 21:06:42 +01:00
Yann E. MORIN
5f253e3e04 support/scripts/cve: fix running on older ijson versions
Commit 22b6945552 (support/scripts/cve.py: switch from NVD to FKIE for
the JSON files) had to change the decompressor from gz to xz, as the new
location is using xz compression.

That commit mentioned that it was spawning an external xz process to do
the decompression, on the pretence that "there is no xz decompressor in
Python stdlib."

Before version 3.1, ijson.items() only accepted a file-like object as
input (that file-like object could yield bytes() or str(), both were
supported). Starting with version 3.1, ijson.items() also accepts that
it be directly passed bytes() or str() directly. subprocess.check_output()
means we are now passing bytes() to ijson.items(), so it fails on ijson
versions before 3.1, with failures such as:

    [...]
      File "/usr/lib/python3/dist-packages/ijson/backends/python.py", line 25, in Lexer
        if type(f.read(0)) == bytetype:
    AttributeError: 'bytes' object has no attribute 'read'

Ubuntu 20.04, on which the pkg-stats run to generate the daily report,
only has ijson 2.3. More recent distros have more recent versions of
ijson, like Fedora 39 that has 3.2.3, recent enough to support being fed
bytes(). Commit 22b6945552 was tested on Fedora 39, so did not catch
the issue.

However, the reasoning in 22b6945552 is wrong: there *is* the lzma
module, at least since python 3.3 (that is, aeons ago), which is able to
read xz-compressed files; it also has an API similar to the gzip module,
and can provide a file-like object that exposes the decompressed data.

So, do just that: provide an lzma-wrapped file-like object to ijson, so
that we can eventually recover our daily reports that everything is
broken! :-]

Note that this construct still works on recent versions!

Reported-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2024-02-29 18:47:16 +01:00
Yann E. MORIN
75a3562324 support/scripts/pkg-stats: sort CVEs in HTML output
While the old NVD JSON feed provided data files where the CVEs were
sorted by ID, the new feed from FKIE does not have sorted CVEs.

Add a method to sort a list of CVE IDs (i.e. CVE ID strings, not CVE
objects!), and use that when emiting the HTML output.

The JSON output need not be sorted, because it is supposed to be used
for post-processing, and we do not care about the ordering there; a
consumer interested in sorting should sort on their side.

Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
2024-02-28 20:16:54 +01:00
Yann E. MORIN
54f8d97c91 support/scripts/pkg-stats: adapt to NVD v2 json format
Commit 22b6945552 (support/scripts/cve.py: switch from NVD to FKIE for
the JSON files) missed the fact that the layout of the FKIE data files
are different from the original NVD ones. They are formatted according
to the NVD v2 API.

Most differences are relatively trivial fields renaming, and those are
easily spotted in this patch.

There is however one key difference in the layout of the configurations.
Where the NVD had "configurations" as an object with a "nodes" key, the
FKIE has a "configurations" as a list of objects with a single "nodes"
key; i.e. it is one-level deeper.

Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
2024-02-28 20:12:08 +01:00
Arnout Vandecappelle
6b2d0b466c support/scripts/cve.py: start in 1999
When the CVE lookup was added in commit
4a157be9ef, the starting year of the JSON
files was set to 2002. However, there are also CVEs from 1999, 2000 and
2001. It is not clear why these were skipped back then.

Set the start year to 1999 to capture these old CVEs too.

Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2024-02-07 17:29:41 +01:00
Arnout Vandecappelle
22b6945552 support/scripts/cve.py: switch from NVD to FKIE for the JSON files
NVD will deprecate the v1.1 API which allows us to download the full
database as individual JSON files. Instead, there's a horribly crappy
API that is extremely slow and subject to race conditions.

Fortunately, there is a project, Fraunhofer FKIE - Cyber Analysis and
Defense [1], that goes through the effort of adapting to this new API
and regenerating the convenient JSON files. The JSON files and meta
files are re-generated daily.

Instead of implementing the NVD v2 API, we decided to just use the JSON
files generatd by fkie-cad. That saves us the effort of solving the race
conditions, devising a cache mechanism that works, handling the frequent
gateway timeouts on the NVD servers, dealing with the rate limiting, and
keeping up with changes in the API.

Switch to this repository on github as NVD_BASE_URL. The file name is
also slightly different (CVE-20XX.json instead of nvdcve-1.1-20XX.json).

The fkie-cad repository compresses with xz instead of gz. Therefore:
 - rename the filename variables to _xz instead of _gz;
 - use xz as a subprocess because there is no xz decompressor in Python
   stdlib.

[1] https://www.fkie.fraunhofer.de/en/departments/cad.html

Cc: Daniel Lang <dalang@gmx.at>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2024-02-07 17:29:34 +01:00
Petr Vorel
35f15db30a support: utils: use python3 explicitly
Python 2 is EOL sice 2020 [1], it's still available on distros, but may not
be installed by default (as being replaced by python3).

Thus remove compatibility imports:
from __future__ import print_function
from __future__ import absolute_import

Tested with python3 -m py_compile.

[1] https://www.python.org/doc/sunset-python-2/

Signed-off-by: Petr Vorel <petr.vorel@gmail.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
2021-09-22 21:30:24 +02:00
Thomas Petazzoni
d06bf96097 support/scripts/cve.py: use proper CPE ID version when available
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2021-04-18 18:20:27 +02:00
Peter Korsgaard
f31227e628 support/scripts/cve.py: use fast ijson backend if available on old ijson versions
ijson < 2.5 (as available in Debian 10) use the slow python backend by
default instead of the most efficient one available like modern ijson
versions, significantly slowing down cve checking. E.G.:

time ./support/scripts/pkg-stats --nvd-path ~/.nvd -p avahi --html foobar.html

Goes from
174,44s user 2,11s system 99% cpu 2:58,04 total

To
93,53s user 2,00s system 98% cpu 1:36,65 total

E.G. almost 2x as fast.

As a workaround, detect when the python backend is used and try to use a
more efficient one instead.  Use the yajl2_cffi backend as recommended by
upstream, as it is most likely to work, and print a warning (and continue)
if we fail to load it.

The detection is slightly complicated by the fact that ijson.backends used
to be a reference to a backend module, but is nowadays a string (without the
ijson.backends prefix).

Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
2021-04-17 09:14:40 +02:00
Thomas Petazzoni
e3ef352ef6 support/scripts/{pkg-stats, cve.py}: support CPE ID based matching
This commit modifies cve.py, as well as its users cve-checker and
pkg-stats to support CPE ID based matching, for packages that have CPE
ID information.

One of the non-trivial thing is that we can't simply iterate over all
CVEs, and then iterate over all our packages to see which packages
have CPE ID information that match the CPEs affected by the
CVE. Indeed, this is an O(n^2) operation.

So instead, we do a pre-filtering of packages potentially affected. In
check_package_cves(), we build a cpe_product_pkgs dict that associates
a CPE product name to the packages that have this CPE product
name. The CPE product name is either derived from the CPE information
provided by the package if available, and otherwise we use the package
name, which is what was used prior to this patch.

And then, when we look at CVEs, we only consider the packages that
have a CPE product name matching the CPE products affected by the
CVEs. This is done in check_package_cve_affects().

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2021-01-04 21:38:20 +01:00
Thomas Petazzoni
1ff7f003e1 support/scripts/cve.py: properly match CPEs with version '*'
Currently, when the version encoded in a CPE is '-', we assume all
versions are affected, but when it's '*' with no further range
information, we assume no version is affected.

This doesn't make sense, so instead, we handle '*' and '-' in the same
way. If there's no version information available in the CVE CPE ID, we
assume all versions are affected.

This increases quite a bit the number of CVEs and package affected:

-    "total-cves": 302,
-    "pkg-cves": 100,
+    "total-cves": 597,
+    "pkg-cves": 135,

For example, CVE-2007-4476 has a CPE ID of:

    cpe:2.3:a:gnu:tar:*:*:*:*:*:*:*:*

So it should be taken into account. In this specific case, it is
combined with an AND with CPE ID
cpe:2.3:o:suse:suse_linux:10:*:enterprise_server:*:*:*:*:* but since
we don't support this kind of matching, we'd better be on the safe
side, and report this CVE as affecting tar, do an analysis of the CVE
impact, and document it in TAR_IGNORE_CVES.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Reviewed-by: Matt Weber <matthew.weber@rockwellcollins.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2020-11-26 16:31:37 +01:00
Gregory CLEMENT
2a2f69d672 support/scripts: make CVE class independent of the Packaage class
The affects method of the CVE uses the Package class defined in
pkg-stats. The purpose of migrating the CVE class outside of pkg-stats
was to be able to reuse it from other scripts. So let's remove the
Package dependency and only use the needed information.

Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2020-08-28 11:00:40 +02:00
Gregory CLEMENT
52ae092046 support/scripts/cve.py: use the JSON data in 1.1 schema
In 2019, the JSON vulnerability feeds switched their schema from
version 1.0 to 1.1.

The main difference is the removal of the "affects" element that we
were using to check if a package was affected by a CVE.

This information is now available in the "configuration" element which
contains the cpeid as well as properties about the versions
affected. Instead of having a list of the versions affected, with
these properties, it is possible to have a range of versions.

Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2020-08-28 09:14:39 +02:00
Gregory CLEMENT
f8ee65570a support/scripts/pkg-stats: move CVE related code to a separate module
In order to be able to use the CVE checking logic outside of
pkg-stats, move the CVE class in a module that can be used by other
scripts.

Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2020-08-28 09:14:38 +02:00