diff mbox series

[v5,2/4] generate-cve-exclusions: Add a .bbclass

Message ID 20260116190520.118714-4-valentin.boudevin@gmail.com
State New
Headers show
Series [1/1] improve_kerne_cve_report: Add a bbclass support | expand

Commit Message

ValentinBoudevin Jan. 16, 2026, 7:05 p.m. UTC
Add a new class named kernel-generate-cve-exclusions.bbclass to
generate-cve-exclusions to use this script at every run.

Two steps for testing:
1) Inherit this class in the kernel recipe with "inherit
   kernel-generate-cve-exclusions.bbclass"
2) Turn the variable ENABLE_KERNEL_CVE_EXCLUSIONS to "1".
3) Use the following command to generate a cvelistV5 entry with a JSON
   file in in ${WORKDIR}/cvelistV5/ :
   "bitbake linux-yocto -c generate-cve-exclusions"

The JSON file can then be parsed in the following run by cve-check.

This class contains several methods:

*do_clone_cvelistV5: Clone the cvelistV5 repo in
${WORKDIR}/cvelistV5/git

(e.g. bitbake-builds/poky-master/build/tmp/work/qemux86_64-poky-linux/
linux-yocto/6.18.1+git/cvelistV5/git)

*do_generate_cve_exclusions: Use the script generate-cve-exclusions.py.
It uses the new "--output-json" argument to generate a JSON file as an
output stored in ${WORKDIR}/cvelistV5//cve-exclusion_${LINUX_VERSION}.json

*do_cve_check:prepend: Parse the previously generated JSON file to set
the variable CVE_STATUS corretly

The class also provides some variables:
*ENABLE_KERNEL_CVE_EXCLUSIONS: Enable/Disable this class (off by default
to not affect linux-yocto OE example)
*GENERATE_CVE_EXCLUSIONS_SRC_URI and GENERATE_CVE_EXCLUSIONS_SRCREV can
be used to change the source repository or fix a commit with SRCREV
(usefull for deterministic testing)
*GENERATE_CVE_EXCLUSIONS_NETWORK can be set to 0 to provide an offline
mode based on DL_DIR directory.
*GENERATE_CVE_EXCLUSIONS_WORKDIR path used as a working directory for
this class
*GENERATE_CVE_EXCLUSIONS_DESTSUFFIX suffix used for the git unpack
*GENERATE_CVE_EXCLUSIONS_UNPACK_DIR path of the unpack for the git
repository

Signed-off-by: Valentin Boudevin <valentin.boudevin@gmail.com>
---
 .../kernel-generate-cve-exclusions.bbclass    | 135 ++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 meta/classes/kernel-generate-cve-exclusions.bbclass

Comments

Peter Kjellerstedt Jan. 17, 2026, 1:36 p.m. UTC | #1
> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> On Behalf Of vboudevin via lists.openembedded.org
> Sent: den 16 januari 2026 20:05
> To: openembedded-core@lists.openembedded.org
> Cc: daniel.turull@ericsson.com; jerome.oufella@savoirfairelinux.com; antonin.godard@bootlin.com; ValentinBoudevin <valentin.boudevin@gmail.com>
> Subject: [OE-core] [PATCH v5 2/4] generate-cve-exclusions: Add a .bbclass

The prefix should match the class name.

