diff mbox series

cve-check.bbclass: support embedded SW components with different version number

Message ID 20231016070106.2772303-1-mikko.rapeli@linaro.org
State New
Headers show
Series cve-check.bbclass: support embedded SW components with different version number | expand

Commit Message

Mikko Rapeli Oct. 16, 2023, 7:01 a.m. UTC
Many recipes embed other SW components. The name and version of the
embedded SW component differs from the main recipe. To detect CVEs in the
embedded SW component, it needs to be added to CVE_PRODUCT list using
name of the SW product in CVE database or with "vendor:product" syntax.
Then the version of the embedded SW component can be set using
CVE_VERSION_product variable.

For example in meta-arm, trusted-firmware-a embeds mbed_tls SW component.
Thus trusted-firmware-a can add CVE_PRODUCT for it since CVE database
uses product name "mbed_tls":

CVE_PRODUCT += "mbed_tls"

and set the version of mbed_tls:

CVE_VERSION_mbed_tls = "2.28.4"

(Real patches for both are a bit more complex due to conditional build
enabling mbed_tls support and due to mbed_tls version being set in an
.inc file.)

Now trusted-firmware-a CVE check output shows:

NOTE: recipe trusted-firmware-a-2.9.0+gitd3e71ead6ea5bc3555ac90a446efec84ef6c6122-r0: task do_cve_check: Started
WARNING: trusted-firmware-a-2.9.0+gitd3e71ead6ea5bc3555ac90a446efec84ef6c6122-r0 do_cve_check: Found unpatched CVE (CVE-2021-36647 CVE-2021-43666 CVE-2021-45451 CVE-2023-43615), for more information check /home/builder/src/base/build/tmp/work/arm64-poky-linux/trusted-firmware-a/2.9.0+gitd3e71ead6ea5bc3555ac90a446efec84ef6c6122/temp/cve.log
NOTE: recipe trusted-firmware-a-2.9.0+gitd3e71ead6ea5bc3555ac90a446efec84ef6c6122-r0: task do_cve_check: Succeeded

Here CVE-2023-43615 is a newly added and fixed CVE in version 2.28.5 and the CVEs
from 2021 need to be checked but are likely fixed in 2.28.3 and newer 2.28.y releases.

Note that CVE-2023-43615 does not impact trusted-firmware-a since it doesn't use
TLS or null or RC4 ciphers, but I think it's a good idea to extend
CVE checker for this use case. I hope the "CVE_VERSION_vendor:product"
does not cause odd breakages.

Signed-off-by: Mikko Rapeli <mikko.rapeli@linaro.org>
---
 meta/classes/cve-check.bbclass | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

Comments

Marta Rybczynska Oct. 19, 2023, 8:19 a.m. UTC | #1
On Mon, Oct 16, 2023 at 9:01 AM Mikko Rapeli <mikko.rapeli@linaro.org> wrote:
>
> Many recipes embed other SW components. The name and version of the
> embedded SW component differs from the main recipe. To detect CVEs in the
> embedded SW component, it needs to be added to CVE_PRODUCT list using
> name of the SW product in CVE database or with "vendor:product" syntax.
> Then the version of the embedded SW component can be set using
> CVE_VERSION_product variable.
>
> For example in meta-arm, trusted-firmware-a embeds mbed_tls SW component.
> Thus trusted-firmware-a can add CVE_PRODUCT for it since CVE database
> uses product name "mbed_tls":
>
> CVE_PRODUCT += "mbed_tls"
>
> and set the version of mbed_tls:
>
> CVE_VERSION_mbed_tls = "2.28.4"
>
> (Real patches for both are a bit more complex due to conditional build
> enabling mbed_tls support and due to mbed_tls version being set in an
> .inc file.)
>

I like the support for embedded software. In this approach, I'm wondering
how it would work for packages like curl that have multiple CPEs. Would we
need  to duplicate the list of CPEs?

There are layers/recipes where we have a very long list of embedded components,
meta-zephyr is probably the best example.

Cheers,
Marta
Mikko Rapeli Oct. 19, 2023, 9:13 a.m. UTC | #2
Hi,

