From cc41cc3fcdce839227c0d64e469b6e3fe43884bd Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 9 Apr 2026 10:13:57 +0200 Subject: [PATCH] utils/generate-cyclonedx: remove indirect dependencies from root component Commit dc4af8bfa979 ("utils/generate-cyclonedx: use direct dependencies") removes indirect dependencies from any listed component, as required by CycloneDX. The root component, however, still includes indirect dependencies, as it just takes the components from the show-info output. Fix this by collecting all component dependencies, and then filter the root component dependencies to include direct dependencies only. Signed-off-by: Martin Willi Acked-By: Thomas Perale Signed-off-by: Arnout Vandecappelle --- .../tests/utils/test_generate_cyclonedx.py | 3 +++ utils/generate-cyclonedx | 21 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/support/testing/tests/utils/test_generate_cyclonedx.py b/support/testing/tests/utils/test_generate_cyclonedx.py index ddad92598b..54250058a0 100644 --- a/support/testing/tests/utils/test_generate_cyclonedx.py +++ b/support/testing/tests/utils/test_generate_cyclonedx.py @@ -129,6 +129,9 @@ class TestGenerateCycloneDX(unittest.TestCase): bar_deps = next(d for d in result["dependencies"] if d["ref"] == "package-bar") self.assertEqual(bar_deps["dependsOn"], ["package-foo"]) + project_deps = next(d for d in result["dependencies"] if d["ref"] == "buildroot") + self.assertEqual(project_deps["dependsOn"], ["host-tool", "package-foo"]) + def test_virtual(self): result = self._run_script(["--virtual"]) diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx index 35198a47cf..f4d5afd847 100755 --- a/utils/generate-cyclonedx +++ b/utils/generate-cyclonedx @@ -385,6 +385,25 @@ def br2_parse_deps(ref, show_info_dict, virtual=False) -> list: return list(deps) +def br2_root_deps(show_info_dict, virtual=False) -> list: + """Retrieve the list of direct dependencies of the root component. + + This function returns all components that are not a dependency of any + other component. + + Args: + show_info_dict (dict): The JSON output of the show-info command. + virtual (bool): Whether to resolve virtual dependencies to their providers. + + Returns: + list: List of direct dependencies of the root component. + """ + indirect = set() + for ref in show_info_dict: + indirect.update(br2_parse_deps(ref, show_info_dict, virtual)) + return [ref for ref in show_info_dict if ref not in indirect] + + def main(): parser = argparse.ArgumentParser( description='''Create a CycloneDX SBoM for the Buildroot configuration. @@ -447,7 +466,7 @@ def main(): 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(args.project_name, br2_root_deps(filtered_show_info_dict, args.virtual)), *[cyclonedx_dependency(ref, br2_parse_deps(ref, show_info_dict, args.virtual)) for ref in filtered_show_info_dict], ],