diff --git a/model/src/main/kotlin/licenses/LicenseInfoResolver.kt b/model/src/main/kotlin/licenses/LicenseInfoResolver.kt index d95deea1cc3f5..a7a3d57287895 100644 --- a/model/src/main/kotlin/licenses/LicenseInfoResolver.kt +++ b/model/src/main/kotlin/licenses/LicenseInfoResolver.kt @@ -83,6 +83,10 @@ class LicenseInfoResolver( licenseInfo.concludedLicenseInfo.concludedLicense?.also { originalExpressions += ResolvedOriginalExpression(expression = it, source = LicenseSource.CONCLUDED) } + + licenseInfo.declaredLicenseInfo.authors.takeIf { it.isNotEmpty() && addAuthorsToCopyrights }?.also { + locations += resolveCopyrightFromAuthors(it) + } } } @@ -98,23 +102,7 @@ class LicenseInfoResolver( }.keys licenseInfo.declaredLicenseInfo.authors.takeIf { it.isNotEmpty() && addAuthorsToCopyrights }?.also { - locations += ResolvedLicenseLocation( - provenance = UnknownProvenance, - location = UNDEFINED_TEXT_LOCATION, - appliedCuration = null, - matchingPathExcludes = emptyList(), - copyrights = it.mapTo(mutableSetOf()) { author -> - val statement = "Copyright (C) $author".takeUnless { - author.contains("Copyright", ignoreCase = true) - } ?: author - - ResolvedCopyrightFinding( - statement = statement, - location = UNDEFINED_TEXT_LOCATION, - matchingPathExcludes = emptyList() - ) - } - ) + locations += resolveCopyrightFromAuthors(it) } } } @@ -287,6 +275,25 @@ class LicenseInfoResolver( return ResolvedLicenseFileInfo(id, licenseFiles) } + + private fun resolveCopyrightFromAuthors(authors: Set): ResolvedLicenseLocation = + ResolvedLicenseLocation( + provenance = UnknownProvenance, + location = UNDEFINED_TEXT_LOCATION, + appliedCuration = null, + matchingPathExcludes = emptyList(), + copyrights = authors.mapTo(mutableSetOf()) { author -> + val statement = "Copyright (C) $author".takeUnless { + author.contains("Copyright", ignoreCase = true) + } ?: author + + ResolvedCopyrightFinding( + statement = statement, + location = UNDEFINED_TEXT_LOCATION, + matchingPathExcludes = emptyList() + ) + } + ) } private class ResolvedLicenseBuilder(val license: SpdxSingleLicenseExpression) { diff --git a/model/src/test/kotlin/licenses/LicenseInfoResolverTest.kt b/model/src/test/kotlin/licenses/LicenseInfoResolverTest.kt index db6774f723377..1ac97552015fe 100644 --- a/model/src/test/kotlin/licenses/LicenseInfoResolverTest.kt +++ b/model/src/test/kotlin/licenses/LicenseInfoResolverTest.kt @@ -589,6 +589,45 @@ class LicenseInfoResolverTest : WordSpec({ result should containCopyrightStatementsForLicenseExactly("LicenseRef-a") result should containCopyrightStatementsForLicenseExactly("LicenseRef-b") } + + "resolve copyrights from authors in concluded license" { + // In case of a concluded license (due to a package curation) verify that the authors named + // in the package curation are added as copyright statement under the concluded license + val licenseInfos = listOf( + createLicenseInfo( + id = pkgId, + // In the curation file, authors might or might not have a "Copyright" prefix. + authors = setOf("Copyright (C) 2024 The Author", "The Other Author"), + declaredLicenses = setOf("MIT"), + concludedLicense = SpdxExpression.parse("BSD-2-Clause") + ) + ) + + val resolver = createResolver(licenseInfos, addAuthorsToCopyrights = true) + + val result = resolver.resolveLicenseInfo(pkgId) + result should containCopyrightStatementsForLicenseExactly( + "BSD-2-Clause", + // A "Copyright" prefix is added to the author (if it did not already exist) + "Copyright (C) 2024 The Author", "Copyright (C) The Other Author" + ) + } + + "not resolve copyrights from authors in concluded license if disabled" { + val licenseInfos = listOf( + createLicenseInfo( + id = pkgId, + authors = authors, + declaredLicenses = setOf("MIT"), + concludedLicense = SpdxExpression.parse("BSD-2-Clause") + ) + ) + + val resolver = createResolver(licenseInfos, addAuthorsToCopyrights = false) + + val result = resolver.resolveLicenseInfo(pkgId) + result should containCopyrightStatementsForLicenseExactly("BSD-2-Clause") + } } "resolveLicenseFiles()" should {