All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Mattias Walström" <lazzer@gmail.com>
To: buildroot@buildroot.org
Cc: "Christian Stewart" <christian@aperture.us>,
	"Thomas Perale" <thomas.perale@mind.be>,
	"Mattias Walström" <lazzer@gmail.com>
Subject: [Buildroot] [PATCH 2/3] utils/generate-cyclonedx: add vendored dependencies as nested components
Date: Wed, 28 Oct 2026 11:32:47 +0100	[thread overview]
Message-ID: <20261028103248.1317597-3-lazzer@gmail.com> (raw)
In-Reply-To: <20261028103248.1317597-1-lazzer@gmail.com>

A fork swapped in by a replace directive lists the original module as
its ancestor, so scanners can still match advisories for it.

Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
 .../tests/utils/test_generate_cyclonedx.py    | 76 +++++++++++++++++++
 utils/generate-cyclonedx                      | 53 ++++++++++++-
 2 files changed, 127 insertions(+), 2 deletions(-)

diff --git a/support/testing/tests/utils/test_generate_cyclonedx.py b/support/testing/tests/utils/test_generate_cyclonedx.py
index 96b68beb40..37a2c1c673 100644
--- a/support/testing/tests/utils/test_generate_cyclonedx.py
+++ b/support/testing/tests/utils/test_generate_cyclonedx.py
@@ -150,6 +150,82 @@ class TestGenerateCycloneDX(unittest.TestCase):
         foo_deps = next(d for d in result["dependencies"] if d["ref"] == "package-foo")
         self.assertEqual(foo_deps["dependsOn"], ["package-bar", "skeleton-baz"])
 
+    def test_vendored_dependencies(self):
+        info = self._make_show_info()
+        info["package-foo"]["vendored_dependencies"] = [
+            {
+                "name": "example.org/lib",
+                "version": "v1.0.0",
+                "purl": "pkg:golang/example.org/lib@v1.0.0",
+            },
+            {
+                "name": "example.org/fork",
+                "version": "v1.1.0",
+                "purl": "pkg:golang/example.org/fork@v1.1.0",
+                "replaces": {
+                    "name": "example.org/upstream",
+                    "version": "v1.0.0",
+                    "purl": "pkg:golang/example.org/upstream@v1.0.0",
+                },
+            },
+        ]
+        info["package-bar"]["vendored_dependencies"] = [
+            {
+                "name": "example.org/lib",
+                "version": "v1.0.0",
+                "purl": "pkg:golang/example.org/lib@v1.0.0",
+            },
+        ]
+
+        result = self._run_script(show_info=info)
+
+        foo = self._find_component(result, "package-foo")
+        self.assertEqual(
+            foo["components"],
+            [
+                {
+                    "bom-ref": "package-foo:pkg:golang/example.org/lib@v1.0.0",
+                    "type": "library",
+                    "name": "example.org/lib",
+                    "version": "v1.0.0",
+                    "purl": "pkg:golang/example.org/lib@v1.0.0",
+                },
+                {
+                    "bom-ref": "package-foo:pkg:golang/example.org/fork@v1.1.0",
+                    "type": "library",
+                    "name": "example.org/fork",
+                    "version": "v1.1.0",
+                    "purl": "pkg:golang/example.org/fork@v1.1.0",
+                    "pedigree": {
+                        "ancestors": [
+                            {
+                                "type": "library",
+                                "name": "example.org/upstream",
+                                "version": "v1.0.0",
+                                "purl": "pkg:golang/example.org/upstream@v1.0.0",
+                            },
+                        ],
+                    },
+                },
+            ],
+        )
+
+        bar = self._find_component(result, "package-bar")
+        self.assertEqual(bar["components"][0]["bom-ref"], "package-bar:pkg:golang/example.org/lib@v1.0.0")
+
+        foo_deps = next(d for d in result["dependencies"] if d["ref"] == "package-foo")
+        self.assertEqual(
+            foo_deps["dependsOn"],
+            [
+                "package-bar",
+                "package-foo:pkg:golang/example.org/fork@v1.1.0",
+                "package-foo:pkg:golang/example.org/lib@v1.0.0",
+                "skeleton-baz",
+            ],
+        )
+
+        self.assertNotIn("components", self._find_component(result, "skeleton-baz"))
+
     def test_external_references(self):
         info = self._make_show_info()
         info["package-foo"]["downloads"] = [
diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx
index a79ca41fe1..d85aec5973 100755
--- a/utils/generate-cyclonedx
+++ b/utils/generate-cyclonedx
@@ -405,6 +405,7 @@ def cyclonedx_component(name, comp):
         } if "cpe-id" in comp else {}),
         **cyclonedx_external_refs(comp),
         **(cyclonedx_patches(name, comp["patches"]) if comp.get("patches") else {}),
