Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions model/src/main/kotlin/Package.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ data class Package(
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
val authors: SortedSet<String> = sortedSetOf(),

/**
* The set of concluded copyright statements for this package. It can be used to override the [detected copyright
* statements][CopyrightFinding.statement] (note that there is no such thing as *declared* copyright statements
* because package managers do not support declaring them explicitly).
*
* ORT itself does not set this field, it needs to be set by the user using a [PackageCuration].
*/
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
val concludedCopyrights: SortedSet<String> = sortedSetOf(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was decided during PR grooming by the TSC that the current implementation is not sufficient even as a minimal solution. At a minimum, this set should be a map that associates license names with their copyright holder in order to not associate concluded copyrights to all licenses found in a package.


/**
* The set of licenses declared for this package. This does not necessarily correspond to the licenses as detected
* by a scanner. Both need to be taken into account for any conclusions.
Expand Down Expand Up @@ -138,6 +148,7 @@ data class Package(
id = Identifier.EMPTY,
purl = "",
authors = sortedSetOf(),
concludedCopyrights = sortedSetOf(),
declaredLicenses = sortedSetOf(),
declaredLicensesProcessed = ProcessedDeclaredLicense.EMPTY,
concludedLicense = null,
Expand Down
8 changes: 8 additions & 0 deletions model/src/main/kotlin/PackageCurationData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ data class PackageCurationData(
*/
val authors: SortedSet<String>? = null,

/**
* The set of concluded copyright statements for the package. It can be used to override the [detected copyright
* statements][CopyrightFinding.statement].
*/
val concludedCopyrights: SortedSet<String>? = null,

/**
* The concluded license as an [SpdxExpression]. It can be used to override the [declared][Package.declaredLicenses]
* / [detected][LicenseFinding.license] licenses of a package.
Expand Down Expand Up @@ -131,6 +137,7 @@ data class PackageCurationData(
purl = purl ?: original.purl,
cpe = cpe ?: original.cpe,
authors = authors ?: original.authors,
concludedCopyrights = concludedCopyrights ?: original.concludedCopyrights,
declaredLicenses = original.declaredLicenses,
declaredLicensesProcessed = declaredLicensesProcessed,
concludedLicense = concludedLicense ?: original.concludedLicense,
Expand Down Expand Up @@ -170,6 +177,7 @@ data class PackageCurationData(
purl = purl ?: other.purl,
cpe = cpe ?: other.cpe,
authors = (authors.orEmpty() + other.authors.orEmpty()).toSortedSet(),
concludedCopyrights = (concludedCopyrights.orEmpty() + other.concludedCopyrights.orEmpty()).toSortedSet(),
concludedLicense = setOfNotNull(concludedLicense, other.concludedLicense).reduce(SpdxExpression::and),
description = description ?: other.description,
homepageUrl = homepageUrl ?: other.homepageUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ class DefaultLicenseInfoProvider(
private fun createConcludedLicenseInfo(id: Identifier): ConcludedLicenseInfo =
ortResult.getPackage(id)?.let { (pkg, curations) ->
ConcludedLicenseInfo(
concludedCopyrights = pkg.concludedCopyrights,
concludedLicense = pkg.concludedLicense,
appliedCurations = curations.filter { it.curation.concludedLicense != null }
)
} ?: ConcludedLicenseInfo(concludedLicense = null, appliedCurations = emptyList())
} ?: ConcludedLicenseInfo(concludedCopyrights = null, concludedLicense = null, appliedCurations = emptyList())

private fun createDeclaredLicenseInfo(id: Identifier): DeclaredLicenseInfo =
ortResult.getProject(id)?.let { project ->
Expand Down
5 changes: 5 additions & 0 deletions model/src/main/kotlin/licenses/LicenseInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ data class LicenseInfo(
* Information about the concluded license of a package or project.
*/
data class ConcludedLicenseInfo(
/**
* The concluded copyright statements, or null if no copyrights were concluded.
*/
val concludedCopyrights: SortedSet<String>?,

/**
* The concluded license, or null if no license was concluded.
*/
Expand Down
16 changes: 16 additions & 0 deletions model/src/main/kotlin/licenses/LicenseInfoResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ class LicenseInfoResolver(
originalDeclaredLicenses += licenseInfo.declaredLicenseInfo.processed.mapped.filterValues {
it == license
}.keys

licenseInfo.concludedLicenseInfo.concludedCopyrights?.takeIf { it.isNotEmpty() }?.also {
locations += ResolvedLicenseLocation(
provenance = UnknownProvenance,
location = UNDEFINED_TEXT_LOCATION,
appliedCuration = null,
matchingPathExcludes = emptyList(),
copyrights = it.mapTo(mutableSetOf()) { statement ->
ResolvedCopyrightFinding(
statement = statement,
location = UNDEFINED_TEXT_LOCATION,
matchingPathExcludes = emptyList()
)
}
)
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions model/src/test/kotlin/PackageCurationDataTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PackageCurationDataTest : WordSpec({
purl = "original",
cpe = "original",
authors = sortedSetOf("original"),
concludedCopyrights = sortedSetOf("original"),
concludedLicense = "original".toSpdx(),
description = "original",
homepageUrl = "original",
Expand Down Expand Up @@ -57,6 +58,7 @@ class PackageCurationDataTest : WordSpec({
purl = "other",
cpe = "other",
authors = sortedSetOf("other"),
concludedCopyrights = sortedSetOf("other"),
concludedLicense = "other".toSpdx(),
description = "other",
homepageUrl = "other",
Expand Down Expand Up @@ -88,6 +90,7 @@ class PackageCurationDataTest : WordSpec({
val originalWithSomeUnsetData = original.copy(
comment = null,
authors = null,
concludedCopyrights = null,
concludedLicense = null,
binaryArtifact = null,
vcs = null,
Expand All @@ -98,6 +101,7 @@ class PackageCurationDataTest : WordSpec({
originalWithSomeUnsetData.merge(other) shouldBe originalWithSomeUnsetData.copy(
comment = other.comment,
authors = other.authors,
concludedCopyrights = other.concludedCopyrights,
concludedLicense = other.concludedLicense,
binaryArtifact = other.binaryArtifact,
vcs = other.vcs,
Expand All @@ -110,6 +114,7 @@ class PackageCurationDataTest : WordSpec({
original.merge(other) shouldBe original.copy(
comment = "original\nother",
authors = sortedSetOf("original", "other"),
concludedCopyrights = sortedSetOf("original", "other"),
concludedLicense = "original AND other".toSpdx(),
declaredLicenseMapping = mapOf(
"original" to "original".toSpdx(),
Expand All @@ -122,6 +127,7 @@ class PackageCurationDataTest : WordSpec({
val otherWithSomeOriginalData = other.copy(
comment = original.comment,
authors = original.authors,
concludedCopyrights = original.concludedCopyrights,
concludedLicense = original.concludedLicense,
declaredLicenseMapping = original.declaredLicenseMapping
)
Expand Down
7 changes: 5 additions & 2 deletions model/src/test/kotlin/PackageCurationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class PackageCurationTest : WordSpec({
purl = "pkg:maven/org.hamcrest/[email protected]#subpath=src/main/java/org/hamcrest/core",
cpe = "cpe:2.3:a:apache:commons_io:2.8.0:rc2:*:*:*:*:*:*",
authors = sortedSetOf("author 1", "author 2"),
declaredLicenseMapping = mapOf("license a" to "Apache-2.0".toSpdx()),
concludedCopyrights = sortedSetOf("copyright 1", "copyright 2"),
concludedLicense = "license1 OR license2".toSpdx(),
description = "description",
homepageUrl = "http://home.page",
Expand All @@ -75,7 +75,8 @@ class PackageCurationTest : WordSpec({
path = "path"
),
isMetaDataOnly = true,
isModified = true
isModified = true,
declaredLicenseMapping = mapOf("license a" to "Apache-2.0".toSpdx())
)
)

Expand All @@ -86,6 +87,7 @@ class PackageCurationTest : WordSpec({
purl shouldBe curation.data.purl
cpe shouldBe curation.data.cpe
authors shouldBe curation.data.authors
concludedCopyrights shouldBe curation.data.concludedCopyrights
declaredLicenses shouldBe pkg.declaredLicenses
declaredLicensesProcessed.spdxExpression shouldBe "Apache-2.0".toSpdx()
declaredLicensesProcessed.unmapped should containExactlyInAnyOrder("license b")
Expand Down Expand Up @@ -147,6 +149,7 @@ class PackageCurationTest : WordSpec({
purl shouldBe pkg.purl
cpe shouldBe pkg.cpe
authors shouldBe pkg.authors
concludedCopyrights shouldBe pkg.concludedCopyrights
declaredLicenses shouldBe pkg.declaredLicenses
concludedLicense shouldBe pkg.concludedLicense
description shouldBe pkg.description
Expand Down
23 changes: 23 additions & 0 deletions model/src/test/kotlin/licenses/LicenseInfoResolverTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,27 @@ class LicenseInfoResolverTest : WordSpec({
result should containFindingsForCopyrightExactly("(c) 2010 Holder 2", TextLocation("LICENSE", 3))
}

"resolve concluded copyright statements" {
val licenseInfos = listOf(
createLicenseInfo(
id = pkgId,
concludedCopyrights = concludedCopyrights,
declaredLicenses = declaredLicenses
)
)
val resolver = createResolver(licenseInfos)

val result = resolver.resolveLicenseInfo(pkgId)
result should containCopyrightStatementsForLicenseExactly(
"LicenseRef-a",
"Copyright (C) 2017 foo", "Copyright (C) 2022 bar"
)
result should containCopyrightStatementsForLicenseExactly(
"LicenseRef-b",
"Copyright (C) 2017 foo", "Copyright (C) 2022 bar"
)
}

"process copyrights by license" {
val licenseInfos = listOf(
createLicenseInfo(
Expand Down Expand Up @@ -625,6 +646,7 @@ private fun createResolver(

private fun createLicenseInfo(
id: Identifier,
concludedCopyrights: SortedSet<String>? = null,
declaredLicenses: Set<String> = emptySet(),
detectedLicenses: List<Findings> = emptyList(),
concludedLicense: SpdxExpression? = null
Expand All @@ -640,6 +662,7 @@ private fun createLicenseInfo(
findings = detectedLicenses
),
concludedLicenseInfo = ConcludedLicenseInfo(
concludedCopyrights = concludedCopyrights,
concludedLicense = concludedLicense,
appliedCurations = emptyList()
)
Expand Down
1 change: 1 addition & 0 deletions model/src/test/kotlin/licenses/TestData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import org.ossreviewtoolkit.utils.spdx.toSpdx
val authors = sortedSetOf("The Author", "The Other Author")
val projectAuthors = sortedSetOf("The Project Author")

val concludedCopyrights = sortedSetOf("Copyright (C) 2017 foo", "Copyright (C) 2022 bar")
val concludedLicense = "LicenseRef-a AND LicenseRef-b".toSpdx()
val declaredLicenses = sortedSetOf("LicenseRef-a", "LicenseRef-b")
val declaredLicensesProcessed = DeclaredLicenseProcessor.process(declaredLicenses)
Expand Down
11 changes: 7 additions & 4 deletions scanner/src/main/kotlin/Scanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,18 @@ import org.ossreviewtoolkit.utils.ort.Environment
const val TOOL_NAME = "scanner"

private fun removeConcludedPackages(packages: Set<Package>, scanner: Scanner): Set<Package> =
packages.takeUnless { scanner.scannerConfig.skipConcluded }
// Remove all packages that have a concluded license and authors set.
?: packages.partition { it.concludedLicense != null && it.authors.isNotEmpty() }.let { (skip, keep) ->
packages.takeUnless { scanner.scannerConfig.skipConcluded } ?: run {
// Remove all packages that have a concluded license and concluded copyrights set.
packages.partition { it.concludedLicense != null && it.concludedCopyrights.isNotEmpty() }.let { (skip, keep) ->
if (skip.isNotEmpty()) {
Scanner.logger.debug { "Not scanning the following packages with concluded licenses: $skip" }
Scanner.logger.debug {
"Not scanning the following packages with concluded licenses and concluded copyrights: $skip"
}
}

keep.toSet()
}
}

/**
* Use the [scanner] to scan the [Project]s and [Package]s specified in the [ortResult]. If [skipExcluded] is true,
Expand Down
8 changes: 5 additions & 3 deletions scanner/src/main/kotlin/experimental/ExperimentalScanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,18 @@ class ExperimentalScanner(
}

private fun Collection<Package>.filterNotConcluded(): Collection<Package> =
takeUnless { scannerConfig.skipConcluded }
?: partition { it.concludedLicense != null && it.authors.isNotEmpty() }.let { (skip, keep) ->
takeUnless { scannerConfig.skipConcluded } ?: run {
// Remove all packages that have a concluded license and concluded copyrights set.
partition { it.concludedLicense != null && it.concludedCopyrights.isNotEmpty() }.let { (skip, keep) ->
if (skip.isNotEmpty()) {
logger.debug {
"Not scanning the following package(s) with concluded licenses: $skip"
"Not scanning the following package(s) with concluded licenses and concluded copyrights: $skip"
}
}

keep
}
}

private fun Collection<Package>.filterNotMetaDataOnly(): List<Package> =
partition { it.isMetaDataOnly }.let { (skip, keep) ->
Expand Down