SERVER-107852 SBOM rebuild to v8.2 (#39929)

GitOrigin-RevId: aec26739ea31c57e8d56d4b0f23f61ad339c6ae6
This commit is contained in:
Jason Hills 2025-09-08 09:52:00 -04:00 committed by MongoDB Bot
parent c3f463cc86
commit 4e07a389ae
25 changed files with 1753 additions and 595 deletions

View File

@ -40,6 +40,9 @@ version_expansions.yml
# Ignore all formatting in third_party/*
src/third_party
# this file is automatically generated and conforms to formatting requirements
README.third_party.md
# Ignore anything in the build output directories
build
bazel-*

View File

@ -21,132 +21,138 @@ not authored by MongoDB, and has a license which requires reproduction,
a notice will be included in
`THIRD-PARTY-NOTICES`.
| Name | License | Vendored Version | Emits persisted data | Distributed in Release Binaries |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------- | -------------------- | ------------------------------- |
| [Abseil] | Apache-2.0 | 20250512.1 | | ✗ |
| [arximboldi/immer] | BSL-1.0 | Unknown | | ✗ |
| [Asio C++ Library] | BSL-1.0 | 1.12.2 | | ✗ |
| [aws-sdk - the AWS SDK client library] | Apache-2.0 | 1.11.471 | | ✗ |
| [benchmark] | Apache-2.0 | v1.5.2 | | |
| [Boost C++ Libraries - boost] | BSL-1.0 | 1.88.0 | | ✗ |
| [c-ares] | MIT | 1.27.0 | | ✗ |
| [concurrencytest] | GPL-3.0-or-later | 0.1.2 | unknown | |
| [Cyrus SASL] | BSD-Attribution-HPND-disclaimer | 2.1.28 | unknown | |
| [dcleblanc/SafeInt] | MIT | 3.0.26 | | ✗ |
| [derickr/timelib] | MIT | 2022.13 | | ✗ |
| [discover] | BSD-3-Clause | 0.4.0 | unknown | |
| [fmtlib/fmt] | MIT | 11.1.3 | | ✗ |
| [folly] | Apache-2.0 | v2025.04.21.00 | | ✗ |
| [google-re2] | BSD-3-Clause | 2023-11-01 | | ✗ |
| [google-snappy] | BSD-3-Clause | 1.1.10 | ✗ | ✗ |
| [google/s2geometry] | Apache-2.0 | Unknown | ✗ | ✗ |
| [gperftools] | BSD-3-Clause | 2.9.1 | | ✗ |
| [grpc] | Apache-2.0 | 1.59.5 | | ✗ |
| [ICU for C/C++ (ICU4C)] | BSD-3-Clause, MIT v2 with Ad Clause License, Public Domain, BSD-2-Clause | 57.1 | ✗ | ✗ |
| [Intel Decimal Floating-Point Math Library] | BSD-3-Clause | v2.0 U1 | | ✗ |
| [jbeder/yaml-cpp] | MIT | 0.6.3 | | ✗ |
| [JSON-Schema-Test-Suite] | Unknown License | Unknown | | |
| [libmongocrypt] | Apache-2.0 | 1.14.0 | ✗ | ✗ |
| [librdkafka - the Apache Kafka C/C++ client library] | BSD-3-Clause, Xmlproc License, ISC, MIT, Public Domain, Zlib, BSD-2-Clause, Andreas Stolcke License | 2.0.2 | | ✗ |
| [LibTomCrypt] | WTFPL, Public Domain | 1.18.2 | ✗ | ✗ |
| [libunwind/libunwind] | MIT | v1.8.1 | | ✗ |
| [linenoise] | BSD-2-Clause | Unknown | | ✗ |
| [MongoDB C Driver] | Apache-2.0 | 1.28.1 | ✗ | ✗ |
| [Mozilla Firefox] | MPL-2.0 | 128.11.0esr | unknown | ✗ |
| [nlohmann-json] | MIT | 3.11.3 | ✗ | |
| [nlohmann.json.decomposed] | MIT | 3.10.5 | unknown | |
| [node] | ISC | 22.1.0 | unknown | |
| [ocspbuilder] | MIT | 0.10.2 | | |
| [ocspresponder] | Apache-2.0 | 0.5.0 | | |
| [opentelemetry-cpp] | Apache-2.0 | 1.17 | ✗ | |
| [opentelemetry-proto] | Apache-2.0 | 1.3.2 | ✗ | |
| [PCRE2] | BSD-3-Clause, Public Domain | 10.40 | | ✗ |
| [Protobuf] | BSD-3-Clause | v4.25.0 | | ✗ |
| [pyiso8601] | MIT | 2.1.0 | unknown | |
| [RoaringBitmap/CRoaring] | Unknown License | v3.0.1 | | ✗ |
| [SchemaStore/schemastore] | Apache-2.0 | Unknown | | |
| [smhasher] | Unknown License | Unknown | unknown | ✗ |
| [Snowball Stemming Algorithms] | BSD-3-Clause | 7b264ffa0f767c579d052fd8142558dc8264d795 | ✗ | ✗ |
| [subunit] | BSD-3-Clause, Apache-2.0 | 1.4.4 | unknown | |
| [tcmalloc] | Apache-2.0 | 20230227-snapshot-093ba93c | | ✗ |
| [testing-cabal/extras] | MIT | 0.0.3 | unknown | |
| [testscenarios] | BSD-3-Clause, Apache-2.0 | 0.4 | unknown | |
| [testtools] | MIT | 2.7.1 | unknown | |
| [unicode-data] | Unicode-DFS-2016 | 8.0 | ✗ | ✗ |
| [valgrind] | GPL-2.0-or-later | Unknown | | ✗ |
| [zlib] | Zlib | v1.3.1 | ✗ | ✗ |
| [zstd] | BSD-3-Clause, GPL-2.0-or-later | 1.5.5 | ✗ | ✗ |
| Name | License | Vendored Version | Emits persisted data | Distributed in Release Binaries |
| ---------------------------------------------------- | --------------------------------- | ---------------------------------------- | -------------------- | ------------------------------- |
| [Abseil Common Libraries (C++)] | Apache-2.0 | 20250512.1 | | ✗ |
| [Asio C++ Library] | BSL-1.0 | 1.12.2 | | ✗ |
| [AWS SDK for C++] | Apache-2.0 | 1.11.471 | | ✗ |
| [benchmark] | Apache-2.0 | v1.5.2 | | |
| [Boost C++ Libraries] | BSL-1.0 | 1.88.0 | | ✗ |
| [c-ares] | MIT | 1.27.0 | | ✗ |
| [CRoaring] | Apache-2.0 OR MIT | 3.0.1 | | ✗ |
| [Cyrus SASL] | BSD-Attribution-HPND-disclaimer | 2.1.28 | | |
| [fmt] | MIT | 11.1.3 | | ✗ |
| [github.com/facebook/folly] | Apache-2.0 | v2025.04.21.00 | | ✗ |
| [gperftools] | BSD-3-Clause | 2.9.1 | | ✗ |
| [gRPC (C++)] | Apache-2.0 | 1.59.5 | | ✗ |
| [immer] | BSL-1.0 | 0.8.0 | | ✗ |
| [Intel® Decimal Floating-Point Math Library] | BSD-3-Clause | v2.0U1 | | ✗ |
| [International Components for Unicode C/C++ (ICU4C)] | Unicode-3.0 | 57.1 | ✗ | ✗ |
| [JSON Schema Store] | Apache-2.0 | 6847cfc3a17a04a7664474212db50c627e1e3408 | | |
| [JSON-Schema-Test-Suite] | MIT | 728066f9c5c258ba3b1804a22a5b998f2ec77ec0 | | |
| [libmongocrypt] | Apache-2.0 | 1.14.0 | ✗ | ✗ |
| [librdkafka - The Apache Kafka C/C++ library] | BSD-2-Clause | 2.0.2 | | ✗ |
| [LibTomCrypt] | Unlicense | 1.18.2 | ✗ | ✗ |
| [libunwind] | MIT | v1.8.1 | | ✗ |
| [linenoise] | BSD-2-Clause | 6cdc775807e57b2c3fd64bd207814f8ee1fe35f3 | | ✗ |
| [MongoDB C Driver] | Apache-2.0 | 1.28.1 | ✗ | ✗ |
| [Mozilla Firefox ESR] | MPL-2.0 | 128.11.0esr | | ✗ |
| [MurmurHash3] | Public Domain | a6bd3ce7be8ad147ea820a7cf6229a975c0c96bb | | ✗ |
| [nlohmann/json] | MIT | 3.10.5 | | |
| [nlohmann/json] | MIT | 3.11.3 | ✗ | |
| [node] | ISC | 22.1.0 | | |
| [opentelemetry-cpp] | Apache-2.0 | 1.17 | ✗ | |
| [opentelemetry-proto] | Apache-2.0 | 1.3.2 | ✗ | |
| [PCRE2 - Perl-Compatible Regular Expressions] | BSD-3-Clause WITH PCRE2-exception | 10.40 | | ✗ |
| [Protobuf] | BSD-3-Clause | v4.25.0 | | ✗ |
| [pypi/asn1crypto] | MIT | 1.5.1 | | |
| [pypi/concurrencytest] | GPL-3.0-or-later | 0.1.2 | | |
| [pypi/discover] | BSD-3-Clause | 0.4.0 | | |
| [pypi/extras] | MIT | 0.0.3 | | |
| [pypi/iso8601] | MIT | 2.1.0 | | |
| [pypi/ocspbuilder] | MIT | 0.10.2 | | |
| [pypi/ocspresponder] | Apache-2.0 | 0.5.0 | | |
| [pypi/oscrypto] | MIT | 1.3.0 | | |
| [pypi/python-subunit] | (Apache-2.0 OR BSD-3-Clause) | 1.4.4 | | |
| [pypi/testscenarios] | BSD-3-Clause | 0.4 | | |
| [pypi/testtools] | MIT | 2.7.1 | | |
| [re2] | BSD-3-Clause | 2023-11-01 | | ✗ |
| [S2 Geometry Library] | Apache-2.0 | c872048da5d1 | ✗ | ✗ |
| [SafeInt] | MIT | 3.0.26 | | ✗ |
| [snappy] | BSD-3-Clause | 1.1.10 | ✗ | ✗ |
| [Snowball Stemming Algorithms (libstemmer)] | BSD-3-Clause | 7b264ffa0f767c579d052fd8142558dc8264d795 | ✗ | ✗ |
| [tcmalloc] | Apache-2.0 | 093ba93c1bd6dca03b0a8334f06d01b019244291 | | ✗ |
| [timelib] | MIT | 2022.13 | | ✗ |
| [Unicode Character Database] | Unicode-DFS-2016 | 8.0.0 | ✗ | ✗ |
| [valgrind.h] | BSD-4-Clause | 3.17.0 | | ✗ |
| [WiredTiger] | GPL-2.0-only OR GPL-3.0-only | mongodb-8.2 | ✗ | ✗ |
| [yaml-cpp] | MIT | 0.6.3 | | ✗ |
| [zlib] | Zlib | 1.3.1 | ✗ | ✗ |
| [Zstandard (zstd)] | BSD-3-Clause OR GPL-2.0-only | 1.5.5 | ✗ | ✗ |
[Abseil]: https://github.com/abseil/abseil-cpp
[AWS SDK for C++]: https://github.com/aws/aws-sdk-cpp
[Abseil Common Libraries (C++)]: https://github.com/abseil/abseil-cpp
[Asio C++ Library]: https://github.com/chriskohlhoff/asio
[Boost C++ Libraries - boost]: http://www.boost.org/
[Boost C++ Libraries]: http://www.boost.org/
[CRoaring]: https://github.com/RoaringBitmap/CRoaring
[Cyrus SASL]: https://www.cyrusimap.org/sasl/
[ICU for C/C++ (ICU4C)]: http://site.icu-project.org/download/
[Intel Decimal Floating-Point Math Library]: https://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library
[Intel® Decimal Floating-Point Math Library]: https://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library
[International Components for Unicode C/C++ (ICU4C)]: http://site.icu-project.org/download/
[JSON Schema Store]: https://www.schemastore.org/json/
[JSON-Schema-Test-Suite]: https://github.com/json-schema-org/JSON-Schema-Test-Suite
[LibTomCrypt]: https://github.com/libtom/libtomcrypt/releases
[MongoDB C Driver]: https://github.com/mongodb/mongo-c-driver
[Mozilla Firefox]: https://www.mozilla.org/en-US/security/known-vulnerabilities/firefox-esr
[PCRE2]: http://www.pcre.org/
[Mozilla Firefox ESR]: https://www.mozilla.org/en-US/security/known-vulnerabilities/firefox-esr
[MurmurHash3]: https://github.com/aappleby/smhasher/blob/a6bd3ce/
[PCRE2 - Perl-Compatible Regular Expressions]: http://www.pcre.org/
[Protobuf]: https://github.com/protocolbuffers/protobuf
[RoaringBitmap/CRoaring]: https://github.com/RoaringBitmap/CRoaring
[SchemaStore/schemastore]: https://www.schemastore.org/json/
[Snowball Stemming Algorithms]: https://github.com/snowballstem/snowball
[arximboldi/immer]: https://github.com/arximboldi/immer
[aws-sdk - the AWS SDK client library]: https://github.com/aws/aws-sdk-cpp
[S2 Geometry Library]: https://github.com/google/s2geometry
[SafeInt]: https://github.com/dcleblanc/SafeInt
[Snowball Stemming Algorithms (libstemmer)]: https://github.com/snowballstem/snowball
[Unicode Character Database]: http://www.unicode.org/versions/enumeratedversions.html
[WiredTiger]: https://source.wiredtiger.com/
[Zstandard (zstd)]: https://github.com/facebook/zstd
[benchmark]: https://github.com/google/benchmark
[c-ares]: https://c-ares.org/
[concurrencytest]: https://pypi.org/project/concurrencytest/
[dcleblanc/SafeInt]: https://github.com/dcleblanc/SafeInt
[derickr/timelib]: https://github.com/derickr/timelib
[discover]: https://pypi.org/project/discover/
[fmtlib/fmt]: http://fmtlib.net/
[folly]: https://github.com/facebook/folly
[google-re2]: https://github.com/google/re2
[google-snappy]: https://github.com/google/snappy/releases
[google/s2geometry]: https://github.com/google/s2geometry
[fmt]: http://fmtlib.net/
[gRPC (C++)]: https://github.com/grpc/grpc
[github.com/facebook/folly]: https://github.com/facebook/folly
[gperftools]: https://github.com/gperftools/gperftools
[grpc]: https://github.com/grpc/grpc
[jbeder/yaml-cpp]: https://github.com/jbeder/yaml-cpp/releases
[immer]: https://github.com/arximboldi/immer
[libmongocrypt]: https://github.com/mongodb/libmongocrypt
[librdkafka - the Apache Kafka C/C++ client library]: https://github.com/confluentinc/librdkafka
[libunwind/libunwind]: http://www.github.com/libunwind/libunwind
[librdkafka - The Apache Kafka C/C++ library]: https://github.com/confluentinc/librdkafka
[libunwind]: http://www.github.com/libunwind/libunwind
[linenoise]: https://github.com/antirez/linenoise
[nlohmann-json]: https://github.com/open-telemetry/opentelemetry-proto
[nlohmann.json.decomposed]: https://www.nuget.org/packages/nlohmann.json.decomposed
[nlohmann/json]: https://github.com/nlohmann/json
[nlohmann/json]: https://github.com/open-telemetry/opentelemetry-proto
[node]: https://nodejs.org/en/blog/release
[ocspbuilder]: https://github.com/wbond/ocspbuilder
[ocspresponder]: https://github.com/threema-ch/ocspresponder
[opentelemetry-cpp]: https://github.com/open-telemetry/opentelemetry-cpp/
[opentelemetry-proto]: https://github.com/open-telemetry/opentelemetry-proto
[pyiso8601]: https://pypi.org/project/iso8601/
[smhasher]: https://github.com/aappleby/smhasher/blob/a6bd3ce/
[subunit]: https://github.com/testing-cabal/subunit
[pypi/asn1crypto]: https://github.com/wbond/asn1crypto
[pypi/concurrencytest]: https://pypi.org/project/concurrencytest/
[pypi/discover]: https://pypi.org/project/discover/
[pypi/extras]: https://github.com/testing-cabal/extras
[pypi/iso8601]: https://pypi.org/project/iso8601/
[pypi/ocspbuilder]: https://github.com/wbond/ocspbuilder
[pypi/ocspresponder]: https://github.com/threema-ch/ocspresponder
[pypi/oscrypto]: https://github.com/wbond/oscrypto
[pypi/python-subunit]: https://github.com/testing-cabal/subunit
[pypi/testscenarios]: https://pypi.org/project/testscenarios/
[pypi/testtools]: https://github.com/testing-cabal/testtools
[re2]: https://github.com/google/re2
[snappy]: https://github.com/google/snappy/releases
[tcmalloc]: https://github.com/google/tcmalloc
[testing-cabal/extras]: https://github.com/testing-cabal/extras
[testscenarios]: https://pypi.org/project/testscenarios/
[testtools]: https://github.com/testing-cabal/testtools
[unicode-data]: http://www.unicode.org/versions/enumeratedversions.html
[valgrind]: http://valgrind.org/downloads/current.html
[timelib]: https://github.com/derickr/timelib
[valgrind.h]: http://valgrind.org/downloads/current.html
[yaml-cpp]: https://github.com/jbeder/yaml-cpp/releases
[zlib]: https://zlib.net/
[zstd]: https://github.com/facebook/zstd
## WiredTiger Vendored Test Libraries
The following Python libraries are transitively included by WiredTiger,
The following libraries are transitively included by WiredTiger,
and are used by that component for testing. They don't appear in
released binary artifacts.
| Name |
| ------------------------ |
| concurrencytest |
| discover |
| nlohmann.json.decomposed |
| pyiso8601 |
| subunit |
| testing-cabal/extras |
| testscenarios |
| testtools |
| Name |
| -------------------------- |
| nlohmann/json@3.10.5 |
| pypi/concurrencytest@0.1.2 |
| pypi/discover@0.4.0 |
| pypi/extras@0.0.3 |
| pypi/iso8601@2.1.0 |
| pypi/python-subunit@1.4.4 |
| pypi/testscenarios@0.4 |
| pypi/testtools@2.7.1 |
## Dynamically Linked Libraries

View File

@ -150,6 +150,10 @@ py_binary(
"jsonschema",
group = "build-metrics",
),
dependency(
"license-expression",
group = "lint",
),
],
)