+        **(cyclonedx_vendored(name, comp["vendored_dependencies"]) if comp.get("vendored_dependencies") else {}),
         "properties": [{
             "name": "BR_TYPE",
             "value": comp["type"],
@@ -412,6 +413,53 @@ def cyclonedx_component(name, comp):
     }
 
 
+def vendored_ref(name, dep):
+    """Return the bom-ref of a dependency vendored into a package.
+
+    Args:
+        name (str): Key used for the package in the show-info output.
+        dep (dict): Vendored dependency from the show-info output.
+
+    Returns:
+        str: bom-ref, unique even when several packages vendor the same dependency.
+    """
+    return f"{name}:{dep['purl']}"
+
+
+def cyclonedx_vendored(name, deps):
+    """Translate the vendored dependencies of a package to nested CycloneDX components.
+
+    A dependency swapped in for another one, e.g. a fork through a Go
+    replace directive, lists the original as its ancestor.
+
+    Args:
+        name (str): Key used for the package in the show-info output.
+        deps (list): Vendored dependencies from the show-info output.
+
+    Returns:
+        dict: Nested components in CycloneDX format.
+    """
+    def component(dep):
+        return {
+            "type": "library",
+            "name": dep["name"],
+            "version": dep["version"],
+            "purl": dep["purl"],
+        }
+
+    return {
+        "components": [{
+            "bom-ref": vendored_ref(name, dep),
+            **component(dep),
+            **({
+                "pedigree": {
+                    "ancestors": [component(dep["replaces"])],
+                },
+            } if "replaces" in dep else {}),
+        } for dep in deps],
+    }
+
+
 def cyclonedx_dependency(ref, depends):
     """Create JSON for dependency relationships between components.
 
@@ -600,8 +648,9 @@ def main():
         ],
         "dependencies": [
             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],
+            *[cyclonedx_dependency(ref, br2_parse_deps(ref, show_info_dict, args.virtual) +
+                                   [vendored_ref(ref, dep) for dep in comp.get("vendored_dependencies", [])])
+              for ref, comp in filtered_show_info_dict.items()],
         ],
         "vulnerabilities": cyclonedx_vulnerabilities(show_info_dict),
     }
-- 
2.43.0

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

  parent reply	other threads:[~2026-10-28 10:33 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 10:32 [Buildroot] [PATCH 0/3] Go: list vendored modules in show-info and the SBOM Mattias Walström
2026-10-28 10:32 ` [Buildroot] [PATCH 1/3] package/pkg-golang: record vendored Go modules in show-info Mattias Walström
2026-10-28 11:00   ` Thomas Perale via buildroot
2026-10-28 10:32 ` Mattias Walström [this message]
2026-10-28 10:32 ` [Buildroot] [PATCH 3/3] package/pkg-golang: vendor modules of local and overridden sources Mattias Walström

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261028103248.1317597-3-lazzer@gmail.com \
    --to=lazzer@gmail.com \
    --cc=buildroot@buildroot.org \
    --cc=christian@aperture.us \
    --cc=thomas.perale@mind.be \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.