On Thu, Oct 19, 2023 at 10:19:53AM +0200, Marta Rybczynska wrote:
> On Mon, Oct 16, 2023 at 9:01 AM Mikko Rapeli <mikko.rapeli@linaro.org> wrote:
> >
> > Many recipes embed other SW components. The name and version of the
> > embedded SW component differs from the main recipe. To detect CVEs in the
> > embedded SW component, it needs to be added to CVE_PRODUCT list using
> > name of the SW product in CVE database or with "vendor:product" syntax.
> > Then the version of the embedded SW component can be set using
> > CVE_VERSION_product variable.
> >
> > For example in meta-arm, trusted-firmware-a embeds mbed_tls SW component.
> > Thus trusted-firmware-a can add CVE_PRODUCT for it since CVE database
> > uses product name "mbed_tls":
> >
> > CVE_PRODUCT += "mbed_tls"
> >
> > and set the version of mbed_tls:
> >
> > CVE_VERSION_mbed_tls = "2.28.4"
> >
> > (Real patches for both are a bit more complex due to conditional build
> > enabling mbed_tls support and due to mbed_tls version being set in an
> > .inc file.)
> >
> 
> I like the support for embedded software. In this approach, I'm wondering
> how it would work for packages like curl that have multiple CPEs. Would we
> need  to duplicate the list of CPEs?

The current approach of listing multiple CPEs in CVE_PRODUCT still works.
It's just possible to include a different version for an entry in CVE_PRODUCT
via CVE_VERSION_swcomponent variable. It will fall back to PV.
 
> There are layers/recipes where we have a very long list of embedded components,
> meta-zephyr is probably the best example.

Yes, I think this embedding of SW components is very common. I think some of the
LICENSE data does reflect this but not in all cases.

Cheers,

-Mikko
Jose Quaresma Oct. 19, 2023, 11:54 a.m. UTC | #3
Hi

This change will need some adaptations in the create-spdx.bbclass to handle
this new variable with _PN

Jose

Mikko Rapeli <mikko.rapeli@linaro.org> escreveu no dia quinta, 19/10/2023
à(s) 10:13:

> Hi,
>
> On Thu, Oct 19, 2023 at 10:19:53AM +0200, Marta Rybczynska wrote:
> > On Mon, Oct 16, 2023 at 9:01 AM Mikko Rapeli <mikko.rapeli@linaro.org>
> wrote:
> > >
> > > Many recipes embed other SW components. The name and version of the
> > > embedded SW component differs from the main recipe. To detect CVEs in
> the
> > > embedded SW component, it needs to be added to CVE_PRODUCT list using
> > > name of the SW product in CVE database or with "vendor:product" syntax.
> > > Then the version of the embedded SW component can be set using
> > > CVE_VERSION_product variable.
> > >
> > > For example in meta-arm, trusted-firmware-a embeds mbed_tls SW
> component.
> > > Thus trusted-firmware-a can add CVE_PRODUCT for it since CVE database
> > > uses product name "mbed_tls":
> > >
> > > CVE_PRODUCT += "mbed_tls"
> > >
> > > and set the version of mbed_tls:
> > >
> > > CVE_VERSION_mbed_tls = "2.28.4"
> > >
> > > (Real patches for both are a bit more complex due to conditional build
> > > enabling mbed_tls support and due to mbed_tls version being set in an
> > > .inc file.)
> > >
> >
> > I like the support for embedded software. In this approach, I'm wondering
> > how it would work for packages like curl that have multiple CPEs. Would
> we
> > need  to duplicate the list of CPEs?
>
> The current approach of listing multiple CPEs in CVE_PRODUCT still works.
> It's just possible to include a different version for an entry in
> CVE_PRODUCT
> via CVE_VERSION_swcomponent variable. It will fall back to PV.
>
> > There are layers/recipes where we have a very long list of embedded
> components,
> > meta-zephyr is probably the best example.
>
> Yes, I think this embedding of SW components is very common. I think some
> of the
> LICENSE data does reflect this but not in all cases.
>
> Cheers,
>
> -Mikko
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#189437):
> https://lists.openembedded.org/g/openembedded-core/message/189437
> Mute This Topic: https://lists.openembedded.org/mt/101991269/5052612
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [
> quaresma.jose@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
Mikko Rapeli Oct. 19, 2023, 12:21 p.m. UTC | #4
Hi,

On Thu, Oct 19, 2023 at 12:54:44PM +0100, Jose Quaresma wrote:
> Hi
> 
> This change will need some adaptations in the create-spdx.bbclass to handle
> this new variable with _PN

Good point. How does SPDX tooling handle embedded SW components in recipe sources?

I presume it does not because recipe and license don't handle it either. Should
there be a more generic PN_subpn, PV_subpn, LICENSE_subpn and matching CVE_PRODUCT
and CVE_VERSION? I don't have use cases for these currently. I would like to fix
the CVE reporting issues with embedded SW components though. mbedtls being one good
example.

Or would it be better to convert mbedtls users to use the meta-oe side recipe for it?

Additionally I don't currently read the SDPX output. I don't have use cases for it.
I do check recipes and their metadata like LICENSE though. Feels like the SDPX data
is used as reporting/export data format which is fed to some other tools which are
not open source.

Can of worms...

Cheers,

-Mikko
Mikko Rapeli Oct. 19, 2023, 12:45 p.m. UTC | #5
Hi,

Could something like this work?

--- a/meta/lib/oe/cve_check.py
+++ b/meta/lib/oe/cve_check.py
@@ -140,15 +140,14 @@ def get_patched_cves(d):
     return patched_cves
 
 
-def get_cpe_ids(cve_product, version):
+def get_cpe_ids(cve_product, cve_version):
     """
     Get list of CPE identifiers for the given product and version
     """
 
-    version = version.split("+git")[0]
-
     cpe_ids = []
     for product in cve_product.split():
+        version = (d.getVar("CVE_VERSION_%s" % product) or cve_version).split("+git")[0]
         # CVE_PRODUCT in recipes may include vendor information for CPE identifiers. If not,
         # use wildcard for vendor.
         if ":" in product:

Cheers,

-Mikko
Jose Quaresma Oct. 20, 2023, 7:46 a.m. UTC | #6
Mikko Rapeli <mikko.rapeli@linaro.org> escreveu no dia quinta, 19/10/2023
à(s) 13:21:

> Hi,
>
> On Thu, Oct 19, 2023 at 12:54:44PM +0100, Jose Quaresma wrote:
> > Hi
> >
> > This change will need some adaptations in the create-spdx.bbclass to
> handle
> > this new variable with _PN
>
> Good point. How does SPDX tooling handle embedded SW components in recipe
> sources?
>

As far as I know SPDX doesn't support this at all because the class has a
way of knowing that these components exist.


>
> I presume it does not because recipe and license don't handle it either.
> Should
> there be a more generic PN_subpn, PV_subpn, LICENSE_subpn and matching
> CVE_PRODUCT
> and CVE_VERSION? I don't have use cases for these currently. I would like
> to fix
> the CVE reporting issues with embedded SW components though. mbedtls being
> one good
> example.
>
> Or would it be better to convert mbedtls users to use the meta-oe side
> recipe for it?
>

In a perfect world this would be the way but as we know the world is far
from perfect :)