View File

@ -5,6 +5,7 @@ import sys
from typing import List
import jsonschema
from license_expression import get_spdx_licensing
from referencing import Registry, Resource
BOM_SCHEMA_LOCATION = os.path.join("buildscripts", "tests", "sbom_linter", "bom-1.5.schema.json")
@ -32,6 +33,7 @@ MISSING_TEAM_ERROR = "Component must include a 'internal:team_responsible' prope
SCHEMA_MATCH_FAILURE = "File did not match the CycloneDX schema"
MISSING_VERSION_IN_SBOM_COMPONENT_ERROR = "Component must include a version."
MISSING_VERSION_IN_IMPORT_FILE_ERROR = "Missing version in the import file: "
MISSING_LICENSE_IN_SBOM_COMPONENT_ERROR = "Component must include a license."
COULD_NOT_FIND_OR_READ_SCRIPT_FILE_ERROR = "Could not find or read the import script file"
VERSION_MISMATCH_ERROR = "Version mismatch: "
@ -115,30 +117,48 @@ def strip_extra_prefixes(string_with_prefix: str) -> str:
return string_with_prefix.removeprefix("mongo/").removeprefix("v")
def validate_evidence(component: dict, third_party_libs: set, error_manager: ErrorManager) -> None:
if "evidence" not in component or "occurrences" not in component["evidence"]:
error_manager.append_full_error_message(MISSING_EVIDENCE_ERROR)
def validate_license(component: dict, error_manager: ErrorManager) -> None:
if "licenses" not in component:
error_manager.append_full_error_message(MISSING_LICENSE_IN_SBOM_COMPONENT_ERROR)
return
occurrences = component["evidence"]["occurrences"]
if not occurrences:
error_manager.append_full_error_message(
"'evidence.occurrences' field must include at least one location."
)
for occurrence in occurrences:
location = occurrence["location"]
valid_license = False
for license in component["licenses"]:
if "expression" in license:
expression = license.get("expression")
elif "license" in license:
if "id" in license["license"]:
# Should be a valid SPDX license ID
expression = license["license"].get("id")
elif "name" in license["license"]:
# If SPDX does not define the license used, the name field may be used to provide the license name
valid_license = True
if not os.path.exists(location) and not SKIP_FILE_CHECKING:
error_manager.append_full_error_message("location does not exist in repo.")
if not valid_license:
licensing_validate = get_spdx_licensing().validate(expression, validate=True)
# ExpressionInfo(
# original_expression='',
# normalized_expression='',
# errors=[],
# invalid_symbols=[]
# )
valid_license = not licensing_validate.errors or not licensing_validate.invalid_symbols
if not valid_license:
error_manager.append_full_error_message(licensing_validate)
return
if location.startswith(THIRD_PARTY_LOCATION_PREFIX):
lib = location.removeprefix(THIRD_PARTY_LOCATION_PREFIX)
if lib in third_party_libs:
third_party_libs.remove(lib)
def validate_evidence(component: dict, third_party_libs: set, error_manager: ErrorManager) -> None:
if component["scope"] == "required":
if "evidence" not in component or "occurrences" not in component["evidence"]:
error_manager.append_full_error_message(MISSING_EVIDENCE_ERROR)
return
validate_location(component, third_party_libs, error_manager)
def validate_properties(component: dict, error_manager: ErrorManager) -> None:
has_team_responsible_property = False
has_team_responsible_property = False or component["scope"] == "excluded"
script_path = ""
if "properties" in component:
for prop in component["properties"]:
@ -159,14 +179,26 @@ def validate_properties(component: dict, error_manager: ErrorManager) -> None:
if comp_version == "Unknown" or script_path == "":
return
# Include the .pedigree.descendants[0] version for version matching
if (
"pedigree" in component
and "descendants" in component["pedigree"]
and "version" in component["pedigree"]["descendants"][0]
):
comp_pedigree_version = component["pedigree"]["descendants"][0]["version"]
else:
comp_pedigree_version = ""
# At this point a version is attempted to be read from the import script file
script_version = get_script_version(script_path, "VERSION", error_manager)
if script_version == "":
error_manager.append_full_error_message(MISSING_VERSION_IN_IMPORT_FILE_ERROR + script_path)
elif strip_extra_prefixes(script_version) != strip_extra_prefixes(comp_version):
elif strip_extra_prefixes(script_version) != strip_extra_prefixes(
comp_version
) and strip_extra_prefixes(script_version) != strip_extra_prefixes(comp_pedigree_version):
error_manager.append_full_error_message(
VERSION_MISMATCH_ERROR
+ f"\nscript version:{script_version}\nsbom version:{comp_version}"
+ f"\nscript version:{script_version}\nsbom component version:{comp_version}\nsbom component pedigree version:{comp_pedigree_version}"
)
@ -174,15 +206,37 @@ def validate_component(component: dict, third_party_libs: set, error_manager: Er
error_manager.update_component_attribute(component["name"])
if "scope" not in component:
error_manager.append_full_error_message("component must include a scope.")
elif component["scope"] != "optional":
else:
validate_evidence(component, third_party_libs, error_manager)
validate_properties(component, error_manager)
validate_license(component, error_manager)
if "purl" not in component and "cpe" not in component:
error_manager.append_full_error_message(MISSING_PURL_CPE_ERROR)
error_manager.update_component_attribute("")
def validate_location(component: dict, third_party_libs: set, error_manager: ErrorManager) -> None:
if "evidence" in component:
if "occurrences" not in component["evidence"]:
error_manager.append_full_error_message(
"'evidence.occurrences' field must include at least one location."
)
occurrences = component["evidence"]["occurrences"]
for occurrence in occurrences:
if "location" in occurrence:
location = occurrence["location"]
if not os.path.exists(location) and not SKIP_FILE_CHECKING:
error_manager.append_full_error_message("location does not exist in repo.")
if location.startswith(THIRD_PARTY_LOCATION_PREFIX):
lib = location.removeprefix(THIRD_PARTY_LOCATION_PREFIX)
if lib in third_party_libs:
third_party_libs.remove(lib)
def lint_sbom(
input_file: str, output_file: str, third_party_libs: set, should_format: bool
) -> ErrorManager:
@ -257,8 +311,6 @@ def main() -> int:
)
# the only files in this dir that are not third party libs
third_party_libs.remove("scripts")
# wiredtiger will not be included in the sbom since it is considered part of the server
third_party_libs.remove("wiredtiger")
# the only files in the sasl dir are BUILD files to setup the sasl library in Windows
third_party_libs.remove("sasl")
error_manager = lint_sbom(input_file, output_file, third_party_libs, should_format)

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"name": "kafka",
"version": "",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"version": "v2.0.2",
"scope": "required",
"cpe": "test_cpe",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"properties": [
{
"name": "internal:team_responsible",

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
@ -16,6 +16,11 @@
"name": "kafka",
"version": "2.0.2",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{
@ -36,6 +41,13 @@
"name": "protobuf",
"version": "v4.25.0",
"scope": "required",
"licenses": [
{
"license": {
"id": "BSD-3-Clause"
}
}
],
"purl": "test_purl",
"properties": [
{
@ -58,6 +70,13 @@
"name": "unicode",
"version": "8.0",
"scope": "optional",
"licenses": [
{
"license": {
"id": "Unicode-DFS-2016"
}
}
],
"purl": "test_purl",
"properties": [
{

View File

@ -0,0 +1,43 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"version": "v2.0.2",
"licenses": [
{
"expression": "xBSD-3-Clause"
}
],
"scope": "required",
"cpe": "test_cpe",
"properties": [
{
"name": "internal:team_responsible",
"value": "server_security"
},
{
"name": "import_script_path",
"value": "buildscripts/tests/sbom_linter/inputs/kafka_valid_import.sh"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/librdkafka"
}
]
}
}
]
}

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"name": "kafka",
"scope": "required",
"cpe": "test_cpe",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"properties": [
{
"name": "internal:team_responsible",

View File

@ -0,0 +1,74 @@
{
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"version": "v2.0.2",
"scope": "required",
"cpe": "test_cpe",
"properties": [
{
"name": "internal:team_responsible",
"value": "server_security"
},
{
"name": "import_script_path",
"value": "buildscripts/tests/sbom_linter/inputs/kafka_valid_import.sh"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/librdkafka"
}
]
}
},
{
"type": "library",
"name": "protobuf",
"version": "v4.25.0",
"scope": "required",
"purl": "test_purl",
"properties": [
{
"name": "internal:team_responsible",
"value": "server_security"
},
{
"name": "import_script_path",
"value": "buildscripts/tests/sbom_linter/inputs/import_script_with_mongo_prefix_version.sh"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/protobuf"
}
]
}
},
{
"type": "library",
"name": "unicode",
"version": "8.0",
"scope": "optional",
"purl": "test_purl",
"properties": [
{
"name": "internal:team_responsible",
"value": "server_security"
}
]
}
]
}

View File

@ -2,17 +2,22 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -2,17 +2,22 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"evidence": {
"occurrences": [

View File

@ -2,17 +2,22 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -0,0 +1,51 @@
{
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"bom-ref": "pkg:github/aappleby/smhasher@a6bd3ce7be8ad147ea820a7cf6229a975c0c96bb",
"supplier": {
"name": "Austin Appleby"
},
"author": "Austin Appleby",
"group": "aappleby",
"name": "MurmurHash3",
"version": "a6bd3ce7be8ad147ea820a7cf6229a975c0c96bb",
"licenses": [
{
"license": {
"name": "Public Domain"
}
}
],
"copyright": "MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author hereby disclaims copyright to this source code.",
"purl": "pkg:github/aappleby/smhasher@a6bd3ce7be8ad147ea820a7cf6229a975c0c96bb",
"properties": [
{
"name": "internal:team_responsible",
"value": "Storage Execution"
},
{
"name": "info_link",
"value": "https://github.com/aappleby/smhasher/blob/a6bd3ce/"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/murmurhash3"
}
]
},
"scope": "required"
}
]
}

