Skip to content

Commit 75710d3

Browse files
feat(CSAF2.1): #356 add mandatory test 6.1.55 - show not listed licenses in message
1 parent 19c007f commit 75710d3

2 files changed

Lines changed: 67 additions & 55 deletions

File tree

csaf_2_1/mandatoryTests/mandatoryTest_6_1_55.js

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const ABOUT_CODE_LICENSE_REF_PREFIX = 'LicenseRef-scancode-'
4949

5050
const ABOUT_CODE_LICENSE_KEYS = new Set(
5151
license_information.licenses
52-
.filter((license) => !license.deprecated && license.source === 'aboutCode')
52+
.filter((license) => license.source === 'aboutCode')
5353
.map((license) => license.license_key)
5454
)
5555

@@ -73,49 +73,57 @@ function isAboutCodeLicense(licenseRefToCheck) {
7373
* Recursively checks if a parsed license expression contains not listed licenses.
7474
*
7575
* @param {import('license-expressions').ParsedSpdxExpression} parsedExpression - The parsed license expression
76-
* @returns {boolean} True if the expression contains any license references, false otherwise
76+
* @returns {Array<string>} all not listed licenses
7777
*/
78-
function containsNotListedLicenses(parsedExpression) {
78+
function notListedLicenses(parsedExpression) {
79+
/** @type {Array<string>} */
80+
const deprecatedLicenses = []
7981
// If it's a LicenseRef type directly
8082
if ('licenseRef' in parsedExpression) {
81-
return !isAboutCodeLicense(parsedExpression.licenseRef)
83+
if (!isAboutCodeLicense(parsedExpression.licenseRef)) {
84+
deprecatedLicenses.push(parsedExpression.licenseRef)
85+
}
8286
}
8387

8488
// If it's a conjunction, check both sides
8589
if ('conjunction' in parsedExpression) {
86-
return (
87-
containsNotListedLicenses(parsedExpression.left) ||
88-
containsNotListedLicenses(parsedExpression.right)
89-
)
90+
deprecatedLicenses.push(...notListedLicenses(parsedExpression.left))
91+
deprecatedLicenses.push(...notListedLicenses(parsedExpression.right))
9092
}
9193

92-
// If it's a LicenseInfo type, it doesn't contain not listed licenses
93-
return false
94+
// If it's a valid LicenseInfo type, it doesn't contain not listed license
95+
// Before we call this function we check that the whole expression is valid.
96+
// The expression is not valid, when it contains licences that are not listend
97+
// in the SPDX License List. (We check this in test 6.1.54)
98+
99+
return deprecatedLicenses
94100
}
95101

96102
/**
97-
* Checks if a license expression string contains any document references.
103+
* Checks if a license expression string contains any not listed references.
98104
*
99105
* @param {string} licenseToCheck - The license expression to check
100-
* @returns {boolean} True if the license expression contains any document references, false otherwise
106+
* @returns {Array<string>} True if the license expression contains any document references, false otherwise
101107
*/
102-
function hasNotListedLicenses(licenseToCheck) {
108+
function allNotListedLicenses(licenseToCheck) {
103109
const parseResult = parse(licenseToCheck)
104-
return containsNotListedLicenses(parseResult)
110+
return notListedLicenses(parseResult)
105111
}
106112

107113
/**
108114
* check if the license_expression contains license identifiers or exceptions
109115
* that are not listed in the SPDX license list or Aboutcode's "ScanCode LicenseDB"
110116
*
111117
* @param {string} licenseToCheck - The license expression to check
112-
* @returns {boolean} True if the license has not listed licenses, false otherwise
118+
* @returns {Array<string>} True if the license has not listed licenses, false otherwise
113119
*/
114-
export function existsNotListedLicenses(licenseToCheck) {
115-
return (
116-
!licenseToCheck ||
117-
(validate(licenseToCheck).valid && hasNotListedLicenses(licenseToCheck))
118-
)
120+
export function getNotListedLicenses(licenseToCheck) {
121+
// Validate ensures that no invalid SPDX licenses are present
122+
if (!licenseToCheck || !validate(licenseToCheck).valid) {
123+
return []
124+
} else {
125+
return allNotListedLicenses(licenseToCheck)
126+
}
119127
}
120128

121129
/**
@@ -163,16 +171,19 @@ export function mandatoryTest_6_1_55(doc) {
163171

164172
const licenseToCheck = doc.document.license_expression
165173
if (isLangEnglishOrUnspecified(doc.document.lang)) {
166-
if (existsNotListedLicenses(licenseToCheck)) {
174+
const notListedLicenses = getNotListedLicenses(licenseToCheck)
175+
if (notListedLicenses.length > 0) {
167176
const notes = doc.document.notes
168177
if (!notes || !containsOneLegalNote(notes)) {
169178
ctx.isValid = false
170179
ctx.errors.push({
171180
instancePath: '/document/notes',
172181
message:
173-
`The license_expression contains a license identifiers or exceptions that is not ` +
174-
`listed in Aboutcode's or SPDX license list. Therefore exactly one note with ` +
175-
` title 'License' and category 'legal_disclaimer' must exist`,
182+
`The license_expression contains the following license identifiers that ` +
183+
`are nor listed in Aboutcode's or SPDX license list: ` +
184+
`"${notListedLicenses.join()}". ` +
185+
`Therefore exactly one note with ` +
186+
`title "License" and category "legal_disclaimer" must exist`,
176187
})
177188
}
178189
}
Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
existsNotListedLicenses,
2+
getNotListedLicenses,
33
mandatoryTest_6_1_55,
44
} from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_55.js'
55
import { expect } from 'chai'
@@ -10,67 +10,68 @@ describe('mandatoryTest_6_1_55', function () {
1010
})
1111

1212
it('check license expressions', function () {
13-
expect(existsNotListedLicenses('GPL-3.0+')).to.be.false
14-
expect(existsNotListedLicenses('GPL-3.0-only')).to.be.false
15-
expect(existsNotListedLicenses('MIT OR (Apache-2.0 AND 0BSD)')).to.be.false
16-
expect(existsNotListedLicenses('Invalid-license-expression')).to.be.false
17-
expect(existsNotListedLicenses('GPL-2.0 OR BSD-3-Clause')).to.be.false
18-
expect(existsNotListedLicenses('LGPL-2.1 OR BSD-3-Clause AND MIT')).to.be
19-
.false
20-
expect(existsNotListedLicenses('(MIT AND (LGPL-2.1+ AND BSD-3-Clause))')).to
21-
.be.false
13+
expect(getNotListedLicenses('GPL-3.0+')).to.eql([])
14+
expect(getNotListedLicenses('GPL-3.0-only')).to.be.eql([])
15+
expect(getNotListedLicenses('MIT OR (Apache-2.0 AND 0BSD)')).to.be.eql([])
16+
expect(getNotListedLicenses('Invalid-license-expression')).to.be.eql([])
17+
expect(getNotListedLicenses('GPL-2.0 OR BSD-3-Clause')).to.be.eql([])
18+
expect(getNotListedLicenses('LGPL-2.1 OR BSD-3-Clause AND MIT')).to.be.eql(
19+
[]
20+
)
2221
expect(
23-
existsNotListedLicenses('MIT OR Apache-2.0 WITH Autoconf-exception-2.0'),
22+
getNotListedLicenses('(MIT AND (LGPL-2.1+ AND BSD-3-Clause))')
23+
).to.eql([])
24+
expect(
25+
getNotListedLicenses('MIT OR Apache-2.0 WITH Autoconf-exception-2.0'),
2426
'Exception associated with unrelated license'
25-
).to.be.false
27+
).to.eql([])
2628
expect(
27-
existsNotListedLicenses('3dslicer-1.0'),
29+
getNotListedLicenses('3dslicer-1.0'),
2830
'SPDX License List matching guidelines'
29-
).to.be.false
31+
).to.eql([])
3032

3133
expect(
32-
existsNotListedLicenses('LicenseRef-www.example.com-no-work-pd'),
34+
getNotListedLicenses('LicenseRef-www.example.com-no-work-pd'),
3335
'Valid SPDX expression with License Ref'
34-
).to.be.true
36+
).to.eql(['LicenseRef-www.example.com-no-work-pd'])
3537

3638
expect(
37-
existsNotListedLicenses(
39+
getNotListedLicenses(
3840
'LicenseRef-www.example.com-no-work-pd OR BSD-3-Clause AND MIT'
3941
),
4042
'Valid SPDX expression with compound-expression and License Ref'
41-
).to.be.true
43+
).to.eql(['LicenseRef-www.example.com-no-work-pd'])
4244

43-
expect(existsNotListedLicenses('wxWindows'), 'Deprecated License').to.be
44-
.false
45+
expect(getNotListedLicenses('wxWindows'), 'Deprecated License').to.eql([])
4546

4647
expect(
47-
existsNotListedLicenses('DocumentRef-X:LicenseRef-Y AND MIT'),
48-
'DocumentRef in License with compound-expression '
49-
).to.be.true
48+
getNotListedLicenses('DocumentRef-X:LicenseRef-Y AND LicenseRef-X'),
49+
'DocumentRef in License with compound-expression'
50+
).to.eql(['LicenseRef-Y', 'LicenseRef-X'])
5051

5152
expect(
52-
existsNotListedLicenses(
53+
getNotListedLicenses(
5354
'DocumentRef-some-document-reference:LicenseRef-www.example.org-Example-CSAF-License-2.0'
5455
),
5556
'DocumentRef in License'
56-
).to.be.true
57+
).to.eql(['LicenseRef-www.example.org-Example-CSAF-License-2.0'])
5758

5859
expect(
59-
existsNotListedLicenses(
60+
getNotListedLicenses(
6061
'LicenseRef-www.example.org-Example-CSAF-License-3.0+'
6162
),
6263
'LicenseRef in License with trailing +'
63-
).to.be.false
64+
).to.eql([])
6465
expect(
65-
existsNotListedLicenses('LicenseRef-scancode-acroname-bdk'),
66+
getNotListedLicenses('LicenseRef-scancode-acroname-bdk'),
6667
'LicenseRef in with About Code Prefix and listed license'
67-
).to.be.false
68+
).to.eql([])
6869

6970
expect(
70-
existsNotListedLicenses(
71+
getNotListedLicenses(
7172
'LicenseRef-scancode-www.example.org-Example-CSAF-License-3.0'
7273
),
7374
'LicenseRef in with About Code Prefix and not listed license'
74-
).to.be.true
75+
).to.eql(['LicenseRef-scancode-www.example.org-Example-CSAF-License-3.0'])
7576
})
7677
})

0 commit comments

Comments
 (0)