>
> Additionally I don't currently read the SDPX output. I don't have use
> cases for it.
> I do check recipes and their metadata like LICENSE though. Feels like the
> SDPX data
> is used as reporting/export data format which is fed to some other tools
> which are
> not open source.


> Can of worms...
>

Given that one of the main characteristics of the SPDX metadata is that
they enable and have a complete description
of the software used, this can and will be used by other tools to do all
types of analysis.
AI models will love eating this...


>
> Cheers,
>
> -Mikko
>
Jose Quaresma Oct. 20, 2023, 7:56 a.m. UTC | #7
Mikko Rapeli <mikko.rapeli@linaro.org> escreveu no dia quinta, 19/10/2023
à(s) 13:45:

> Hi,
>
> Could something like this work?
>
> --- a/meta/lib/oe/cve_check.py
> +++ b/meta/lib/oe/cve_check.py
> @@ -140,15 +140,14 @@ def get_patched_cves(d):
>      return patched_cves
>
>
> -def get_cpe_ids(cve_product, version):
> +def get_cpe_ids(cve_product, cve_version):
>      """
>      Get list of CPE identifiers for the given product and version
>      """
>
> -    version = version.split("+git")[0]
> -
>      cpe_ids = []
>      for product in cve_product.split():
> +        version = (d.getVar("CVE_VERSION_%s" % product) or
> cve_version).split("+git")[0]
>

Looks like your patch fixes the remaining issue
but don't know if it will be better to get the CVE_VERSION_ after
splitting  the vendor from the product

Jose


>          # CVE_PRODUCT in recipes may include vendor information for CPE
> identifiers. If not,
>          # use wildcard for vendor.
>          if ":" in product:
>
> Cheers,
>
> -Mikko
>
Mikko Rapeli Oct. 20, 2023, 7:59 a.m. UTC | #8
On Fri, Oct 20, 2023 at 08:56:43AM +0100, Jose Quaresma wrote:
> Mikko Rapeli <mikko.rapeli@linaro.org> escreveu no dia quinta, 19/10/2023
> �(s) 13:45:
> 
> > Hi,
> >
> > Could something like this work?
> >
> > --- a/meta/lib/oe/cve_check.py
> > +++ b/meta/lib/oe/cve_check.py
> > @@ -140,15 +140,14 @@ def get_patched_cves(d):
> >      return patched_cves
> >
> >
> > -def get_cpe_ids(cve_product, version):
> > +def get_cpe_ids(cve_product, cve_version):
> >      """
> >      Get list of CPE identifiers for the given product and version
> >      """
> >
> > -    version = version.split("+git")[0]
> > -
> >      cpe_ids = []
> >      for product in cve_product.split():
> > +        version = (d.getVar("CVE_VERSION_%s" % product) or
> > cve_version).split("+git")[0]
> >
> 
> Looks like your patch fixes the remaining issue
> but don't know if it will be better to get the CVE_VERSION_ after
> splitting  the vendor from the product