> 
> Add a new class named kernel-generate-cve-exclusions.bbclass to
> generate-cve-exclusions to use this script at every run.
> 
> Two steps for testing:
> 1) Inherit this class in the kernel recipe with "inherit
>    kernel-generate-cve-exclusions.bbclass"
> 2) Turn the variable ENABLE_KERNEL_CVE_EXCLUSIONS to "1".
> 3) Use the following command to generate a cvelistV5 entry with a JSON
>    file in in ${WORKDIR}/cvelistV5/ :
>    "bitbake linux-yocto -c generate-cve-exclusions"
> 
> The JSON file can then be parsed in the following run by cve-check.
> 
> This class contains several methods:
> 
> *do_clone_cvelistV5: Clone the cvelistV5 repo in
> ${WORKDIR}/cvelistV5/git
> 
> (e.g. bitbake-builds/poky-master/build/tmp/work/qemux86_64-poky-linux/
> linux-yocto/6.18.1+git/cvelistV5/git)
> 
> *do_generate_cve_exclusions: Use the script generate-cve-exclusions.py.
> It uses the new "--output-json" argument to generate a JSON file as an
> output stored in ${WORKDIR}/cvelistV5//cve-exclusion_${LINUX_VERSION}.json
> 
> *do_cve_check:prepend: Parse the previously generated JSON file to set
> the variable CVE_STATUS corretly
> 
> The class also provides some variables:
> *ENABLE_KERNEL_CVE_EXCLUSIONS: Enable/Disable this class (off by default
> to not affect linux-yocto OE example)
> *GENERATE_CVE_EXCLUSIONS_SRC_URI and GENERATE_CVE_EXCLUSIONS_SRCREV can
> be used to change the source repository or fix a commit with SRCREV
> (usefull for deterministic testing)
> *GENERATE_CVE_EXCLUSIONS_NETWORK can be set to 0 to provide an offline
> mode based on DL_DIR directory.
> *GENERATE_CVE_EXCLUSIONS_WORKDIR path used as a working directory for
> this class
> *GENERATE_CVE_EXCLUSIONS_DESTSUFFIX suffix used for the git unpack
> *GENERATE_CVE_EXCLUSIONS_UNPACK_DIR path of the unpack for the git
> repository
> 
> Signed-off-by: Valentin Boudevin <valentin.boudevin@gmail.com>
> ---
>  .../kernel-generate-cve-exclusions.bbclass    | 135 ++++++++++++++++++
>  1 file changed, 135 insertions(+)
>  create mode 100644 meta/classes/kernel-generate-cve-exclusions.bbclass
> 
> diff --git a/meta/classes/kernel-generate-cve-exclusions.bbclass b/meta/classes/kernel-generate-cve-exclusions.bbclass
> new file mode 100644
> index 0000000000..cd81cc5899
> --- /dev/null
> +++ b/meta/classes/kernel-generate-cve-exclusions.bbclass
> @@ -0,0 +1,135 @@
> +# Generate CVE exclusions for the kernel build (set to "1" to enable)
> +ENABLE_KERNEL_CVE_EXCLUSIONS ?= "0"
> +
> +# CVE exclusions source repository settings
> +GENERATE_CVE_EXCLUSIONS_SRC_URI ?= "git://github.com/CVEProject/cvelistV5.git;branch=main;protocol=https"
> +GENERATE_CVE_EXCLUSIONS_SRCREV ?= "${@bb.fetch2.get_autorev(d)}"
> +GENERATE_CVE_EXCLUSIONS_NETWORK ?= "1"
> +GENERATE_CVE_EXCLUSIONS_WORKDIR ?= "${WORKDIR}/cvelistV5"
> +GENERATE_CVE_EXCLUSIONS_DESTSUFFIX ?= "git"
> +GENERATE_CVE_EXCLUSIONS_UNPACK_DIR ?= "${GENERATE_CVE_EXCLUSIONS_WORKDIR}/${GENERATE_CVE_EXCLUSIONS_DESTSUFFIX}"
> +
> +python __anonymous() {
> +    # Only run if CVE exclusions are enabled
> +    if d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS", True) == "1":
> +        srcrev = d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV", True) or ""
> +        network = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK", True) or "0"
> +        # Check offline mode with AUTOREV-like SRCREV
> +        if network == "0" and srcrev.strip() in ("${AUTOREV}", "AUTOINC", "INVALID"):
> +            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to AUTOREV/AUTOINC/INVALID. "
> +                     "Cannot proceed without network access or use a fixed SRCREV.")
> +        d.appendVar("SRC_URI", " ${GENERATE_CVE_EXCLUSIONS_SRC_URI};name=generate-cve-exclusions;destsuffix=${GENERATE_CVE_EXCLUSIONS_DESTSUFFIX}")
> +        d.setVar("SRCREV_generate-cve-exclusions", d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV"))
> +}
> +
> +python do_clone_cvelistV5() {
> +    import subprocess
> +    import shutil, os
> +    # Only run if CVE exclusions are enabled
> +    if not d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS") == "1":
> +        return
> +    network_allowed = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK") == "1"
> +    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
> +    unpack_dir = d.getVar("GENERATE_CVE_EXCLUSIONS_UNPACK_DIR")
> +    # Remove existing unpacked directory if any
> +    if os.path.exists(workdir):
> +        shutil.rmtree(workdir)
> +    # Prepare fetcher
> +    src_uri_list = (d.getVar('SRC_URI') or "").split()
> +    cve_uris = []
> +    for uri in src_uri_list:
> +        if "name=generate-cve-exclusions" in uri:
> +            cve_uris.append(uri)
> +    if not cve_uris:
> +        bb.note("No CVE exclusions SRC_URI found, skipping fetch")
> +        return
> +    fetcher = bb.fetch2.Fetch(cve_uris, d)
> +    # Clone only if network is allowed
> +    if network_allowed:
> +        fetcher.download()
> +    else:
> +        # Offline mode without network access
> +        bb.note("GENERATE_CVE_EXCLUSIONS_NETWORK=0: Skipping online fetch. Checking local downloads in DL_DIR...")
> +        have_sources = False
> +        dl_dir = d.getVar("DL_DIR")
> +        srcrev = d.getVar("SRCREV_generate-cve-exclusions")
> +        bb.note(f"Checking for sources for SRCREV: {srcrev}")
> +        # Check SRCREV is NOT set to AUTOREV
> +        if srcrev.strip() in ("${AUTOREV}", "AUTOINC", "INVALID"):
> +            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to AUTOREV/AUTOINC/INVALID. Cannot proceed without network access or use a fixed SRCREV.")
> +            return
> +        # Loop through the fetcher's expanded URL data
> +        for ud in fetcher.expanded_urldata():
> +            ud.setup_localpath(d)
> +            # Check mirror tarballs first
> +            for mirror_fname in ud.mirrortarballs:
> +                mirror_path = os.path.join(dl_dir, mirror_fname)
> +                if os.path.exists(mirror_path):
> +                    bb.note(f"Found mirror tarball: {mirror_path}")
> +                    have_sources = True
> +                    break
> +            # If no mirror, check original download path
> +            if not have_sources and ud.localpath and os.path.exists(ud.localpath):
> +                bb.note(f"Found local download: {ud.localpath}")
> +                have_sources = True
> +            if not have_sources:
> +                bb.fatal("generate-cve-exclusions: Offline mode but required source is missing.\n"f"SRC_URI = {ud.url}")
> +                return
> +    # Unpack into the standard work directory
> +    fetcher.unpack(unpack_dir)
> +    # Remove the folder ${PN} set by unpack
> +    subdirs = [d for d in os.listdir(unpack_dir) if os.path.isdir(os.path.join(unpack_dir, d))]
> +    if len(subdirs) == 1:
> +        srcdir = os.path.join(unpack_dir, subdirs[0])
> +        for f in os.listdir(srcdir):
> +            shutil.move(os.path.join(srcdir, f), unpack_dir)
> +        shutil.rmtree(srcdir)
> +    bb.note("Vulnerabilities repo unpacked into: %s" % unpack_dir)
> +}
> +do_clone_cvelistV5[network] = "${GENERATE_CVE_EXCLUSIONS_NETWORK}"
> +do_clone_cvelistV5[nostamp] = "1"
> +do_clone_cvelistV5[doc] = "Clone CVE information from the CVE Project: https://github.com/CVEProject/cvelistV5.git"
> +addtask clone_cvelistV5 before do_generate_cve_exclusions
> +
> +do_generate_cve_exclusions() {
> +    # Only run if CVE exclusions are enabled
> +    if [ "${ENABLE_KERNEL_CVE_EXCLUSIONS}" != "1" ]; then
> +        return 0
> +    fi
> +    generate_cve_exclusions_script=${COREBASE}/scripts/contrib/generate-cve-exclusions.py
> +    if [ ! -f "${generate_cve_exclusions_script}" ]; then
> +        bbwarn "generate-cve-exclusions.py not found in ${COREBASE}."
> +        return 0
> +    fi
> +    if [ ! -d "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" ]; then
> +        bbwarn "CVE exclusions source directory not found in ${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}."
> +        return 0
> +    fi
> +    python3 "${generate_cve_exclusions_script}" \
> +        "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" \
> +        ${LINUX_VERSION} \
> +        --output-json > ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-exclusion_${LINUX_VERSION}.json
> +    bbplain "CVE exclusions generated for kernel version ${LINUX_VERSION} at ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-exclusion_${LINUX_VERSION}.json."
> +}
> +do_generate_cve_exclusions[nostamp] = "1"
> +do_generate_cve_exclusions[doc] = "Generate CVE exclusions for the kernel build. (e.g., cve-exclusion_6.12.inc)"
> +addtask generate_cve_exclusions after do_clone_cvelistV5 before do_cve_check
> +
> +python do_cve_check:prepend() {
> +    import os
> +    import json
> +    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
> +    kernel_version = d.getVar("LINUX_VERSION")
> +    json_input_file = os.path.join(workdir, "cve-exclusion_%s.json" % kernel_version)
> +    if os.path.exists(json_input_file):
> +        with open(json_input_file, 'r', encoding='utf-8') as f:
> +            cve_data = json.load(f)
> +        cve_status_dict = cve_data.get("cve_status", {})
> +        count = 0
> +        for cve_id, info in cve_status_dict.items():
> +            if info.get("active", True):
> +                continue
> +            d.setVarFlag("CVE_STATUS", cve_id, info.get("message", ""))
> +            count += 1
> +        bb.note("Loaded %d CVE_STATUS entries from JSON output for kernel %s" % (count, kernel_version))
> +}
> \ No newline at end of file

Please add a newline at the end.

//Peter
Daniel Turull Jan. 19, 2026, 10:40 a.m. UTC | #2
> -----Original Message-----
> From: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
> Sent: Saturday, 17 January 2026 14:36
> To: valentin.boudevin@gmail.com; openembedded-
> core@lists.openembedded.org
> Cc: Daniel Turull <daniel.turull@ericsson.com>;
> jerome.oufella@savoirfairelinux.com; antonin.godard@bootlin.com
> Subject: RE: [OE-core] [PATCH v5 2/4] generate-cve-exclusions: Add a .bbclass
> 
> [You don't often get email from peter.kjellerstedt@axis.com. Learn why this is
> important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> > -----Original Message-----
> > From: openembedded-core@lists.openembedded.org
> > <openembedded-core@lists.openembedded.org> On Behalf Of vboudevin
> via
> > lists.openembedded.org
> > Sent: den 16 januari 2026 20:05
> > To: openembedded-core@lists.openembedded.org
> > Cc: daniel.turull@ericsson.com; jerome.oufella@savoirfairelinux.com;
> > antonin.godard@bootlin.com; ValentinBoudevin
> > <valentin.boudevin@gmail.com>
> > Subject: [OE-core] [PATCH v5 2/4] generate-cve-exclusions: Add a
> > .bbclass
> 
> The prefix should match the class name.
> 
> >
> > Add a new class named kernel-generate-cve-exclusions.bbclass to
> > generate-cve-exclusions to use this script at every run.
> >
> > Two steps for testing:
> > 1) Inherit this class in the kernel recipe with "inherit
> >    kernel-generate-cve-exclusions.bbclass"
> > 2) Turn the variable ENABLE_KERNEL_CVE_EXCLUSIONS to "1".
> > 3) Use the following command to generate a cvelistV5 entry with a JSON
> >    file in in ${WORKDIR}/cvelistV5/ :
> >    "bitbake linux-yocto -c generate-cve-exclusions"
> >
The task is using _ instead of -

 bitbake linux-yocto -c do_generate_cve_exclusions 

> > The JSON file can then be parsed in the following run by cve-check.
> >
> > This class contains several methods:
> >
> > *do_clone_cvelistV5: Clone the cvelistV5 repo in
> > ${WORKDIR}/cvelistV5/git
> >
> > (e.g. bitbake-builds/poky-master/build/tmp/work/qemux86_64-poky-linux/
> > linux-yocto/6.18.1+git/cvelistV5/git)
> >
> > *do_generate_cve_exclusions: Use the script generate-cve-exclusions.py.
> > It uses the new "--output-json" argument to generate a JSON file as an
> > output stored in
> > ${WORKDIR}/cvelistV5//cve-exclusion_${LINUX_VERSION}.json
> >
> > *do_cve_check:prepend: Parse the previously generated JSON file to set
> > the variable CVE_STATUS corretly
> >
> > The class also provides some variables:
> > *ENABLE_KERNEL_CVE_EXCLUSIONS: Enable/Disable this class (off by
> > default to not affect linux-yocto OE example)
> > *GENERATE_CVE_EXCLUSIONS_SRC_URI and
> GENERATE_CVE_EXCLUSIONS_SRCREV
> > can be used to change the source repository or fix a commit with
> > SRCREV (usefull for deterministic testing)
> > *GENERATE_CVE_EXCLUSIONS_NETWORK can be set to 0 to provide an
> offline
> > mode based on DL_DIR directory.
> > *GENERATE_CVE_EXCLUSIONS_WORKDIR path used as a working directory
> for
> > this class *GENERATE_CVE_EXCLUSIONS_DESTSUFFIX suffix used for the git
> > unpack *GENERATE_CVE_EXCLUSIONS_UNPACK_DIR path of the unpack for
> the
> > git repository
> >
> > Signed-off-by: Valentin Boudevin <valentin.boudevin@gmail.com>
> > ---
> >  .../kernel-generate-cve-exclusions.bbclass    | 135 ++++++++++++++++++
> >  1 file changed, 135 insertions(+)
> >  create mode 100644
> > meta/classes/kernel-generate-cve-exclusions.bbclass
> >
> > diff --git a/meta/classes/kernel-generate-cve-exclusions.bbclass
> > b/meta/classes/kernel-generate-cve-exclusions.bbclass
> > new file mode 100644
> > index 0000000000..cd81cc5899
> > --- /dev/null
> > +++ b/meta/classes/kernel-generate-cve-exclusions.bbclass
> > @@ -0,0 +1,135 @@
> > +# Generate CVE exclusions for the kernel build (set to "1" to enable)
> > +ENABLE_KERNEL_CVE_EXCLUSIONS ?= "0"
> > +
> > +# CVE exclusions source repository settings
> > +GENERATE_CVE_EXCLUSIONS_SRC_URI ?=
> "git://github.com/CVEProject/cvelistV5.git;branch=main;protocol=https"
> > +GENERATE_CVE_EXCLUSIONS_SRCREV ?= "${@bb.fetch2.get_autorev(d)}"
> > +GENERATE_CVE_EXCLUSIONS_NETWORK ?= "1"
> > +GENERATE_CVE_EXCLUSIONS_WORKDIR ?= "${WORKDIR}/cvelistV5"
> > +GENERATE_CVE_EXCLUSIONS_DESTSUFFIX ?= "git"
> > +GENERATE_CVE_EXCLUSIONS_UNPACK_DIR ?=
> "${GENERATE_CVE_EXCLUSIONS_WORKDIR}/${GENERATE_CVE_EXCLUSIONS_
> DESTSUFFIX}"
> > +
> > +python __anonymous() {
> > +    # Only run if CVE exclusions are enabled
> > +    if d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS", True) == "1":
> > +        srcrev = d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV", True) or ""
> > +        network = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK", True)
> or "0"
> > +        # Check offline mode with AUTOREV-like SRCREV
> > +        if network == "0" and srcrev.strip() in ("${AUTOREV}", "AUTOINC",
> "INVALID"):
> > +            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to
> AUTOREV/AUTOINC/INVALID. "
> > +                     "Cannot proceed without network access or use a fixed
> SRCREV.")
> > +        d.appendVar("SRC_URI", "
> ${GENERATE_CVE_EXCLUSIONS_SRC_URI};name=generate-cve-
> exclusions;destsuffix=${GENERATE_CVE_EXCLUSIONS_DESTSUFFIX}")
> > +        d.setVar("SRCREV_generate-cve-exclusions",
> > +d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV"))
> > +}
> > +
> > +python do_clone_cvelistV5() {
> > +    import subprocess
> > +    import shutil, os
> > +    # Only run if CVE exclusions are enabled
> > +    if not d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS") == "1":
> > +        return
> > +    network_allowed = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK")
> == "1"
> > +    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
> > +    unpack_dir = d.getVar("GENERATE_CVE_EXCLUSIONS_UNPACK_DIR")
> > +    # Remove existing unpacked directory if any
> > +    if os.path.exists(workdir):
> > +        shutil.rmtree(workdir)
> > +    # Prepare fetcher
> > +    src_uri_list = (d.getVar('SRC_URI') or "").split()
> > +    cve_uris = []
> > +    for uri in src_uri_list:
> > +        if "name=generate-cve-exclusions" in uri:
> > +            cve_uris.append(uri)
> > +    if not cve_uris:
> > +        bb.note("No CVE exclusions SRC_URI found, skipping fetch")
> > +        return
> > +    fetcher = bb.fetch2.Fetch(cve_uris, d)
> > +    # Clone only if network is allowed
> > +    if network_allowed:
> > +        fetcher.download()
> > +    else:
> > +        # Offline mode without network access
> > +        bb.note("GENERATE_CVE_EXCLUSIONS_NETWORK=0: Skipping online
> fetch. Checking local downloads in DL_DIR...")
> > +        have_sources = False
> > +        dl_dir = d.getVar("DL_DIR")
> > +        srcrev = d.getVar("SRCREV_generate-cve-exclusions")
> > +        bb.note(f"Checking for sources for SRCREV: {srcrev}")
> > +        # Check SRCREV is NOT set to AUTOREV
> > +        if srcrev.strip() in ("${AUTOREV}", "AUTOINC", "INVALID"):
> > +            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to
> AUTOREV/AUTOINC/INVALID. Cannot proceed without network access or use
> a fixed SRCREV.")
> > +            return
> > +        # Loop through the fetcher's expanded URL data
> > +        for ud in fetcher.expanded_urldata():
> > +            ud.setup_localpath(d)
> > +            # Check mirror tarballs first
> > +            for mirror_fname in ud.mirrortarballs:
> > +                mirror_path = os.path.join(dl_dir, mirror_fname)
> > +                if os.path.exists(mirror_path):
> > +                    bb.note(f"Found mirror tarball: {mirror_path}")
> > +                    have_sources = True
> > +                    break
> > +            # If no mirror, check original download path
> > +            if not have_sources and ud.localpath and
> os.path.exists(ud.localpath):
> > +                bb.note(f"Found local download: {ud.localpath}")
> > +                have_sources = True
> > +            if not have_sources:
> > +                bb.fatal("generate-cve-exclusions: Offline mode but required
> source is missing.\n"f"SRC_URI = {ud.url}")
> > +                return
> > +    # Unpack into the standard work directory
> > +    fetcher.unpack(unpack_dir)
> > +    # Remove the folder ${PN} set by unpack
> > +    subdirs = [d for d in os.listdir(unpack_dir) if
> os.path.isdir(os.path.join(unpack_dir, d))]
> > +    if len(subdirs) == 1:
> > +        srcdir = os.path.join(unpack_dir, subdirs[0])
> > +        for f in os.listdir(srcdir):
> > +            shutil.move(os.path.join(srcdir, f), unpack_dir)
> > +        shutil.rmtree(srcdir)
> > +    bb.note("Vulnerabilities repo unpacked into: %s" % unpack_dir) }
> > +do_clone_cvelistV5[network] =
> "${GENERATE_CVE_EXCLUSIONS_NETWORK}"
> > +do_clone_cvelistV5[nostamp] = "1"
> > +do_clone_cvelistV5[doc] = "Clone CVE information from the CVE Project:
> https://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.
> com%2FCVEProject%2FcvelistV5.git&data=05%7C02%7Cdaniel.turull%40ericss
> on.com%7Cfa5589510fa742416d8a08de55cd6520%7C92e84cebfbfd47abbe52
> 080c6b87953f%7C0%7C0%7C639042537815406521%7CUnknown%7CTWFpb
> GZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zM
> iIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=xnXk6OEkQ
> 28sBu5RzuX3xtWT%2B1hfEEKaSMeoDhreo30%3D&reserved=0"
> > +addtask clone_cvelistV5 before do_generate_cve_exclusions
> > +
> > +do_generate_cve_exclusions() {
> > +    # Only run if CVE exclusions are enabled
> > +    if [ "${ENABLE_KERNEL_CVE_EXCLUSIONS}" != "1" ]; then
> > +        return 0
> > +    fi
> > +    generate_cve_exclusions_script=${COREBASE}/scripts/contrib/generate-
> cve-exclusions.py
> > +    if [ ! -f "${generate_cve_exclusions_script}" ]; then
> > +        bbwarn "generate-cve-exclusions.py not found in ${COREBASE}."
> > +        return 0
> > +    fi
> > +    if [ ! -d "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" ]; then
> > +        bbwarn "CVE exclusions source directory not found in
> ${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}."
> > +        return 0
> > +    fi
> > +    python3 "${generate_cve_exclusions_script}" \
> > +        "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" \
> > +        ${LINUX_VERSION} \
> > +        --output-json > ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-
> exclusion_${LINUX_VERSION}.json
> > +    bbplain "CVE exclusions generated for kernel version ${LINUX_VERSION}
> at ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-
> exclusion_${LINUX_VERSION}.json."
> > +}
> > +do_generate_cve_exclusions[nostamp] = "1"
> > +do_generate_cve_exclusions[doc] = "Generate CVE exclusions for the
> kernel build. (e.g., cve-exclusion_6.12.inc)"
> > +addtask generate_cve_exclusions after do_clone_cvelistV5 before
> > +do_cve_check
> > +
> > +python do_cve_check:prepend() {
> > +    import os
> > +    import json
> > +    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
> > +    kernel_version = d.getVar("LINUX_VERSION")
> > +    json_input_file = os.path.join(workdir, "cve-exclusion_%s.json" %
> kernel_version)
> > +    if os.path.exists(json_input_file):
> > +        with open(json_input_file, 'r', encoding='utf-8') as f:
> > +            cve_data = json.load(f)
> > +        cve_status_dict = cve_data.get("cve_status", {})
> > +        count = 0
> > +        for cve_id, info in cve_status_dict.items():
> > +            if info.get("active", True):
> > +                continue
> > +            d.setVarFlag("CVE_STATUS", cve_id, info.get("message", ""))
> > +            count += 1
> > +        bb.note("Loaded %d CVE_STATUS entries from JSON output for
> > +kernel %s" % (count, kernel_version)) }
> > \ No newline at end of file
> 
> Please add a newline at the end.
> 
> //Peter
diff mbox series

Patch

diff --git a/meta/classes/kernel-generate-cve-exclusions.bbclass b/meta/classes/kernel-generate-cve-exclusions.bbclass
new file mode 100644
index 0000000000..cd81cc5899
--- /dev/null
+++ b/meta/classes/kernel-generate-cve-exclusions.bbclass
@@ -0,0 +1,135 @@ 
+# Generate CVE exclusions for the kernel build (set to "1" to enable)
+ENABLE_KERNEL_CVE_EXCLUSIONS ?= "0"
+
+# CVE exclusions source repository settings
+GENERATE_CVE_EXCLUSIONS_SRC_URI ?= "git://github.com/CVEProject/cvelistV5.git;branch=main;protocol=https"
+GENERATE_CVE_EXCLUSIONS_SRCREV ?= "${@bb.fetch2.get_autorev(d)}"
+GENERATE_CVE_EXCLUSIONS_NETWORK ?= "1"
+GENERATE_CVE_EXCLUSIONS_WORKDIR ?= "${WORKDIR}/cvelistV5"
+GENERATE_CVE_EXCLUSIONS_DESTSUFFIX ?= "git"
+GENERATE_CVE_EXCLUSIONS_UNPACK_DIR ?= "${GENERATE_CVE_EXCLUSIONS_WORKDIR}/${GENERATE_CVE_EXCLUSIONS_DESTSUFFIX}"
+
+python __anonymous() {
+    # Only run if CVE exclusions are enabled
+    if d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS", True) == "1":
+        srcrev = d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV", True) or ""
+        network = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK", True) or "0"
+        # Check offline mode with AUTOREV-like SRCREV
+        if network == "0" and srcrev.strip() in ("${AUTOREV}", "AUTOINC", "INVALID"):
+            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to AUTOREV/AUTOINC/INVALID. "
+                     "Cannot proceed without network access or use a fixed SRCREV.")
+        d.appendVar("SRC_URI", " ${GENERATE_CVE_EXCLUSIONS_SRC_URI};name=generate-cve-exclusions;destsuffix=${GENERATE_CVE_EXCLUSIONS_DESTSUFFIX}")
+        d.setVar("SRCREV_generate-cve-exclusions", d.getVar("GENERATE_CVE_EXCLUSIONS_SRCREV"))
+}
+
+python do_clone_cvelistV5() {
+    import subprocess
+    import shutil, os
+    # Only run if CVE exclusions are enabled
+    if not d.getVar("ENABLE_KERNEL_CVE_EXCLUSIONS") == "1":
+        return
+    network_allowed = d.getVar("GENERATE_CVE_EXCLUSIONS_NETWORK") == "1"
+    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
+    unpack_dir = d.getVar("GENERATE_CVE_EXCLUSIONS_UNPACK_DIR")
+    # Remove existing unpacked directory if any
+    if os.path.exists(workdir):
+        shutil.rmtree(workdir)
+    # Prepare fetcher
+    src_uri_list = (d.getVar('SRC_URI') or "").split()
+    cve_uris = []
+    for uri in src_uri_list:
+        if "name=generate-cve-exclusions" in uri:
+            cve_uris.append(uri)
+    if not cve_uris:
+        bb.note("No CVE exclusions SRC_URI found, skipping fetch")
+        return
+    fetcher = bb.fetch2.Fetch(cve_uris, d)
+    # Clone only if network is allowed
+    if network_allowed:
+        fetcher.download()
+    else:
+        # Offline mode without network access
+        bb.note("GENERATE_CVE_EXCLUSIONS_NETWORK=0: Skipping online fetch. Checking local downloads in DL_DIR...")
+        have_sources = False
+        dl_dir = d.getVar("DL_DIR")
+        srcrev = d.getVar("SRCREV_generate-cve-exclusions")
+        bb.note(f"Checking for sources for SRCREV: {srcrev}")
+        # Check SRCREV is NOT set to AUTOREV
+        if srcrev.strip() in ("${AUTOREV}", "AUTOINC", "INVALID"):
+            bb.fatal("generate-cve-exclusions: Offline mode but SRCREV is set to AUTOREV/AUTOINC/INVALID. Cannot proceed without network access or use a fixed SRCREV.")
+            return
+        # Loop through the fetcher's expanded URL data
+        for ud in fetcher.expanded_urldata():
+            ud.setup_localpath(d)
+            # Check mirror tarballs first
+            for mirror_fname in ud.mirrortarballs:
+                mirror_path = os.path.join(dl_dir, mirror_fname)
+                if os.path.exists(mirror_path):
+                    bb.note(f"Found mirror tarball: {mirror_path}")
+                    have_sources = True
+                    break
+            # If no mirror, check original download path
+            if not have_sources and ud.localpath and os.path.exists(ud.localpath):
+                bb.note(f"Found local download: {ud.localpath}")
+                have_sources = True
+            if not have_sources:
+                bb.fatal("generate-cve-exclusions: Offline mode but required source is missing.\n"f"SRC_URI = {ud.url}")
+                return
+    # Unpack into the standard work directory
+    fetcher.unpack(unpack_dir)
+    # Remove the folder ${PN} set by unpack
+    subdirs = [d for d in os.listdir(unpack_dir) if os.path.isdir(os.path.join(unpack_dir, d))]
+    if len(subdirs) == 1:
+        srcdir = os.path.join(unpack_dir, subdirs[0])
+        for f in os.listdir(srcdir):
+            shutil.move(os.path.join(srcdir, f), unpack_dir)
+        shutil.rmtree(srcdir)
+    bb.note("Vulnerabilities repo unpacked into: %s" % unpack_dir)
+}
+do_clone_cvelistV5[network] = "${GENERATE_CVE_EXCLUSIONS_NETWORK}"
+do_clone_cvelistV5[nostamp] = "1"
+do_clone_cvelistV5[doc] = "Clone CVE information from the CVE Project: https://github.com/CVEProject/cvelistV5.git"
+addtask clone_cvelistV5 before do_generate_cve_exclusions
+
+do_generate_cve_exclusions() {
+    # Only run if CVE exclusions are enabled
+    if [ "${ENABLE_KERNEL_CVE_EXCLUSIONS}" != "1" ]; then
+        return 0
+    fi
+    generate_cve_exclusions_script=${COREBASE}/scripts/contrib/generate-cve-exclusions.py
+    if [ ! -f "${generate_cve_exclusions_script}" ]; then
+        bbwarn "generate-cve-exclusions.py not found in ${COREBASE}."
+        return 0
+    fi
+    if [ ! -d "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" ]; then
+        bbwarn "CVE exclusions source directory not found in ${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}."
+        return 0
+    fi
+    python3 "${generate_cve_exclusions_script}" \
+        "${GENERATE_CVE_EXCLUSIONS_UNPACK_DIR}" \
+        ${LINUX_VERSION} \
+        --output-json > ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-exclusion_${LINUX_VERSION}.json
+    bbplain "CVE exclusions generated for kernel version ${LINUX_VERSION} at ${GENERATE_CVE_EXCLUSIONS_WORKDIR}/cve-exclusion_${LINUX_VERSION}.json."
+}
+do_generate_cve_exclusions[nostamp] = "1"
+do_generate_cve_exclusions[doc] = "Generate CVE exclusions for the kernel build. (e.g., cve-exclusion_6.12.inc)"
+addtask generate_cve_exclusions after do_clone_cvelistV5 before do_cve_check
+
+python do_cve_check:prepend() {
+    import os
+    import json
+    workdir = d.getVar("GENERATE_CVE_EXCLUSIONS_WORKDIR")
+    kernel_version = d.getVar("LINUX_VERSION")
+    json_input_file = os.path.join(workdir, "cve-exclusion_%s.json" % kernel_version)
+    if os.path.exists(json_input_file):
+        with open(json_input_file, 'r', encoding='utf-8') as f:
+            cve_data = json.load(f)
+        cve_status_dict = cve_data.get("cve_status", {})
+        count = 0
+        for cve_id, info in cve_status_dict.items():
+            if info.get("active", True):
+                continue
+            d.setVarFlag("CVE_STATUS", cve_id, info.get("message", ""))
+            count += 1
+        bb.note("Loaded %d CVE_STATUS entries from JSON output for kernel %s" % (count, kernel_version))
+}
\ No newline at end of file