From 065c48d85a0dd72cbec6333023cd3740c72c7ab7 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Fri, 22 Nov 2024 12:00:25 +0100 Subject: [PATCH] fix(model): Normalize purl name(space segments) The algorithm description at [1] demands to "apply type-specific normalization" to namespace segments and the name before applying percent-encoding. In general, type-specific requirements are documented at [2]. For Bazel the PR still pending, but in the current state lowercasing of the name should be performed [3]. [1]: https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#how-to-build-purl-string-from-its-components [2]: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst [3]: https://github.com/package-url/purl-spec/pull/317 Signed-off-by: Sebastian Schuberth --- model/src/main/kotlin/utils/PurlUtils.kt | 36 +++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/model/src/main/kotlin/utils/PurlUtils.kt b/model/src/main/kotlin/utils/PurlUtils.kt index f0b433cb77bd0..8e70865b6835c 100644 --- a/model/src/main/kotlin/utils/PurlUtils.kt +++ b/model/src/main/kotlin/utils/PurlUtils.kt @@ -29,35 +29,39 @@ import org.ossreviewtoolkit.utils.common.percentEncode /** * A subset of the Package URL types defined at https://github.com/package-url/purl-spec/blob/ad8a673/PURL-TYPES.rst. */ -enum class PurlType(private val value: String) { - APK("apk"), - BAZEL("bazel"), - BITBUCKET("bitbucket"), +enum class PurlType( + private val value: String, + val nameNormalization: (String) -> String = { it }, + val namespaceNormalization: (String) -> String = { it } +) { + APK("apk", { it.lowercase() }, { it.lowercase() }), + BAZEL("bazel", { it.lowercase() }), + BITBUCKET("bitbucket", { it.lowercase() }, { it.lowercase() }), BOWER("bower"), CARGO("cargo"), CARTHAGE("carthage"), COCOAPODS("cocoapods"), - COMPOSER("composer"), + COMPOSER("composer", { it.lowercase() }, { it.lowercase() }), CONAN("conan"), CONDA("conda"), CRAN("cran"), - DEBIAN("deb"), + DEBIAN("deb", { it.lowercase() }, { it.lowercase() }), DOCKER("docker"), DRUPAL("drupal"), GEM("gem"), GENERIC("generic"), - GITHUB("github"), + GITHUB("github", { it.lowercase() }, { it.lowercase() }), GITLAB("gitlab"), - GOLANG("golang"), + GOLANG("golang", { it.lowercase() }, { it.lowercase() }), HACKAGE("hackage"), HUGGING_FACE("huggingface"), MAVEN("maven"), MLFLOW("mlflow"), - NPM("npm"), + NPM("npm", { it.lowercase() }), NUGET("nuget"), - PUB("pub"), - PYPI("pypi"), - RPM("rpm"), + PUB("pub", { it.lowercase() }), + PYPI("pypi", { it.lowercase() }), + RPM("rpm", namespaceNormalization = { it.lowercase() }), SWIFT("swift"); init { @@ -112,11 +116,15 @@ internal fun createPurl( append('/') if (namespace.isNotEmpty()) { - append(namespace.trim('/').split('/').joinToString("/") { it.percentEncode() }) + append( + namespace.trim('/').split('/').joinToString("/") { segment -> + type.namespaceNormalization(segment).percentEncode() + } + ) append('/') } - append(name.trim('/').percentEncode()) + append(type.nameNormalization(name.trim('/')).percentEncode()) if (version.isNotEmpty()) { append('@')