diff --git a/include/vcpkg/base/contractual-constants.h b/include/vcpkg/base/contractual-constants.h index e637a800db..9c73988f70 100644 --- a/include/vcpkg/base/contractual-constants.h +++ b/include/vcpkg/base/contractual-constants.h @@ -128,6 +128,12 @@ namespace vcpkg inline constexpr StringLiteral SpdxDocumentNamespace = "documentNamespace"; inline constexpr StringLiteral SpdxDownloadLocation = "downloadLocation"; inline constexpr StringLiteral SpdxElementId = "spdxElementId"; + inline constexpr StringLiteral SpdxExternalRefs = "externalRefs"; + inline constexpr StringLiteral SpdxExternalReferenceCategory = "referenceCategory"; + inline constexpr StringLiteral SpdxExternalReferenceCategoryPackageManager = "PACKAGE_MANAGER"; + inline constexpr StringLiteral SpdxExternalReferenceLocator = "referenceLocator"; + inline constexpr StringLiteral SpdxExternalReferenceType = "referenceType"; + inline constexpr StringLiteral SpdxExternalReferenceTypePurl = "purl"; inline constexpr StringLiteral SpdxFileName = "fileName"; inline constexpr StringLiteral SpdxGeneratedFrom = "GENERATED_FROM"; inline constexpr StringLiteral SpdxGenerates = "GENERATES"; diff --git a/src/vcpkg-test/spdx.cpp b/src/vcpkg-test/spdx.cpp index 743e0207f2..bbe40322ac 100644 --- a/src/vcpkg-test/spdx.cpp +++ b/src/vcpkg-test/spdx.cpp @@ -106,7 +106,14 @@ TEST_CASE ("spdx maximum serialization", "[spdx]") "copyrightText": "NOASSERTION", "summary": "summary", "description": "description", - "comment": "This is the port (recipe) consumed by vcpkg." + "comment": "This is the port (recipe) consumed by vcpkg.", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:vcpkg/zlib@1.0?registry_url=git%3A%2F%2Fsome-vcs-url&port_version=5", + "referenceType": "purl" + } + ] }, { "name": "zlib:arm-uwp", @@ -247,7 +254,14 @@ TEST_CASE ("spdx minimum serialization", "[spdx]") "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", - "comment": "This is the port (recipe) consumed by vcpkg." + "comment": "This is the port (recipe) consumed by vcpkg.", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:vcpkg/zlib@1.0", + "referenceType": "purl" + } + ] }, { "name": "zlib:arm-uwp", @@ -366,7 +380,14 @@ TEST_CASE ("spdx concat resources", "[spdx]") "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", - "comment": "This is the port (recipe) consumed by vcpkg." + "comment": "This is the port (recipe) consumed by vcpkg.", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:vcpkg/zlib@1.0", + "referenceType": "purl" + } + ] }, { "name": "zlib:arm-uwp", diff --git a/src/vcpkg/spdx.cpp b/src/vcpkg/spdx.cpp index bcaba964b6..a621b344a8 100644 --- a/src/vcpkg/spdx.cpp +++ b/src/vcpkg/spdx.cpp @@ -195,6 +195,21 @@ std::string vcpkg::create_spdx_sbom(const InstallPlanAction& action, rel.insert(SpdxRelationshipType, SpdxContains); rel.insert(SpdxRelatedSpdxElement, fmt::format("SPDXRef-file-{}", i)); } + auto& external_refs = obj.insert(SpdxExternalRefs, Json::Array()); + auto& external_ref = external_refs.push_back(Json::Object()); + external_ref.insert(SpdxExternalReferenceCategory, SpdxExternalReferenceCategoryPackageManager); + external_ref.insert(SpdxExternalReferenceType, SpdxExternalReferenceTypePurl); + std::string purl_qualifiers = ""; + if (!scfl.spdx_location.empty()) + { + purl_qualifiers = Strings::concat("?registry_url=", Strings::percent_encode(scfl.spdx_location)); + if (cpgh.version.port_version > 0) + { + purl_qualifiers += fmt::format("&port_version={}", cpgh.version.port_version); + } + } + external_ref.insert(SpdxExternalReferenceLocator, + Strings::concat("pkg:vcpkg/", action.spec.name(), '@', cpgh.version.text, purl_qualifiers)); } { auto& obj = packages.push_back(Json::Object());