View File

@ -0,0 +1,51 @@
{
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"version": "v2.0.0",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"pedigree": {
"descendants": [
{
"type": "library",
"name": "kafka-fork",
"version": "v2.0.2"
}
]
},
"properties": [
{
"name": "internal:team_responsible",
"value": "server_security"
},
{
"name": "import_script_path",
"value": "buildscripts/tests/sbom_linter/inputs/kafka_valid_import.sh"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/kafka"
}
]
}
}
]
}

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"name": "kafka",
"version": "2.0.2",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"name": "kafka",
"version": "2.0.2",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -2,11 +2,11 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
@ -14,6 +14,11 @@
"name": "kafka",
"version": "v4.25.0",
"scope": "required",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"cpe": "test_cpe",
"properties": [
{

View File

@ -2,17 +2,22 @@
"properties": [
{
"name": "comment",
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.6/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
"value": "SBOM for MDB server product; this file should comply with the format specified here: https://cyclonedx.org/docs/1.5/json/#components_items_publisher; This file is still in development; see https://jira.mongodb.org/browse/DEVPROD-2623 for details."
}
],
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "kafka",
"version": "v2.0.2",
"licenses": [
{
"expression": "BSD-3-Clause"
}
],
"scope": "required",
"cpe": "test_cpe",
"properties": [
@ -38,6 +43,13 @@
"name": "protobuf",
"version": "v4.25.0",
"scope": "required",
"licenses": [
{
"license": {
"id": "BSD-3-Clause"
}
}
],
"purl": "test_purl",
"properties": [
{
@ -61,6 +73,13 @@
"type": "library",
"name": "unicode",
"version": "8.0",
"licenses": [
{
"license": {
"id": "Unicode-DFS-2016"
}
}
],
"scope": "optional",
"purl": "test_purl",
"properties": [

View File

@ -103,6 +103,14 @@ class TestSbom(unittest.TestCase):
error_manager = sbom_linter.lint_sbom(test_file, test_file, third_party_libs, False)
self.assert_message_in_errors(error_manager, sbom_linter.VERSION_MISMATCH_ERROR)
def test_pedigree_version_match(self):
test_file = os.path.join(self.input_dir, "sbom_pedigree_version_match.json")
third_party_libs = {"kafka"}
error_manager = sbom_linter.lint_sbom(test_file, test_file, third_party_libs, False)
if not error_manager.zero_error():
error_manager.print_errors()
self.assertTrue(error_manager.zero_error())
def test_schema_match_failure(self):
test_file = os.path.join(self.input_dir, "sbom_component_name_missing.json")
third_party_libs = {"librdkafka"}
@ -116,3 +124,26 @@ class TestSbom(unittest.TestCase):
self.assert_message_in_errors(
error_manager, sbom_linter.MISSING_VERSION_IN_SBOM_COMPONENT_ERROR
)
def test_missing_license(self):
test_file = os.path.join(self.input_dir, "sbom_missing_license.json")
third_party_libs = {"librdkafka"}
error_manager = sbom_linter.lint_sbom(test_file, test_file, third_party_libs, False)
self.assert_message_in_errors(
error_manager, sbom_linter.MISSING_LICENSE_IN_SBOM_COMPONENT_ERROR
)
def test_invalid_license_expression(self):
test_file = os.path.join(self.input_dir, "sbom_invalid_license_expression.json")
third_party_libs = {"librdkafka"}
error_manager = sbom_linter.lint_sbom(test_file, test_file, third_party_libs, False)
# print(error_manager.errors)
self.assert_message_in_errors(error_manager, "ExpressionInfo")
def test_named_license(self):
test_file = os.path.join(self.input_dir, "sbom_named_license.json")
third_party_libs = {"murmurhash3"}
error_manager = sbom_linter.lint_sbom(test_file, test_file, third_party_libs, False)
if not error_manager.zero_error():
error_manager.print_errors()
self.assertTrue(error_manager.zero_error())

40
poetry.lock generated
View File

@ -96,6 +96,25 @@ files = [
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
]
[[package]]
name = "boolean-py"
version = "5.0"
description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL."
optional = false
python-versions = "*"
groups = ["lint"]
markers = "platform_machine != \"s390x\" and platform_machine != \"ppc64le\" or platform_machine == \"s390x\" or platform_machine == \"ppc64le\""
files = [
{file = "boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9"},
{file = "boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95"},
]
[package.extras]
dev = ["build", "twine"]
docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)", "sphinxcontrib-apidoc (>=0.3.0)"]
linting = ["black", "isort", "pycodestyle"]
testing = ["pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"]
[[package]]
name = "boto3"
version = "1.36.18"
@ -1718,6 +1737,25 @@ six = ">=1.7"
Twisted = "*"
"zope.interface" = "*"
[[package]]
name = "license-expression"
version = "30.4.4"
description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic."
optional = false
python-versions = ">=3.9"
groups = ["lint"]
markers = "platform_machine != \"s390x\" and platform_machine != \"ppc64le\" or platform_machine == \"s390x\" or platform_machine == \"ppc64le\""
files = [
{file = "license_expression-30.4.4-py3-none-any.whl", hash = "sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4"},
{file = "license_expression-30.4.4.tar.gz", hash = "sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd"},
]
[package.dependencies]
"boolean.py" = ">=4.0"
[package.extras]
dev = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "pytest (>=7.0.1)", "pytest-xdist (>=2)", "ruff", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)", "twine"]
[[package]]
name = "linkify-it-py"
version = "2.0.3"
@ -5459,4 +5497,4 @@ libdeps = ["cxxfilt", "eventlet", "flask", "flask-cors", "gevent", "lxml", "prog
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<4.0"
content-hash = "622c4368619483bbf23d0e5d482c6905d2e947952135e3b13f688fa9b748f825"
content-hash = "ef2e57da22cc4cd89c4839944accd7e412853dc56bcbb6410cb73769a6db6518"

View File

@ -120,6 +120,7 @@ tqdm = "*"
colorama = "^0.4.6"
evergreen-lint = "^0.1.10"
ruff = "^0.6.7"
license-expression = "^30.4.4"
[tool.poetry.group.modules_poc.dependencies]
codeowners = { version = "^0.8.0", markers = "platform_machine != 's390x' and platform_machine != 'ppc64le'" }

1554
sbom.json

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ $component_links
## WiredTiger Vendored Test Libraries
The following Python libraries are transitively included by WiredTiger,
The following libraries are transitively included by WiredTiger,
and are used by that component for testing. They don't appear in
released binary artifacts.

View File

@ -65,16 +65,23 @@ def sbom_to_component_chart(sbom: dict) -> list[list[str]]:
name = component["name"]
license_string = []
for lic in component["licenses"]:
for key in ["id", "name"]:
if key in lic["license"]:
license_string.append(lic["license"][key])
if "license" in lic:
for key in ["id", "name"]:
if key in lic["license"]:
license_string.append(lic["license"][key])
elif "expression" in lic:
license_string.append(lic["expression"])
license_string = ", ".join(license_string)
version = component["version"]
emits_persisted_data = "unknown"
for prop in component["properties"]:
k, v = prop["name"], prop["value"]
if k == "emits_persisted_data":
emits_persisted_data = ("", "")[v == "true"]
if component["scope"] == "excluded":
emits_persisted_data = ""
else:
emits_persisted_data = "unknown"
if "properties" in component:
for prop in component["properties"]:
k, v = prop["name"], prop["value"]
if k == "emits_persisted_data":
emits_persisted_data = ("", "")[v == "true"]
distributed_in_release_binaries = ("", "")[component["scope"] == "required"]
row = [
@ -109,7 +116,7 @@ def sbom_to_component_links_string(sbom: dict) -> list[list[str]]:
for component in components:
check_component_validity(component)
info_link = get_component_info_link(component)
bisect.insort(link_list, f"[{component['name'].replace('|','')}]: {info_link}")
bisect.insort(link_list, f"[{component['name'].replace('|', '')}]: {info_link}")
return "\n".join(link_list)
@ -123,7 +130,10 @@ def sbom_to_wiredtiger_chart(sbom: dict) -> list[list[str]]:
locations = get_component_locations(component)
for location in locations:
if location.startswith("src/third_party/wiredtiger/"):
bisect.insort(wiredtiger_chart, [component["name"].replace("|", "")])
bisect.insort(
wiredtiger_chart,
([component["name"].replace("|", "") + "@" + component["version"]]),
)
return wiredtiger_chart
@ -139,19 +149,22 @@ def check_component_validity(component) -> None:
def get_component_info_link(component) -> str:
name = component["name"]
links = []
for prop in component["properties"]:
k, v = prop["name"], prop["value"]
if k == "info_link":
links.append(v)
if len(links) != 1:
logging.warning("Warning: Expected 1 info_link for %s. Got %d:", name, len(links))
if len(links) > 1:
logging.warning(" ".join(links))
logging.warning("Using first link only.")
else:
logging.warning("Falling back to `purl` value: %s", component["purl"])
links.append(component["purl"])
return links[0]
if "properties" in component:
for prop in component["properties"]:
k, v = prop["name"], prop["value"]
if k == "info_link":
links.append(v)
if len(links) != 1:
logging.warning("Warning: Expected 1 info_link for %s. Got %d:", name, len(links))
if len(links) > 1:
logging.warning(" ".join(links))
logging.warning("Using first link only.")
else:
logging.warning("Falling back to `purl` value: %s", component["purl"])
links.append(component["purl"])
return links[0]
else:
return ""
def get_component_locations(component) -> list[str]: