* [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 12:08 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 2/8] utils/generate-cyclonedx: move license download in a function Thomas Perale via buildroot
` (6 subsequent siblings)
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
Allows to compare different (major, minor) version easily.
In next commit, an optional newer version of CycloneDX will be
introduced and this syntax makes it easy to compare without introducing
a dependency to 'packaging.version'.
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index 2b6c6d63d3..eff55bf598 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -20,13 +20,13 @@ import sys
import re
-CYCLONEDX_VERSION = "1.6"
-SPDX_SCHEMA_URL = f"https://raw.githubusercontent.com/CycloneDX/specification/{CYCLONEDX_VERSION}/schema/spdx.schema.json"
+CYCLONEDX_VERSION = (1, 6)
+SPDX_SCHEMA_URL = "https://raw.githubusercontent.com/CycloneDX/specification/{}.{}/schema/spdx.schema.json".format(*CYCLONEDX_VERSION)
brpath = Path(__file__).parent.parent
cyclonedxpath = Path(os.getenv("BR2_DL_DIR", brpath / "dl")) / "cyclonedx"
-SPDX_SCHEMA_PATH = cyclonedxpath / f"spdx-{CYCLONEDX_VERSION}.schema.json"
+SPDX_SCHEMA_PATH = cyclonedxpath / "spdx-{}.{}.schema.json".format(*CYCLONEDX_VERSION)
BR2_VERSION_FULL = (
subprocess.check_output(
@@ -413,8 +413,8 @@ def main():
cyclonedx_dict = {
"bomFormat": "CycloneDX",
- "$schema": f"http://cyclonedx.org/schema/bom-{CYCLONEDX_VERSION}.schema.json",
- "specVersion": f"{CYCLONEDX_VERSION}",
+ "$schema": "http://cyclonedx.org/schema/bom-{}.{}.schema.json".format(*CYCLONEDX_VERSION),
+ "specVersion": "{}.{}".format(*CYCLONEDX_VERSION),
"metadata": {
"component": {
"bom-ref": args.project_name,
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version
2026-03-11 14:04 ` [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version Thomas Perale via buildroot
@ 2026-04-09 12:08 ` Quentin Schulz via buildroot
2026-04-09 20:27 ` Thomas Perale via buildroot
0 siblings, 1 reply; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-09 12:08 UTC (permalink / raw)
To: Thomas Perale, buildroot
Hi Thomas,
On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> Allows to compare different (major, minor) version easily.
>
> In next commit, an optional newer version of CycloneDX will be
> introduced and this syntax makes it easy to compare without introducing
> a dependency to 'packaging.version'.
>
> Signed-off-by: Thomas Perale <thomas.perale@mind.be>
> ---
> utils/generate-cyclonedx | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
> index 2b6c6d63d3..eff55bf598 100755
> --- a/utils/generate-cyclonedx
> +++ b/utils/generate-cyclonedx
> @@ -20,13 +20,13 @@ import sys
> import re
>
>
> -CYCLONEDX_VERSION = "1.6"
> -SPDX_SCHEMA_URL = f"https://raw.githubusercontent.com/CycloneDX/specification/{CYCLONEDX_VERSION}/schema/spdx.schema.json"
> +CYCLONEDX_VERSION = (1, 6)
> +SPDX_SCHEMA_URL = "https://raw.githubusercontent.com/CycloneDX/specification/{}.{}/schema/
How about generating the version with
'.'.join(str(x) for x in CYCLONEDX_VERSION)
? This way, we support when there are patch versions as well (1.6.1
exists after all).
Maybe a class for the version would be best, then we can convert to
(__init__) and from (__str__) the class to a string and compare two
versions against each other (using tuples or list for example;
__eq__/__lt__).
Now I'm wondering whether the SPDX schema version needs to be in sync
with the BOM schema? Because while there exists a 1.6.1 SPDX schema
which differs from 1.6 SPDX schema, see commit 5f3ee8066491 ("Updating
SPDX license list to 3.24.0."), the BOM schema too is different but it's
kept under the same name of bom-1.6.schema.json (and the $id is the same).
Also, are we validating that our SBOM generated by this script is
actually CycloneDX version X.Y compatible? Should we download the
bom-1.6.schema.json and validate it with e.g. jsonschema?
Cheers,
Quentin
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version
2026-04-09 12:08 ` Quentin Schulz via buildroot
@ 2026-04-09 20:27 ` Thomas Perale via buildroot
0 siblings, 0 replies; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-04-09 20:27 UTC (permalink / raw)
To: Quentin Schulz; +Cc: Thomas Perale, buildroot
Hi Quentin,
> How about generating the version with
>
> '.'.join(str(x) for x in CYCLONEDX_VERSION)
> ? This way, we support when there are patch versions as well (1.6.1 exists after all).
>
> Maybe a class for the version would be best, then we can convert to (__init__) and from (__str__) the class to a string and compare two versions against each other (using tuples or list for example; __eq__/__lt__).
>
> Now I'm wondering whether the SPDX schema version needs to be in sync with the BOM schema? Because while there exists a 1.6.1 SPDX schema which differs from 1.6 SPDX schema, see commit 5f3ee8066491 ("Updating SPDX license list to 3.24.0."), the BOM schema too is different but it's kept under the same name of bom-1.6.schema.json (and the $id is the same).
>
> Also, are we validating that our SBOM generated by this script is actually CycloneDX version X.Y compatible? Should we download the bom-1.6.schema.json and validate it with e.g. jsonschema?
It's true that 1.6.1 exists ... Good catch, i will rework this part and take into account your comments.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Buildroot] [PATCH v5 2/8] utils/generate-cyclonedx: move license download in a function
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 12:12 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 3/8] utils/generate-cyclonedx: move utility function Thomas Perale via buildroot
` (5 subsequent siblings)
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
Move the code that retrieve the SPDX licenses into a function instead of
doing it top-level.
This allows to pass the CycloneDX version as a function argument instead
of defining it as a top-level constant.
In next commit, the CycloneDX spec version might not be known when the
top-level code is executed.
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 49 +++++++++++++++++++++++++++++-----------
1 file changed, 36 insertions(+), 13 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index eff55bf598..52fb8cc3e9 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -18,15 +18,14 @@ import urllib.request
import subprocess
import sys
import re
+from typing import Tuple
CYCLONEDX_VERSION = (1, 6)
-SPDX_SCHEMA_URL = "https://raw.githubusercontent.com/CycloneDX/specification/{}.{}/schema/spdx.schema.json".format(*CYCLONEDX_VERSION)
brpath = Path(__file__).parent.parent
cyclonedxpath = Path(os.getenv("BR2_DL_DIR", brpath / "dl")) / "cyclonedx"
-SPDX_SCHEMA_PATH = cyclonedxpath / "spdx-{}.{}.schema.json".format(*CYCLONEDX_VERSION)
BR2_VERSION_FULL = (
subprocess.check_output(
@@ -41,20 +40,42 @@ BR2_VERSION_FULL = (
# 'resolved_with_pedigree'.
VULN_WITH_PEDIGREE = set()
+# List of supported SPDX license expression.
SPDX_LICENSES = []
-if not SPDX_SCHEMA_PATH.exists():
- # Download the CycloneDX SPDX schema JSON, and cache it locally
- cyclonedxpath.mkdir(parents=True, exist_ok=True)
- urllib.request.urlretrieve(SPDX_SCHEMA_URL, SPDX_SCHEMA_PATH)
-try:
- with SPDX_SCHEMA_PATH.open() as f:
- SPDX_LICENSES = json.load(f).get("enum", [])
-except json.JSONDecodeError:
- # In case of error the license will just not be matched to the SPDX names
- # but the SBOM generation still work.
- print(f"Failed to load the SPDX licenses file: {SPDX_SCHEMA_PATH}", file=sys.stderr)
+def br2_retrieve_spdx_licenses(version: Tuple[int, int]):
+ """Populate the list of supported SPDX licenses for a given CycloneDX
+ version. If the list is not present locally it will be downloaded
+ externally.
+
+ Args:
+ version (str): CycloneDX version
+
+ Returns:
+ None
+ """
+ SPDX_SCHEMA_URL = "https://raw.githubusercontent.com/CycloneDX/specification/{}.{}/schema/spdx.schema.json".format(
+ *version
+ )
+
+ ret = []
+ path = cyclonedxpath / "spdx-{}.{}.schema.json".format(*version)
+
+ if not path.exists():
+ # Download the CycloneDX SPDX schema JSON, and cache it locally
+ cyclonedxpath.mkdir(parents=True, exist_ok=True)
+ urllib.request.urlretrieve(SPDX_SCHEMA_URL, path)
+
+ try:
+ with path.open() as f:
+ ret = json.load(f).get("enum", [])
+ except json.JSONDecodeError:
+ # In case of error the license will just not be matched to the SPDX names
+ # but the SBOM generation still work.
+ print(f"Failed to load the SPDX licenses file: {path}", file=sys.stderr)
+
+ return ret
def split_top_level_comma(subj):
@@ -404,6 +425,8 @@ def main():
parser.print_help()
sys.exit(1)
+ SPDX_LICENSES.extend(br2_retrieve_spdx_licenses(CYCLONEDX_VERSION))
+
show_info_dict = json.load(args.in_file)
# Remove rootfs and virtual packages if not explicitly included
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* [Buildroot] [PATCH v5 3/8] utils/generate-cyclonedx: move utility function
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 1/8] utils/generate-cyclonedx: use tuple for version Thomas Perale via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 2/8] utils/generate-cyclonedx: move license download in a function Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 12:27 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 4/8] utils/generate-cyclonedx: encapsulate CycloneDX generation functions Thomas Perale via buildroot
` (4 subsequent siblings)
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
Move all the utility functions that are not generating CycloneDX
structure to the top of the script and group the CycloneDX generation
function together.
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 178 +++++++++++++++++++--------------------
1 file changed, 89 insertions(+), 89 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index 52fb8cc3e9..facbd6c78f 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -78,65 +78,48 @@ def br2_retrieve_spdx_licenses(version: Tuple[int, int]):
return ret
-def split_top_level_comma(subj):
- """Split a string at comma's, but do not split at comma's in between parentheses.
+def br2_virtual_is_provided_by(ref, show_info_dict) -> list:
+ """Retrieve the list of packages that provide a virtual package.
Args:
- subj (str): String to be split.
+ ref (str): The identifier of the virtual package.
+ show_info_dict (dict): The JSON output of the show-info
+ command, parsed into a Python dictionary.
Returns:
- list: A list of substrings
+ list: package list that provides the virtual package.
"""
- counter = 0
- substring = ""
-
- for char in subj:
- if char == "," and counter == 0:
- yield substring
- substring = ""
- else:
- if char == "(":
- counter += 1
- elif char == ")":
- counter -= 1
- substring += char
+ return [
+ name
+ for name, comp in show_info_dict.items()
+ if "provides" in comp and ref in comp["provides"]
+ ]
- yield substring
+def br2_parse_deps(ref, show_info_dict, virtual=False) -> list:
+ """This function will collect all dependencies from the show-info output.
-def cyclonedx_license(lic):
- """Given the name of a license, create an individual entry in
- CycloneDX format. In CycloneDX, the 'id' keyword is used for
- names that are recognized as SPDX License abbreviations. All other
- license names are placed under the 'name' keyword.
+ The dependency on virtual package will collect the final dependency without
+ including the virtual one.
Args:
- lic (str): Name of the license
+ ref (str): The identifier of the package for which the dependencies have
+ to be looked up.
+ show_info_dict (dict): The JSON output of the show-info
+ command, parsed into a Python dictionary.
Returns:
- dict: An entry for the license in CycloneDX format.
+ list: A list of dependencies of the 'ref' package.
"""
- key = "id" if lic in SPDX_LICENSES else "name"
- return {
- key: lic,
- }
-
-
-def cyclonedx_licenses(lic_list):
- """Create a licenses list formatted for a CycloneDX component
+ deps = set()
- Args:
- lic_list (str): A comma separated list of license names.
+ for dep in show_info_dict.get(ref, {}).get("dependencies", []):
+ if not virtual and show_info_dict.get(dep, {}).get("virtual"):
+ deps.update(br2_virtual_is_provided_by(dep, show_info_dict))
+ else:
+ deps.add(dep)
- Returns:
- dict: A dictionary with license information for the component,
- in CycloneDX format.
- """
- return {
- "licenses": [
- {"license": cyclonedx_license(lic.strip())} for lic in split_top_level_comma(lic_list)
- ]
- }
+ return list(deps)
def extract_cves_from_header(header: str) -> list[str]:
@@ -169,7 +152,7 @@ def patch_retrieve_header(content: str) -> str:
DIFF_LINE_REGEX = re.compile(r"^diff\s+(?:--git|-[-\w]+)\s+(\S+)\s+(\S+)$")
INDEX_LINE_REGEX = re.compile(r"^Index:\s+(\S+)$")
- lines = content.split('\n')
+ lines = content.split("\n")
header = []
for i, line in enumerate(lines):
@@ -218,6 +201,67 @@ def read_patch_file(patch_path: Path) -> str:
return content
+def split_top_level_comma(subj):
+ """Split a string at comma's, but do not split at comma's in between parentheses.
+
+ Args:
+ subj (str): String to be split.
+
+ Returns:
+ list: A list of substrings
+ """
+ counter = 0
+ substring = ""
+
+ for char in subj:
+ if char == "," and counter == 0:
+ yield substring
+ substring = ""
+ else:
+ if char == "(":
+ counter += 1
+ elif char == ")":
+ counter -= 1
+ substring += char
+
+ yield substring
+
+
+def cyclonedx_license(lic):
+ """Given the name of a license, create an individual entry in
+ CycloneDX format. In CycloneDX, the 'id' keyword is used for
+ names that are recognized as SPDX License abbreviations. All other
+ license names are placed under the 'name' keyword.
+
+ Args:
+ lic (str): Name of the license
+
+ Returns:
+ dict: An entry for the license in CycloneDX format.
+ """
+ key = "id" if lic in SPDX_LICENSES else "name"
+ return {
+ key: lic,
+ }
+
+
+def cyclonedx_licenses(lic_list):
+ """Create a licenses list formatted for a CycloneDX component
+
+ Args:
+ lic_list (str): A comma separated list of license names.
+
+ Returns:
+ dict: A dictionary with license information for the component,
+ in CycloneDX format.
+ """
+ return {
+ "licenses": [
+ {"license": cyclonedx_license(lic.strip())} for lic in split_top_level_comma(lic_list)
+ ]
+ }
+
+
def cyclonedx_patches(patch_list: list[str]):
"""Translate a list of patches from the show-info JSON to a list of
patches in CycloneDX format.
@@ -358,50 +402,6 @@ def cyclonedx_vulnerabilities(show_info_dict):
} for cve, components in cves.items()]
-def br2_virtual_is_provided_by(ref, show_info_dict) -> list:
- """Retrieve the list of packages that provide a virtual package.
-
- Args:
- ref (str): The identifier of the virtual package.
- show_info_dict (dict): The JSON output of the show-info
- command, parsed into a Python dictionary.
-
- Returns:
- list: package list that provides the virtual package.
- """
- return [
- name
- for name, comp in show_info_dict.items()
- if "provides" in comp and ref in comp["provides"]
- ]
-
-
-def br2_parse_deps(ref, show_info_dict, virtual=False) -> list:
- """This function will collect all dependencies from the show-info output.
-
- The dependency on virtual package will collect the final dependency without
- including the virtual one.
-
- Args:
- ref (str): The identifier of the package for which the dependencies have
- to be looked up.
- show_info_dict (dict): The JSON output of the show-info
- command, parsed into a Python dictionary.
-
- Returns:
- list: A list of dependencies of the 'ref' package.
- """
- deps = set()
-
- for dep in show_info_dict.get(ref, {}).get("dependencies", []):
- if not virtual and show_info_dict.get(dep, {}).get("virtual"):
- deps.update(br2_virtual_is_provided_by(dep, show_info_dict))
- else:
- deps.add(dep)
-
- return list(deps)
-
-
def main():
parser = argparse.ArgumentParser(
description='''Create a CycloneDX SBoM for the Buildroot configuration.
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 3/8] utils/generate-cyclonedx: move utility function
2026-03-11 14:04 ` [Buildroot] [PATCH v5 3/8] utils/generate-cyclonedx: move utility function Thomas Perale via buildroot
@ 2026-04-09 12:27 ` Quentin Schulz via buildroot
0 siblings, 0 replies; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-09 12:27 UTC (permalink / raw)
To: Thomas Perale, buildroot
Hi Thomas,
On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> Move all the utility functions that are not generating CycloneDX
> structure to the top of the script and group the CycloneDX generation
> function together.
>
and the non-CycloneDX functions were also reordered alphabetically
(except br2_parse_deps) and ...
[...]
> @@ -169,7 +152,7 @@ def patch_retrieve_header(content: str) -> str:
> DIFF_LINE_REGEX = re.compile(r"^diff\s+(?:--git|-[-\w]+)\s+(\S+)\s+(\S+)$")
> INDEX_LINE_REGEX = re.compile(r"^Index:\s+(\S+)$")
>
> - lines = content.split('\n')
> + lines = content.split("\n")
... this was also changed.
But I could reproduce this locally and indeed it's only (except the
small change above) moving things around so:
Reviewed-by: Quentin Schulz <quentin.schulz@cherry.de>
Cheers,
Quentin
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Buildroot] [PATCH v5 4/8] utils/generate-cyclonedx: encapsulate CycloneDX generation functions
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
` (2 preceding siblings ...)
2026-03-11 14:04 ` [Buildroot] [PATCH v5 3/8] utils/generate-cyclonedx: move utility function Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 5/8] utils/generate-cyclonedx: optional bump to v1.7 Thomas Perale via buildroot
` (3 subsequent siblings)
7 siblings, 0 replies; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
Since the script is starting to get bigger, it's also starting to get
more difficult to keep track of all the different functions.
This commit move all the functions that generate a part of the CycloneDX
SBOM spec under the same `CycloneDX` dataclass.
It also allows to avoid to have to deal with a top-level shared context
for the SBOM generation.
Additionnaly, two structure are created: the `Context` and `Filter` one.
They hold arguments passed from the CLI.
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 485 +++++++++++++++++++++------------------
1 file changed, 263 insertions(+), 222 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index facbd6c78f..24c15df94e 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -18,7 +18,8 @@ import urllib.request
import subprocess
import sys
import re
-from typing import Tuple
+from typing import Mapping, Tuple
+from dataclasses import dataclass, field
CYCLONEDX_VERSION = (1, 6)
@@ -35,13 +36,21 @@ BR2_VERSION_FULL = (
.strip()
)
-# Set of vulnerabilities that were addressed by a patch present in buildroot
-# tree. This set is used to set the analysis of the ignored CVEs to
-# 'resolved_with_pedigree'.
-VULN_WITH_PEDIGREE = set()
-# List of supported SPDX license expression.
-SPDX_LICENSES = []
+@dataclass(frozen=True)
+class Options:
+ cyclonedx_version: Tuple[int, int]
+ """CycloneDX Spec version"""
+ project_name: str
+ """Specify the project name to use in the SBOM metadata"""
+ project_version: str
+ """Specify the project version to use in the SBOM metadata"""
+
+
+@dataclass(frozen=True)
+class Filters:
+ virtual: bool
+ """Includes virtual packages to the output"""
def br2_retrieve_spdx_licenses(version: Tuple[int, int]):
@@ -227,179 +236,245 @@ def split_top_level_comma(subj):
yield substring
-def cyclonedx_license(lic):
- """Given the name of a license, create an individual entry in
- CycloneDX format. In CycloneDX, the 'id' keyword is used for
- names that are recognized as SPDX License abbreviations. All other
- license names are placed under the 'name' keyword.
-
- Args:
- lic (str): Name of the license
-
- Returns:
- dict: An entry for the license in CycloneDX format.
- """
- key = "id" if lic in SPDX_LICENSES else "name"
- return {
- key: lic,
- }
-
-
-def cyclonedx_licenses(lic_list):
- """Create a licenses list formatted for a CycloneDX component
-
- Args:
- lic_list (str): A comma separated list of license names.
-
- Returns:
- dict: A dictionary with license information for the component,
- in CycloneDX format.
- """
- return {
- "licenses": [
- {"license": cyclonedx_license(lic.strip())} for lic in split_top_level_comma(lic_list)
- ]
- }
-
-
-def cyclonedx_patches(patch_list: list[str]):
- """Translate a list of patches from the show-info JSON to a list of
- patches in CycloneDX format.
-
- Args:
- patch_list (list): Array of patch relative paths for a given component.
-
- Returns:
- dict: Patch information in CycloneDX format.
- """
- patch_contents = []
- for patch in patch_list:
- patch_path = brpath / patch
- if patch_path.exists():
- try:
- content = read_patch_file(patch_path)
- except Exception:
- # If the patch can't be read it won't be added to
- # the resulting SBOM.
- print(f"Failed to handle patch: {patch}", file=sys.stderr)
- continue
-
- header = patch_retrieve_header(content)
-
- issue = {}
- cves = extract_cves_from_header(header)
- if cves:
- VULN_WITH_PEDIGREE.update(cves)
- issue = {
- "resolves": [
- {
- "type": "security",
- "name": cve
- } for cve in cves
- ]
- }
+@dataclass
+class CycloneDX:
+ show_info_dict: Mapping
+ """Output of Buildroot `make show-info` converted as a dict."""
+ options: Options
+ """CycloneDX generation options passed from the CLI arguments"""
+ filters: Filters
+ """Filters that apply to the `show_info_dict` content"""
+ _filtered_show_info_dict: Mapping = field(default_factory=dict)
+ """Subset of `show_info_dict` based on the `Filters` passed in the the
+ `filters` argument."""
+ _spdx_licenses: list = field(default_factory=list)
+ """List of supported SPDX license expression. Initialized during the
+ `__post_init__` step, fetched either online or from local copy"""
+ _vuln_with_pedigree: set = field(default_factory=set)
+ """Set of vulnerabilities that were addressed by a patch present in
+ buildroot tree. This set is used to set the analysis of the ignored CVEs to
+ 'resolved_with_pedigree'."""
+
+ def __post_init__(self):
+ self._spdx_licenses = br2_retrieve_spdx_licenses(self.options.cyclonedx_version)
+
+ # Remove rootfs and virtual packages if not explicitly included
+ # from the cli arguments
+ self._filtered_show_info_dict = {
+ k: v for k, v in self.show_info_dict.items()
+ if ("rootfs" not in v["type"]) and (self.filters.virtual or v["virtual"] is False)
+ }
+
+ def _component_licenses_license(self, lic: str):
+ """Given the name of a license, create an individual entry in
+ CycloneDX format. In CycloneDX, the 'id' keyword is used for
+ names that are recognized as SPDX License abbreviations. All other
+ license names are placed under the 'name' keyword.
+
+ Args:
+ lic (str): Name of the license
+
+ Returns:
+ dict: An entry for the license in CycloneDX format.
+ """
+ key = "id" if lic in self._spdx_licenses else "name"
+ return {
+ key: lic,
+ }
+
+ def _component_licenses(self, lic_list: str):
+ """Create a licenses list formatted for a CycloneDX component
+
+ Args:
+ lic_list (str): A comma separated list of license names.
+
+ Returns:
+ dict: A dictionary with license information for the component,
+ in CycloneDX format.
+ """
+ return {
+ "licenses": [
+ {"license": self._component_licenses_license(lic.strip())} for lic in split_top_level_comma(lic_list)
+ ]
+ }
+
+ def _component_patches(self, patch_list: list[str]):
+ """Translate a list of patches from the show-info JSON to a list of
+ patches in CycloneDX format.
+
+ Args:
+ patch_list (list): Array of patch relative paths for a given component.
+
+ Returns:
+ dict: Patch information in CycloneDX format.
+ """
+ patch_contents = []
+ for patch in patch_list:
+ patch_path = brpath / patch
+ if patch_path.exists():
+ try:
+ content = read_patch_file(patch_path)
+ except Exception:
+ # If the patch can't be read it won't be added to
+ # the resulting SBOM.
+ print(f"Failed to handle patch: {patch}", file=sys.stderr)
+ continue
+
+ header = patch_retrieve_header(content)
+
+ issue = {}
+ cves = extract_cves_from_header(header)
+ if cves:
+ self._vuln_with_pedigree.update(cves)
+ issue = {
+ "resolves": [
+ {
+ "type": "security",
+ "name": cve
+ } for cve in cves
+ ]
+ }
- patch_contents.append({
- "diff": {
- "text": {
- "content": content
+ patch_contents.append({
+ "diff": {
+ "text": {
+ "content": content
+ }
+ },
+ **issue
+ })
+ else:
+ # If the patch is not a file it's a tarball or diff url passed
+ # through the `<pkg-name>_PATCH` variable.
+ patch_contents.append({
+ "diff": {
+ "url": patch
}
+ })
+
+ return {
+ "pedigree": {
+ "patches": [{
+ "type": "unofficial",
+ **content
+ } for content in patch_contents]
+ },
+ }
+
+ def _component(self, name: str, comp: dict):
+ """Translate a component from the show-info output, to a component entry in CycloneDX format.
+
+ Args:
+ name (str): Key used for the package in the show-info output.
+ comp (dict): Data about the package as a Python dictionary.
+
+ Returns:
+ dict: Component information in CycloneDX format.
+ """
+ return {
+ "bom-ref": name,
+ "type": "library",
+ **({
+ "name": comp["name"],
+ } if "name" in comp else {}),
+ **({
+ "version": comp["version"],
+ **(self._component_licenses(comp["licenses"]) if "licenses" in comp else {}),
+ } if not comp["virtual"] else {}),
+ **({
+ "cpe": comp["cpe-id"],
+ } if "cpe-id" in comp else {}),
+ **(self._component_patches(comp["patches"]) if comp.get("patches") else {}),
+ "properties": [{
+ "name": "BR_TYPE",
+ "value": comp["type"],
+ }],
+ }
+
+ def _dependency(self, ref: str, depends: list):
+ """Create JSON for dependency relationships between components.
+
+ Args:
+ ref (str): reference to a component bom-ref.
+ depends (list): array of component bom-ref identifier to create the dependencies.
+
+ Returns:
+ dict: Dependency information in CycloneDX format.
+ """
+ return {
+ "ref": ref,
+ "dependsOn": sorted(depends),
+ }
+
+ @property
+ def vulnerabilities(self):
+ """Create a JSON list of vulnerabilities ignored by buildroot and associate
+ the component for which they are solved.
+
+ Args:
+ show_info_dict (dict): The JSON output of the show-info
+ command, parsed into a Python dictionary.
+
+ Returns:
+ list: Solved vulnerabilities list in CycloneDX format.
+ """
+ cves = {}
+
+ for name, comp in self._filtered_show_info_dict.items():
+ for cve in comp.get('ignore_cves', []):
+ cves.setdefault(cve, []).append(name)
+
+ return [{
+ "id": cve,
+ "analysis": {
+ "state": "resolved_with_pedigree" if cve in self._vuln_with_pedigree else "in_triage",
+ "detail": f"The CVE '{cve}' has been marked as ignored by Buildroot"
+ },
+ "affects": [
+ {"ref": bomref} for bomref in components
+ ]
+ } for cve, components in cves.items()]
+
+ @property
+ def cyclonedx(self):
+ return {
+ "bomFormat": "CycloneDX",
+ "$schema": "http://cyclonedx.org/schema/bom-{}.{}.schema.json".format(*self.options.cyclonedx_version),
+ "specVersion": "{}.{}".format(*self.options.cyclonedx_version),
+ "metadata": {
+ "component": {
+ "bom-ref": self.options.project_name,
+ "name": self.options.project_name,
+ "version": self.options.project_version,
+ "type": "firmware",
},
- **issue
- })
- else:
- # If the patch is not a file it's a tarball or diff url passed
- # through the `<pkg-name>_PATCH` variable.
- patch_contents.append({
- "diff": {
- "url": patch
+ "tools": {
+ "components": [
+ {
+ "type": "application",
+ "name": "Buildroot generate-cyclonedx",
+ "version": f"{BR2_VERSION_FULL}",
+ "licenses": [
+ {
+ "license": {
+ "id": "GPL-2.0"
+ }
+ }
+ ]
+ }
+ ],
}
- })
-
- return {
- "pedigree": {
- "patches": [{
- "type": "unofficial",
- **content
- } for content in patch_contents]
- },
- }
-
-
-def cyclonedx_component(name, comp):
- """Translate a component from the show-info output, to a component entry in CycloneDX format.
-
- Args:
- name (str): Key used for the package in the show-info output.
- comp (dict): Data about the package as a Python dictionary.
-
- Returns:
- dict: Component information in CycloneDX format.
- """
- return {
- "bom-ref": name,
- "type": "library",
- **({
- "name": comp["name"],
- } if "name" in comp else {}),
- **({
- "version": comp["version"],
- **(cyclonedx_licenses(comp["licenses"]) if "licenses" in comp else {}),
- } if not comp["virtual"] else {}),
- **({
- "cpe": comp["cpe-id"],
- } if "cpe-id" in comp else {}),
- **(cyclonedx_patches(comp["patches"]) if comp.get("patches") else {}),
- "properties": [{
- "name": "BR_TYPE",
- "value": comp["type"],
- }],
- }
-
-
-def cyclonedx_dependency(ref, depends):
- """Create JSON for dependency relationships between components.
-
- Args:
- ref (str): reference to a component bom-ref.
- depends (list): array of component bom-ref identifier to create the dependencies.
-
- Returns:
- dict: Dependency information in CycloneDX format.
- """
- return {
- "ref": ref,
- "dependsOn": sorted(depends),
- }
-
-
-def cyclonedx_vulnerabilities(show_info_dict):
- """Create a JSON list of vulnerabilities ignored by buildroot and associate
- the component for which they are solved.
-
- Args:
- show_info_dict (dict): The JSON output of the show-info
- command, parsed into a Python dictionary.
-
- Returns:
- list: Solved vulnerabilities list in CycloneDX format.
- """
- cves = {}
-
- for name, comp in show_info_dict.items():
- for cve in comp.get('ignore_cves', []):
- cves.setdefault(cve, []).append(name)
-
- return [{
- "id": cve,
- "analysis": {
- "state": "resolved_with_pedigree" if cve in VULN_WITH_PEDIGREE else "in_triage",
- "detail": f"The CVE '{cve}' has been marked as ignored by Buildroot"
- },
- "affects": [
- {"ref": bomref} for bomref in components
- ]
- } for cve, components in cves.items()]
+ },
+ "components": [
+ self._component(name, comp) for name, comp in self._filtered_show_info_dict.items()
+ ],
+ "dependencies": [
+ self._dependency(self.options.project_name, list(self._filtered_show_info_dict)),
+ *[self._dependency(ref, br2_parse_deps(ref, self.show_info_dict, self.filters.virtual))
+ for ref in self._filtered_show_info_dict],
+ ],
+ "vulnerabilities": self.vulnerabilities,
+ }
def main():
@@ -425,55 +500,21 @@ def main():
parser.print_help()
sys.exit(1)
- SPDX_LICENSES.extend(br2_retrieve_spdx_licenses(CYCLONEDX_VERSION))
+ opt = Options(
+ cyclonedx_version=CYCLONEDX_VERSION,
+ project_name=args.project_name,
+ project_version=args.project_version,
+ )
+
+ filters = Filters(
+ virtual=args.virtual
+ )
show_info_dict = json.load(args.in_file)
- # Remove rootfs and virtual packages if not explicitly included
- # from the cli arguments
- filtered_show_info_dict = {k: v for k, v in show_info_dict.items()
- if ("rootfs" not in v["type"]) and (args.virtual or v["virtual"] is False)}
-
- cyclonedx_dict = {
- "bomFormat": "CycloneDX",
- "$schema": "http://cyclonedx.org/schema/bom-{}.{}.schema.json".format(*CYCLONEDX_VERSION),
- "specVersion": "{}.{}".format(*CYCLONEDX_VERSION),
- "metadata": {
- "component": {
- "bom-ref": args.project_name,
- "name": args.project_name,
- "version": args.project_version,
- "type": "firmware",
- },
- "tools": {
- "components": [
- {
- "type": "application",
- "name": "Buildroot generate-cyclonedx",
- "version": f"{BR2_VERSION_FULL}",
- "licenses": [
- {
- "license": {
- "id": "GPL-2.0"
- }
- }
- ]
- }
- ],
- }
- },
- "components": [
- cyclonedx_component(name, comp) for name, comp in filtered_show_info_dict.items()
- ],
- "dependencies": [
- cyclonedx_dependency(args.project_name, list(filtered_show_info_dict)),
- *[cyclonedx_dependency(ref, br2_parse_deps(ref, show_info_dict, args.virtual))
- for ref in filtered_show_info_dict],
- ],
- "vulnerabilities": cyclonedx_vulnerabilities(show_info_dict),
- }
-
- args.out_file.write(json.dumps(cyclonedx_dict, indent=2))
+ out = CycloneDX(show_info_dict=show_info_dict, options=opt, filters=filters)
+
+ args.out_file.write(json.dumps(out.cyclonedx, indent=2))
args.out_file.write('\n')
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* [Buildroot] [PATCH v5 5/8] utils/generate-cyclonedx: optional bump to v1.7
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
` (3 preceding siblings ...)
2026-03-11 14:04 ` [Buildroot] [PATCH v5 4/8] utils/generate-cyclonedx: encapsulate CycloneDX generation functions Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 12:40 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external Thomas Perale via buildroot
` (2 subsequent siblings)
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
See changes:
- https://cyclonedx.org/news/cyclonedx-v1.7-released/
- https://github.com/CycloneDX/specification/releases/tag/1.7
Since some tools like DependencyTrack don't support CycloneDX SBOM v1.7
this bump is optional and default remains v1.6.
To allow generation of CycloneDX based on the current version of the
spec we are using a 'Context' datastructure is created that reference
the CycloneDX version spec currently in use.
This Context datastructure is then passed down to functions that
generate part of the CycloneDX SBOM.
All the top level datastructure are moved into the Context
datastructure that will be generated after the arguments from the
command line are interpreted.
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index 24c15df94e..cbce3364de 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -21,8 +21,7 @@ import re
from typing import Mapping, Tuple
from dataclasses import dataclass, field
-
-CYCLONEDX_VERSION = (1, 6)
+DEFAULT_CYCLONEDX_VERSION = (1, 6)
brpath = Path(__file__).parent.parent
@@ -493,6 +492,9 @@ def main():
help="Specify the project name to use in the SBOM metadata (default:'buildroot')")
parser.add_argument("--project-version", type=str, default=f"{BR2_VERSION_FULL}",
help="Specify the project version to use in the SBOM metadata (default: builroot version)")
+ parser.add_argument("--v17", dest='cdx_version', action='store_const',
+ const=(1, 7), default=DEFAULT_CYCLONEDX_VERSION,
+ help=f"Use CycloneDX version 1.7 (default: {DEFAULT_CYCLONEDX_VERSION})")
args = parser.parse_args()
@@ -501,7 +503,7 @@ def main():
sys.exit(1)
opt = Options(
- cyclonedx_version=CYCLONEDX_VERSION,
+ cyclonedx_version=args.cdx_version,
project_name=args.project_name,
project_version=args.project_version,
)
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 5/8] utils/generate-cyclonedx: optional bump to v1.7
2026-03-11 14:04 ` [Buildroot] [PATCH v5 5/8] utils/generate-cyclonedx: optional bump to v1.7 Thomas Perale via buildroot
@ 2026-04-09 12:40 ` Quentin Schulz via buildroot
0 siblings, 0 replies; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-09 12:40 UTC (permalink / raw)
To: Thomas Perale, buildroot
Hi Thomas,
On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> See changes:
>
> - https://cyclonedx.org/news/cyclonedx-v1.7-released/
> - https://github.com/CycloneDX/specification/releases/tag/1.7
>
> Since some tools like DependencyTrack don't support CycloneDX SBOM v1.7
> this bump is optional and default remains v1.6.
>
> To allow generation of CycloneDX based on the current version of the
> spec we are using a 'Context' datastructure is created that reference
> the CycloneDX version spec currently in use.
>
> This Context datastructure is then passed down to functions that
> generate part of the CycloneDX SBOM.
>
> All the top level datastructure are moved into the Context
> datastructure that will be generated after the arguments from the
> command line are interpreted.
>
> Signed-off-by: Thomas Perale <thomas.perale@mind.be>
> ---
> utils/generate-cyclonedx | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
> index 24c15df94e..cbce3364de 100755
> --- a/utils/generate-cyclonedx
> +++ b/utils/generate-cyclonedx
> @@ -21,8 +21,7 @@ import re
> from typing import Mapping, Tuple
> from dataclasses import dataclass, field
>
> -
> -CYCLONEDX_VERSION = (1, 6)
> +DEFAULT_CYCLONEDX_VERSION = (1, 6)
>
> brpath = Path(__file__).parent.parent
>
> @@ -493,6 +492,9 @@ def main():
> help="Specify the project name to use in the SBOM metadata (default:'buildroot')")
> parser.add_argument("--project-version", type=str, default=f"{BR2_VERSION_FULL}",
> help="Specify the project version to use in the SBOM metadata (default: builroot version)")
> + parser.add_argument("--v17", dest='cdx_version', action='store_const',
> + const=(1, 7), default=DEFAULT_CYCLONEDX_VERSION,
> + help=f"Use CycloneDX version 1.7 (default: {DEFAULT_CYCLONEDX_VERSION})")
>
How about making this a choice instead so we can easily extend this
later on? c.f. https://docs.python.org/3/library/argparse.html#choices
Then use it with generate-cyclonedx --cyclone-sbom-vers 1.7 for example.
Otherwise I'm afraid we're going to have to add more --v arguments and
handle them separately (or I guess with a mutual exclusion group).
Cheers,
Quentin
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
` (4 preceding siblings ...)
2026-03-11 14:04 ` [Buildroot] [PATCH v5 5/8] utils/generate-cyclonedx: optional bump to v1.7 Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 12:58 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves Thomas Perale via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 8/8] utils/generate-cyclonedx: split vulnerabilities per state Thomas Perale via buildroot
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
Mark Buildroot's host packages as 'external' with the 'isExternal'
property. Starting CycloneDX v1.7 [1][2] the CycloneDX spec defines this
property like the following [3].
> An external component is one that is not part of an assembly, but is
> expected to be provided by the environment, regardless of the
> component's .scope. This setting can be useful for distinguishing which
> components are bundled with the product and which can be relied upon to
> be present in the deployment environment.
This fits the usage of 'host' packages : a package provided by the
environment and not bundled with the target.
[1] https://github.com/CycloneDX/specification/releases/tag/1.7
[2] https://cyclonedx.org/docs/1.7/json/#components_items_isExternal
[3] https://github.com/CycloneDX/specification/issues/321
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 3 +++
1 file changed, 3 insertions(+)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index cbce3364de..35d8c72057 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -373,6 +373,9 @@ class CycloneDX:
return {
"bom-ref": name,
"type": "library",
+ **({
+ "isExternal": True
+ } if self.options.cyclonedx_version >= (1, 7) and comp["type"] == "host" else {}),
**({
"name": comp["name"],
} if "name" in comp else {}),
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external
2026-03-11 14:04 ` [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external Thomas Perale via buildroot
@ 2026-04-09 12:58 ` Quentin Schulz via buildroot
2026-04-09 20:42 ` Thomas Perale via buildroot
0 siblings, 1 reply; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-09 12:58 UTC (permalink / raw)
To: Thomas Perale, buildroot
Hi Thomas,
On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> Mark Buildroot's host packages as 'external' with the 'isExternal'
> property. Starting CycloneDX v1.7 [1][2] the CycloneDX spec defines this
> property like the following [3].
>
>> An external component is one that is not part of an assembly, but is
>> expected to be provided by the environment, regardless of the
>> component's .scope. This setting can be useful for distinguishing which
>> components are bundled with the product and which can be relied upon to
>> be present in the deployment environment.
>
> This fits the usage of 'host' packages : a package provided by the
> environment and not bundled with the target.
>
That's not my understanding. None of the examples provided in the GitHub
issue match a requirement on host executables (in the context of
cross-compilation). They all are related to runtime dependencies
(dynamic linking, interpreters, hardware-limitation (which SoC/CPU can
this run on)) you need to use with the binary(ies) associated with that
SBOM. What Buildroot generates is likely self-sufficient? A Buildroot
SDK is kinda different, but then the host packages wouldn't be external
anyway since they would be part of the SDK and thus "internal". Do you
have a different reading of the spec on this? (We just started to look
at CycloneDX generated by Buildroot so I wouldn't trust my own
gut-feeling on that :) ).
> [1] https://github.com/CycloneDX/specification/releases/tag/1.7
> [2] https://cyclonedx.org/docs/1.7/json/#components_items_isExternal
> [3] https://github.com/CycloneDX/specification/issues/321
>
> Signed-off-by: Thomas Perale <thomas.perale@mind.be>
> ---
> utils/generate-cyclonedx | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
> index cbce3364de..35d8c72057 100755
> --- a/utils/generate-cyclonedx
> +++ b/utils/generate-cyclonedx
> @@ -373,6 +373,9 @@ class CycloneDX:
> return {
> "bom-ref": name,
> "type": "library",
> + **({
> + "isExternal": True
> + } if self.options.cyclonedx_version >= (1, 7) and comp["type"] == "host" else {}),
> **({
> "name": comp["name"],
> } if "name" in comp else {}),
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external
2026-04-09 12:58 ` Quentin Schulz via buildroot
@ 2026-04-09 20:42 ` Thomas Perale via buildroot
2026-04-09 20:43 ` Thomas Perale via buildroot
0 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-04-09 20:42 UTC (permalink / raw)
To: Quentin Schulz; +Cc: Thomas Perale, buildroot
Hi Quentin,
In reply of:
> Hi Thomas,
>
> On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> > Mark Buildroot's host packages as 'external' with the 'isExternal'
> > property. Starting CycloneDX v1.7 [1][2] the CycloneDX spec defines this
> > property like the following [3].
> > >> An external component is one that is not part of an assembly, but is
> >> expected to be provided by the environment, regardless of the
> >> component's .scope. This setting can be useful for distinguishing which
> >> components are bundled with the product and which can be relied upon to
> >> be present in the deployment environment.
> > > This fits the usage of 'host' packages : a package provided by the
> > environment and not bundled with the target.
> >
> That's not my understanding. None of the examples provided in the GitHub issue match a requirement on host executables (in the context of cross-compilation).
Couldn't find any example that would suit the Buildroot case as well.
> They all are related to runtime dependencies (dynamic linking, interpreters, hardware-limitation (which SoC/CPU can this run on)) you need to use with the binary(ies) associated with that SBOM.
> What Buildroot generates is likely self-sufficient?
As you said, I think this property was mostly added to support dynamicly linked libraries.
> A Buildroot SDK is kinda different, but then the host packages wouldn't be external anyway since they would be part of the SDK and thus "internal".
If you need to create an SBOM of the Buildroot SDK itself indeed that would be
a completely different "point of view" for the software and would require a
different logic.
Is it something you are trying to create ?
> Do you have a different reading of the spec on this? (We just started to look at CycloneDX generated by Buildroot so I wouldn't trust my own gut-feeling on that :) ).
As you said, I don't think they have taken into account the use-cases for
build-systems. It's mostly an interpretation of the spec that made me add this
property.
I saw other implementation use the `scope: excluded` property but imo it make
less sense.
According to this discussion: https://github.com/CycloneDX/specification/discussions/712
For `isExternal` they mention:
> This setting can be useful for distinguishing which components are bundled
> with the product and which can be relied upon to be present in the deployment
> environment
If the firmware you generate with Buildroot is the "bundled product" and the
build-system the "deployment environment" I found this property to suit well
our use-case.
For the `scope` property they mention:
> "excluded" - Components that are excluded provide the ability to document
> component usage for test and other non-runtime purposes. Excluded components
> are not reachable within a call graph at runtime.
The "other non-runtime purposes" part could also suits the "host" package
definition I guess ?
It's all interpretation so it's good that we have these discussions :-)
PERALE Thomas
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external
2026-04-09 20:42 ` Thomas Perale via buildroot
@ 2026-04-09 20:43 ` Thomas Perale via buildroot
2026-04-10 9:12 ` Quentin Schulz via buildroot
0 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-04-09 20:43 UTC (permalink / raw)
To: Quentin Schulz, Arnout Vandecappelle; +Cc: buildroot
Adding Arnout to the discussion, we briefly discussed this during the
FOSDEM Dev Meeting. Maybe he can share his point of view as well.
On 4/9/26 10:42 PM, Thomas Perale wrote:
> Hi Quentin,
>
> In reply of:
>> Hi Thomas,
>>
>> On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
>>> Mark Buildroot's host packages as 'external' with the 'isExternal'
>>> property. Starting CycloneDX v1.7 [1][2] the CycloneDX spec defines this
>>> property like the following [3].
>>>>> An external component is one that is not part of an assembly, but is
>>>> expected to be provided by the environment, regardless of the
>>>> component's .scope. This setting can be useful for distinguishing which
>>>> components are bundled with the product and which can be relied upon to
>>>> be present in the deployment environment.
>>>> This fits the usage of 'host' packages : a package provided by the
>>> environment and not bundled with the target.
>>>
>> That's not my understanding. None of the examples provided in the GitHub issue match a requirement on host executables (in the context of cross-compilation).
> Couldn't find any example that would suit the Buildroot case as well.
>
>> They all are related to runtime dependencies (dynamic linking, interpreters, hardware-limitation (which SoC/CPU can this run on)) you need to use with the binary(ies) associated with that SBOM.
>> What Buildroot generates is likely self-sufficient?
> As you said, I think this property was mostly added to support dynamicly linked libraries.
>
>> A Buildroot SDK is kinda different, but then the host packages wouldn't be external anyway since they would be part of the SDK and thus "internal".
> If you need to create an SBOM of the Buildroot SDK itself indeed that would be
> a completely different "point of view" for the software and would require a
> different logic.
>
> Is it something you are trying to create ?
>
>> Do you have a different reading of the spec on this? (We just started to look at CycloneDX generated by Buildroot so I wouldn't trust my own gut-feeling on that :) ).
> As you said, I don't think they have taken into account the use-cases for
> build-systems. It's mostly an interpretation of the spec that made me add this
> property.
>
> I saw other implementation use the `scope: excluded` property but imo it make
> less sense.
>
> According to this discussion: https://github.com/CycloneDX/specification/discussions/712
>
> For `isExternal` they mention:
>
>> This setting can be useful for distinguishing which components are bundled
>> with the product and which can be relied upon to be present in the deployment
>> environment
> If the firmware you generate with Buildroot is the "bundled product" and the
> build-system the "deployment environment" I found this property to suit well
> our use-case.
>
> For the `scope` property they mention:
>
>> "excluded" - Components that are excluded provide the ability to document
>> component usage for test and other non-runtime purposes. Excluded components
>> are not reachable within a call graph at runtime.
> The "other non-runtime purposes" part could also suits the "host" package
> definition I guess ?
>
> It's all interpretation so it's good that we have these discussions :-)
>
> PERALE Thomas
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external
2026-04-09 20:43 ` Thomas Perale via buildroot
@ 2026-04-10 9:12 ` Quentin Schulz via buildroot
0 siblings, 0 replies; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-10 9:12 UTC (permalink / raw)
To: Thomas Perale, Arnout Vandecappelle; +Cc: buildroot
Hi Thomas,
On 4/9/26 10:43 PM, Thomas Perale wrote:
> Adding Arnout to the discussion, we briefly discussed this during the
> FOSDEM Dev Meeting. Maybe he can share his point of view as well.
>
> On 4/9/26 10:42 PM, Thomas Perale wrote:
>> Hi Quentin,
>>
>> In reply of:
>>> Hi Thomas,
>>>
>>> On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
>>>> Mark Buildroot's host packages as 'external' with the 'isExternal'
>>>> property. Starting CycloneDX v1.7 [1][2] the CycloneDX spec defines
>>>> this
>>>> property like the following [3].
>>>>>> An external component is one that is not part of an assembly, but is
>>>>> expected to be provided by the environment, regardless of the
>>>>> component's .scope. This setting can be useful for distinguishing
>>>>> which
>>>>> components are bundled with the product and which can be relied
>>>>> upon to
>>>>> be present in the deployment environment.
>>>>> This fits the usage of 'host' packages : a package provided by the
>>>> environment and not bundled with the target.
>>>>
>>> That's not my understanding. None of the examples provided in the
>>> GitHub issue match a requirement on host executables (in the context
>>> of cross-compilation).
>> Couldn't find any example that would suit the Buildroot case as well.
>>
>>> They all are related to runtime dependencies (dynamic linking,
>>> interpreters, hardware-limitation (which SoC/CPU can this run on))
>>> you need to use with the binary(ies) associated with that SBOM.
>>> What Buildroot generates is likely self-sufficient?
>> As you said, I think this property was mostly added to support
>> dynamicly linked libraries.
>>
>>> A Buildroot SDK is kinda different, but then the host packages
>>> wouldn't be external anyway since they would be part of the SDK and
>>> thus "internal".
>> If you need to create an SBOM of the Buildroot SDK itself indeed that
>> would be
>> a completely different "point of view" for the software and would
>> require a
>> different logic.
>>
>> Is it something you are trying to create ?
>>
Nope, but I think it could be important (though not sure
generate-cyclonedx have a way to know whether you're building an image
or an sdk, or even if it should). FWIW, we strip our SBOM of the host
packages. We weren't asked for an SBOM but for versions of the software
running on our products, so a strictly valid SBOM isn't required per-se
for now. That may change though. (we also duplicate components for which
we know there are multiple CPEs, since CycloneDX has decided to not
support that and there are multiple instances of packages having
multiple CPEs (arm-trusted-firmware, optee-os to name only those).
>>> Do you have a different reading of the spec on this? (We just started
>>> to look at CycloneDX generated by Buildroot so I wouldn't trust my
>>> own gut-feeling on that :) ).
>> As you said, I don't think they have taken into account the use-cases for
>> build-systems. It's mostly an interpretation of the spec that made me
>> add this
>> property.
>>
>> I saw other implementation use the `scope: excluded` property but imo
>> it make
>> less sense.
>>
>> According to this discussion: https://
>> eur02.safelinks.protection.outlook.com/?
>> url=https%3A%2F%2Fgithub.com%2FCycloneDX%2Fspecification%2Fdiscussions%2F712&data=05%7C02%7Cquentin.schulz%40cherry.de%7C571d171992b84ec86a4208de9678ad6c%7C5e0e1b5221b54e7b83bb514ec460677e%7C0%7C0%7C639113642214259479%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=gl4Nx2Id9MjfJ4DfXvVVlmfhhG5xxqVkhAqnGzrCcL4%3D&reserved=0
>>
>> For `isExternal` they mention:
>>
>>> This setting can be useful for distinguishing which components are
>>> bundled
>>> with the product and which can be relied upon to be present in the
>>> deployment
>>> environment
>> If the firmware you generate with Buildroot is the "bundled product"
>> and the
>> build-system the "deployment environment" I found this property to
>> suit well
>> our use-case.
>>
I don't understand deployment environment as being Buildroot. I
understand it as being the device where this runs (hardware, OS, shared
libraries and external binaries linked against/called at runtime). So
the target device is the deployment environment. Deployment environment
is where it's deployed, not where it's built. When I say "I'm deploying
to production", nobody cares from which PC I do this, but to which
device/server I'm deploying.
>> For the `scope` property they mention:
>>
>>> "excluded" - Components that are excluded provide the ability to
>>> document
>>> component usage for test and other non-runtime purposes. Excluded
>>> components
>>> are not reachable within a call graph at runtime.
>> The "other non-runtime purposes" part could also suits the "host" package
>> definition I guess ?
>>
This sounds like a better match for a Buildroot host package. Though the
comment you referred to uses both .isExternal=True and .scope=excluded
as example. The question is where to draw the line... As far as I
remember, some host tools are not built by Buildroot, Buildroot expects
them to be available on the host system. So, should the host system be
part of the SBOM too? (Of course, that's likely outside of Buildroot's
scope for generating an SBOM).
>> It's all interpretation so it's good that we have these discussions :-)
Maybe we should start a Discussion on GitHub about that? Yocto doesn't
have official support for CycloneDX (only SPDX) so not much we can check
on that side.
Cheers,
Quentin
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
` (5 preceding siblings ...)
2026-03-11 14:04 ` [Buildroot] [PATCH v5 6/8] utils/generate-cyclonedx: mark host packages as external Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
2026-04-09 13:22 ` Quentin Schulz via buildroot
2026-03-11 14:04 ` [Buildroot] [PATCH v5 8/8] utils/generate-cyclonedx: split vulnerabilities per state Thomas Perale via buildroot
7 siblings, 1 reply; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
The CycloneDX spec defines the `components.pedigree.patches.resolves`
object [1] as supporting both a 'name' & 'id' property.
The definition provided for the `id` property is the following [2]:
> The identifier of the issue assigned by the source of the issue
And for the `name` property [3]:
> The name of the issue
The `resolves` object was introduced in commit [4] and only used the
`name` property to link to the vulnerability it resolves.
In example [5] provided on the CycloneDX website, both the `name` & `id`
properties are set in the security example.
In the vulnerability context, and more precisely the CVEs, the `name`
and the `id' are the same and thus the value can be duplicated across
the two properties.
This commit replicates the behavior documented in [5] to improve
compatibility with tools.
[1] https://cyclonedx.org/docs/1.7/json/#components_items_pedigree_patches_items_resolves
[2] https://cyclonedx.org/docs/1.7/json/#components_items_pedigree_patches_items_resolves_items_id
[3] https://cyclonedx.org/docs/1.7/json/#components_items_pedigree_patches_items_resolves_items_name
[4] 9415529923 utils/generate-cyclonedx: add support for 'resolved_with_pedigree'
[5] https://cyclonedx.org/use-cases/pedigree/
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 1 +
1 file changed, 1 insertion(+)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index 35d8c72057..43167d12d0 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -329,6 +329,7 @@ class CycloneDX:
"resolves": [
{
"type": "security",
+ "id": cve,
"name": cve
} for cve in cves
]
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves
2026-03-11 14:04 ` [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves Thomas Perale via buildroot
@ 2026-04-09 13:22 ` Quentin Schulz via buildroot
2026-04-09 20:24 ` Thomas Perale via buildroot
0 siblings, 1 reply; 20+ messages in thread
From: Quentin Schulz via buildroot @ 2026-04-09 13:22 UTC (permalink / raw)
To: Thomas Perale, buildroot
Hi Thomas,
On 3/11/26 3:04 PM, Thomas Perale via buildroot wrote:
> The CycloneDX spec defines the `components.pedigree.patches.resolves`
> object [1] as supporting both a 'name' & 'id' property.
>
> The definition provided for the `id` property is the following [2]:
>
>> The identifier of the issue assigned by the source of the issue
>
> And for the `name` property [3]:
>
>> The name of the issue
>
> The `resolves` object was introduced in commit [4] and only used the
> `name` property to link to the vulnerability it resolves.
>
> In example [5] provided on the CycloneDX website, both the `name` & `id`
> properties are set in the security example.
>
> In the vulnerability context, and more precisely the CVEs, the `name`
> and the `id' are the same and thus the value can be duplicated across
> the two properties.
>
> This commit replicates the behavior documented in [5] to improve
> compatibility with tools.
>
Are you aware of tools that were broken before this? Would be nice for
the commit log :)
My opinion is this isn't v1.7-specific but rather a "fix" or
"improvement" that we don't necessarily need to have in this series.
What do you think of having this as a separate patch (or at the
beginning of the series so the series can be applied partially easily?).?
Honestly strange we have id and name for seemingly the same thing. The
introducing commit was 2512eb835a46 ("#21 - Added support for patches
and unit tests for JSON schema") for both and
https://github.com/CycloneDX/specification/issues/21 doesn't shine a
light of why for either. The only hint is
https://github.com/CycloneDX/specification/issues/21#issuecomment-625104801
where
```
<issue type="[ defect | enhancement | security ]"
ref="7bc2d01f-a0e8-4ae2-a274-7bd188f89926">
<id>18</id>
<name>LDAP Injection</name>
<description>blah blah</description>
<source>NPM Advisories</source>
<url>https://www.npmjs.com/advisories/18</url>
</issue>
```
With ID being the number assigned by npm (though you'll note that the
link is a redirect to a CVE in the GitHub Advisory Database) and name
the title of the CVE.
The example in the same comment for CVE on NVD sets id and name to the
CVE full number, so I guess this checks out with what's implemented in
this patch, so
Reviewed-by: Quentin Schulz <quentin.schulz@cherry.de>
Thanks!
Quentin
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves
2026-04-09 13:22 ` Quentin Schulz via buildroot
@ 2026-04-09 20:24 ` Thomas Perale via buildroot
0 siblings, 0 replies; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-04-09 20:24 UTC (permalink / raw)
To: Quentin Schulz; +Cc: Thomas Perale, buildroot
Hi Quentin,
Thanks for taking the time looking at this series. Really helpful to discuss
the new features.
> Are you aware of tools that were broken before this? Would be nice for the commit log :)
None that I'm aware, I just found it odd that the `id` property wasn't used
when adding support for it over security.buildroot.org and gave it a second
look.
> My opinion is this isn't v1.7-specific but rather a "fix" or "improvement" that we don't necessarily need to have in this series. What do you think of having this as a separate patch (or at the beginning of the series so the series can be applied partially easily?).?
Sure, good idea.
> Honestly strange we have id and name for seemingly the same thing. The introducing commit was 2512eb835a46 ("#21 - Added support for patches and unit tests for JSON schema") for both and https://github.com/CycloneDX/specification/issues/21 doesn't shine a light of why for either. The only hint is https://github.com/CycloneDX/specification/issues/21#issuecomment-625104801 where
>
> ```
> <issue type="[ defect | enhancement | security ]" ref="7bc2d01f-a0e8-4ae2-a274-7bd188f89926">
> <id>18</id>
> <name>LDAP Injection</name>
> <description>blah blah</description>
> <source>NPM Advisories</source>
> <url>https://www.npmjs.com/advisories/18</url>
> </issue>
> ```
>
> With ID being the number assigned by npm (though you'll note that the link is a redirect to a CVE in the GitHub Advisory Database) and name the title of the CVE.
>
> The example in the same comment for CVE on NVD sets id and name to the CVE full number, so I guess this checks out with what's implemented in this patch, so
I think you could also interpret it outside of the scope security
vulnerabilities. If a pedigree resolve something like a JIRA ticket for
instance. The `id` is the ID of the ticket on JIRA while the name is the title
probably ? That is how I interpret it at least.
PERALE Thomas
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply [flat|nested] 20+ messages in thread
* [Buildroot] [PATCH v5 8/8] utils/generate-cyclonedx: split vulnerabilities per state
2026-03-11 14:04 [Buildroot] [PATCH v5 0/8] Support CycloneDX v1.7 Thomas Perale via buildroot
` (6 preceding siblings ...)
2026-03-11 14:04 ` [Buildroot] [PATCH v5 7/8] utils/generate-cyclonedx: add 'id' property to resolves Thomas Perale via buildroot
@ 2026-03-11 14:04 ` Thomas Perale via buildroot
7 siblings, 0 replies; 20+ messages in thread
From: Thomas Perale via buildroot @ 2026-03-11 14:04 UTC (permalink / raw)
To: buildroot; +Cc: Thomas Perale
If a vulnerability is present in multiple components and only one of
them has a patch that addresses that vulnerability, all components will
currently have that vulnerability marked as `resolved_with_pedigree`.
Right now, this issue does not affect any packages, but in the future it
might affect packages that provide multiple version options (e.g.
gnupg and gnupg2).
There is a small chance this happens in a real-world use case, but it
may occur in the context of maintenance when running
`make show-info-all`.
This commit changes how vulnerabilities fixed by a patch are stored.
Instead of placing all vulnerabilities into a single set, it now keeps an
index of the component for which the patch has been applied.
When generating the vulnerability list, the component reference is
checked instead of only the vulnerability ID. If a vulnerability ID has
multiple states for different references, multiple vulnerability
entries are created with distinct analyses.
Consider the hypothetical case where gnupg ignores a vulnerability
because it was introduced in a later version, while gnupg2 has patched
that vulnerability. This would result in the following JSON:
```json
[
{
"id": "CVE-1234-1234",
"analysis": {
"state": "in_triage",
"detail": "The CVE 'CVE-1234-1234' has been marked as ignored by Buildroot"
},
"affects": [
{"ref": "gnupg"}
]
},
{
"id": "CVE-1234-1234",
"analysis": {
"state": "resolved_with_pedigree",
"detail": "The CVE 'CVE-1234-1234' has been marked as ignored by Buildroot"
},
"affects": [
{"ref": "gnupg2"}
]
}
]
```
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
---
utils/generate-cyclonedx | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index 43167d12d0..f4e0e8b539 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -249,10 +249,10 @@ class CycloneDX:
_spdx_licenses: list = field(default_factory=list)
"""List of supported SPDX license expression. Initialized during the
`__post_init__` step, fetched either online or from local copy"""
- _vuln_with_pedigree: set = field(default_factory=set)
- """Set of vulnerabilities that were addressed by a patch present in
- buildroot tree. This set is used to set the analysis of the ignored CVEs to
- 'resolved_with_pedigree'."""
+ _vuln_with_pedigree: dict = field(default_factory=dict)
+ """Index of vulnerabilities per component ref that were addressed by a
+ patch present in buildroot tree. This index is used to set the analysis of
+ the ignored CVEs to 'resolved_with_pedigree'."""
def __post_init__(self):
self._spdx_licenses = br2_retrieve_spdx_licenses(self.options.cyclonedx_version)
@@ -297,11 +297,12 @@ class CycloneDX:
]
}
- def _component_patches(self, patch_list: list[str]):
+ def _component_patches(self, comp_ref: str, patch_list: list[str]):
"""Translate a list of patches from the show-info JSON to a list of
patches in CycloneDX format.
Args:
+ comp_ref (str): Reference of the component the patches are part of.
patch_list (list): Array of patch relative paths for a given component.
Returns:
@@ -324,7 +325,7 @@ class CycloneDX:
issue = {}
cves = extract_cves_from_header(header)
if cves:
- self._vuln_with_pedigree.update(cves)
+ self._vuln_with_pedigree.setdefault(comp_ref, set()).update(cves)
issue = {
"resolves": [
{
@@ -387,7 +388,7 @@ class CycloneDX:
**({
"cpe": comp["cpe-id"],
} if "cpe-id" in comp else {}),
- **(self._component_patches(comp["patches"]) if comp.get("patches") else {}),
+ **(self._component_patches(name, comp["patches"]) if comp.get("patches") else {}),
"properties": [{
"name": "BR_TYPE",
"value": comp["type"],
@@ -421,22 +422,31 @@ class CycloneDX:
Returns:
list: Solved vulnerabilities list in CycloneDX format.
"""
+ # This will generate an index of affected component per
+ # (<cve-id>, <analysis-state>) tuple.
+ # In the case a CVE have different analysis based on the package they
+ # affects this will enable to create multiple entries with the same
+ # vulnerability id.
cves = {}
for name, comp in self._filtered_show_info_dict.items():
for cve in comp.get('ignore_cves', []):
- cves.setdefault(cve, []).append(name)
+ state = "in_triage"
+ if cve in self._vuln_with_pedigree.get(name, {}):
+ state = "resolved_with_pedigree"
+
+ cves.setdefault((cve, state), []).append(name)
return [{
"id": cve,
"analysis": {
- "state": "resolved_with_pedigree" if cve in self._vuln_with_pedigree else "in_triage",
+ "state": state,
"detail": f"The CVE '{cve}' has been marked as ignored by Buildroot"
},
"affects": [
{"ref": bomref} for bomref in components
]
- } for cve, components in cves.items()]
+ } for (cve, state), components in cves.items()]
@property
def cyclonedx(self):
--
2.53.0
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
^ permalink raw reply related [flat|nested] 20+ messages in thread