This is now in v2. For the CVE_VERSION_%s, it uses what ever product was defined
in CVE_PRODUCT space separated list so it is used before vendor and product split.

Cheers,

-Mikko
diff mbox series

Patch

diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass
index b55f4299da..9c41d54188 100644
--- a/meta/classes/cve-check.bbclass
+++ b/meta/classes/cve-check.bbclass
@@ -309,7 +309,16 @@  def check_cves(d, patched_cves):
     # If this has been unset then we're not scanning for CVEs here (for example, image recipes)
     if not products:
         return ([], [], [], [])
-    pv = d.getVar("CVE_VERSION").split("+git")[0]
+
+    # Version is PV, CVE_VERSION or CVE_VERSION_%s where %s is one of the entries in CVE_PRODUCT.
+    # Enables checking embedded SW component CVEs provided that CVE_PRODUCT contains the embedded SW
+    # component and that version of that component is set via CVE_VERSION_embedded_component variable.
+    pv = {}
+    for product in products:
+        version = (d.getVar("CVE_VERSION_%s" % product) or "").split("+git")[0]
+        if version == "":
+            version = d.getVar("CVE_VERSION").split("+git")[0]
+        pv[product] = version
 
     # If the recipe has been skipped/ignored we return empty lists
     if pn in d.getVar("CVE_CHECK_SKIP_RECIPE").split():
@@ -329,6 +338,7 @@  def check_cves(d, patched_cves):
 
     # For each of the known product names (e.g. curl has CPEs using curl and libcurl)...
     for product in products:
+        full_product = product
         cves_in_product = False
         if ":" in product:
             vendor, product = product.split(":", 1)
@@ -341,7 +351,7 @@  def check_cves(d, patched_cves):
             cve = cverow[0]
 
             if cve in cve_ignore:
-                bb.note("%s-%s ignores %s" % (product, pv, cve))
+                bb.note("%s-%s ignores %s" % (product, pv[full_product], cve))
                 cves_ignored.append(cve)
                 continue
             elif cve in patched_cves:
@@ -366,27 +376,27 @@  def check_cves(d, patched_cves):
                 version_start = convert_cve_version(version_start)
                 version_end = convert_cve_version(version_end)
 
-                if (operator_start == '=' and pv == version_start) or version_start == '-':
+                if (operator_start == '=' and pv[full_product] == version_start) or version_start == '-':
                     vulnerable = True
                 else:
                     if operator_start:
                         try:
-                            vulnerable_start =  (operator_start == '>=' and Version(pv,suffix) >= Version(version_start,suffix))
-                            vulnerable_start |= (operator_start == '>' and Version(pv,suffix) > Version(version_start,suffix))
+                            vulnerable_start =  (operator_start == '>=' and Version(pv[full_product],suffix) >= Version(version_start,suffix))
+                            vulnerable_start |= (operator_start == '>' and Version(pv[full_product],suffix) > Version(version_start,suffix))
                         except:
                             bb.warn("%s: Failed to compare %s %s %s for %s" %
-                                    (product, pv, operator_start, version_start, cve))
+                                    (product, pv[full_product], operator_start, version_start, cve))
                             vulnerable_start = False
                     else:
                         vulnerable_start = False
 
                     if operator_end:
                         try:
-                            vulnerable_end  = (operator_end == '<=' and Version(pv,suffix) <= Version(version_end,suffix) )
-                            vulnerable_end |= (operator_end == '<' and Version(pv,suffix) < Version(version_end,suffix) )
+                            vulnerable_end  = (operator_end == '<=' and Version(pv[full_product],suffix) <= Version(version_end,suffix) )
+                            vulnerable_end |= (operator_end == '<' and Version(pv[full_product],suffix) < Version(version_end,suffix) )
                         except:
                             bb.warn("%s: Failed to compare %s %s %s for %s" %
-                                    (product, pv, operator_end, version_end, cve))
+                                    (product, pv[full_product], operator_end, version_end, cve))
                             vulnerable_end = False
                     else:
                         vulnerable_end = False