From e8c54ffb3d1b92b7e8744824ca0f28fe0d19440d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 9 Apr 2026 10:14:01 +0200 Subject: [PATCH] utils/generate-cyclonedx: generate vcs externalReferences for source repos Some packages do not have a http/https download URL for a source tarball, but are acquired over a version control system like git. If so, add externalReferences of type "vcs" for such URLs. As most git repositories use a https:// transport that may not indicated the repository type, add a "comment" due to the lack of a better mechanism in CycloneDX. While the hashes are calculated over a tarball created locally, it still may be useful, so add them for "vcs" externalReferences as well. Signed-off-by: Martin Willi Acked-By: Thomas Perale Signed-off-by: Arnout Vandecappelle --- .../tests/utils/test_generate_cyclonedx.py | 30 ++++++++++++++++++- utils/generate-cyclonedx | 8 +++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/support/testing/tests/utils/test_generate_cyclonedx.py b/support/testing/tests/utils/test_generate_cyclonedx.py index 0a13fdb6ad..e6640fbd0d 100644 --- a/support/testing/tests/utils/test_generate_cyclonedx.py +++ b/support/testing/tests/utils/test_generate_cyclonedx.py @@ -147,6 +147,8 @@ class TestGenerateCycloneDX(unittest.TestCase): { "source": "foo-1.2.tar.gz", "uris": [ + "git+git://git.example.org/foo", + "svn+https://svn.example.org/foo", "https+https://sources.buildroot.net/foo", "http|https+https://mirror.example.org/foo", ], @@ -160,10 +162,20 @@ class TestGenerateCycloneDX(unittest.TestCase): self.assertEqual( foo["externalReferences"], [ + { + "type": "vcs", + "url": "git://git.example.org/foo", + "comment": "git repository", + }, + { + "type": "vcs", + "url": "https://svn.example.org/foo", + "comment": "svn repository", + }, { "type": "source-distribution", "url": "https://mirror.example.org/foo/foo-1.2.tar.gz", - }, + } ], ) @@ -183,6 +195,7 @@ class TestGenerateCycloneDX(unittest.TestCase): { "source": "foo-1.2.tar.gz", "uris": [ + "git+git://git.example.org/foo", "http|https+https://mirror.example.org/foo", ], }, @@ -194,6 +207,21 @@ class TestGenerateCycloneDX(unittest.TestCase): self.assertEqual( foo["externalReferences"], [ + { + "type": "vcs", + "url": "git://git.example.org/foo", + "comment": "git repository", + "hashes": [ + { + "alg": "SHA-256", + "content": "1111111111111111111111111111111111111111111111111111111111111111", + }, + { + "alg": "SHA-1", + "content": "2222222222222222222222222222222222222222", + }, + ] + }, { "type": "source-distribution", "url": "https://mirror.example.org/foo/foo-1.2.tar.gz", diff --git a/utils/generate-cyclonedx b/utils/generate-cyclonedx index 382d91ce55..4166abd9ff 100755 --- a/utils/generate-cyclonedx +++ b/utils/generate-cyclonedx @@ -325,6 +325,7 @@ def cyclonedx_external_refs(comp): dict: External reference information in CycloneDX format, or empty dict """ SOURCE_DIST_SCHEMES = {"http", "https"} + VCS_SCHEMES = {"git", "svn", "cvs", "hg", "bzr"} refs = [] for download in comp.get("downloads", []): @@ -336,6 +337,13 @@ def cyclonedx_external_refs(comp): "url": f"{uri}/{source}", **cyclonedx_source_hashes(comp, source), }) + elif set(schemes) & VCS_SCHEMES: + refs.append({ + "type": "vcs", + "url": uri, + "comment": f"{schemes[0]} repository", + **cyclonedx_source_hashes(comp, source), + }) if refs: return {"externalReferences": refs} return {}