Skip to content

Commit 0f70088

Browse files
authoredNov 6, 2023
fix: correctly handle object licenses in SBOM generation (#6969)
1 parent b3a53c6 commit 0f70088

File tree

6 files changed

+255
-5
lines changed

6 files changed

+255
-5
lines changed
 

‎lib/utils/sbom-cyclonedx.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,14 @@ const toCyclonedxItem = (node, { packageType }) => {
8686

8787
let parsedLicense
8888
try {
89-
parsedLicense = parseLicense(node.package?.license)
89+
let license = node.package?.license
90+
if (license) {
91+
if (typeof license === 'object') {
92+
license = license.type
93+
}
94+
}
95+
96+
parsedLicense = parseLicense(license)
9097
} catch (err) {
9198
parsedLicense = null
9299
}
@@ -152,7 +159,7 @@ const toCyclonedxItem = (node, { packageType }) => {
152159
// If license is a single SPDX license, use the license field
153160
if (parsedLicense?.license) {
154161
component.licenses = [{ license: { id: parsedLicense.license } }]
155-
// If license is a conjunction, use the expression field
162+
// If license is a conjunction, use the expression field
156163
} else if (parsedLicense?.conjunction) {
157164
component.licenses = [{ expression: node.package.license }]
158165
}

‎lib/utils/sbom-spdx.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ const toSpdxItem = (node, { packageType }) => {
9393
location = node.linksIn.values().next().value.location
9494
}
9595

96+
let license = node.package?.license
97+
if (license) {
98+
if (typeof license === 'object') {
99+
license = license.type
100+
}
101+
}
102+
96103
const pkg = {
97104
name: node.packageName,
98105
SPDXID: toSpdxID(node),
@@ -103,7 +110,7 @@ const toSpdxItem = (node, { packageType }) => {
103110
downloadLocation: (node.isLink ? undefined : node.resolved) || NO_ASSERTION,
104111
filesAnalyzed: false,
105112
homepage: node.package?.homepage || NO_ASSERTION,
106-
licenseDeclared: node.package?.license || NO_ASSERTION,
113+
licenseDeclared: license || NO_ASSERTION,
107114
externalRefs: [
108115
{
109116
referenceCategory: REF_CAT_PACKAGE_MANAGER,

‎tap-snapshots/test/lib/utils/sbom-cyclonedx.js.test.cjs

+55
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,61 @@ exports[`test/lib/utils/sbom-cyclonedx.js TAP single node - with license express
912912
}
913913
`
914914

915+
exports[`test/lib/utils/sbom-cyclonedx.js TAP single node - with license object > must match snapshot 1`] = `
916+
{
917+
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
918+
"bomFormat": "CycloneDX",
919+
"specVersion": "1.5",
920+
"serialNumber": "urn:uuid:00000000-0000-0000-0000-000000000000",
921+
"version": 1,
922+
"metadata": {
923+
"timestamp": "2020-01-01T00:00:00.000Z",
924+
"lifecycles": [
925+
{
926+
"phase": "build"
927+
}
928+
],
929+
"tools": [
930+
{
931+
"vendor": "npm",
932+
"name": "cli",
933+
"version": "10.0.0 "
934+
}
935+
],
936+
"component": {
937+
"bom-ref": "root@1.0.0",
938+
"type": "library",
939+
"name": "root",
940+
"version": "1.0.0",
941+
"scope": "required",
942+
"author": "Author",
943+
"purl": "pkg:npm/root@1.0.0",
944+
"properties": [
945+
{
946+
"name": "cdx:npm:package:path",
947+
"value": ""
948+
}
949+
],
950+
"externalReferences": [],
951+
"licenses": [
952+
{
953+
"license": {
954+
"id": "MIT"
955+
}
956+
}
957+
]
958+
}
959+
},
960+
"components": [],
961+
"dependencies": [
962+
{
963+
"ref": "root@1.0.0",
964+
"dependsOn": []
965+
}
966+
]
967+
}
968+
`
969+
915970
exports[`test/lib/utils/sbom-cyclonedx.js TAP single node - with repository url > must match snapshot 1`] = `
916971
{
917972
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",

‎tap-snapshots/test/lib/utils/sbom-spdx.js.test.cjs

+135
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,138 @@ exports[`test/lib/utils/sbom-spdx.js TAP single node - with integrity > must mat
504504
]
505505
}
506506
`
507+
508+
exports[`test/lib/utils/sbom-spdx.js TAP single node - with license expression > must match snapshot 1`] = `
509+
{
510+
"spdxVersion": "SPDX-2.3",
511+
"dataLicense": "CC0-1.0",
512+
"SPDXID": "SPDXRef-DOCUMENT",
513+
"name": "root@1.0.0",
514+
"documentNamespace": "docns",
515+
"creationInfo": {
516+
"created": "2020-01-01T00:00:00.000Z",
517+
"creators": [
518+
"Tool: npm/cli-10.0.0 "
519+
]
520+
},
521+
"documentDescribes": [
522+
"SPDXRef-Package-root-1.0.0"
523+
],
524+
"packages": [
525+
{
526+
"name": "root",
527+
"SPDXID": "SPDXRef-Package-root-1.0.0",
528+
"versionInfo": "1.0.0",
529+
"packageFileName": "",
530+
"downloadLocation": "NOASSERTION",
531+
"filesAnalyzed": false,
532+
"homepage": "NOASSERTION",
533+
"licenseDeclared": "(MIT OR Apache-2.0)",
534+
"externalRefs": [
535+
{
536+
"referenceCategory": "PACKAGE-MANAGER",
537+
"referenceType": "purl",
538+
"referenceLocator": "pkg:npm/root@1.0.0"
539+
}
540+
]
541+
}
542+
],
543+
"relationships": [
544+
{
545+
"spdxElementId": "SPDXRef-DOCUMENT",
546+
"relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
547+
"relationshipType": "DESCRIBES"
548+
}
549+
]
550+
}
551+
`
552+
553+
exports[`test/lib/utils/sbom-spdx.js TAP single node - with license object > must match snapshot 1`] = `
554+
{
555+
"spdxVersion": "SPDX-2.3",
556+
"dataLicense": "CC0-1.0",
557+
"SPDXID": "SPDXRef-DOCUMENT",
558+
"name": "root@1.0.0",
559+
"documentNamespace": "docns",
560+
"creationInfo": {
561+
"created": "2020-01-01T00:00:00.000Z",
562+
"creators": [
563+
"Tool: npm/cli-10.0.0 "
564+
]
565+
},
566+
"documentDescribes": [
567+
"SPDXRef-Package-root-1.0.0"
568+
],
569+
"packages": [
570+
{
571+
"name": "root",
572+
"SPDXID": "SPDXRef-Package-root-1.0.0",
573+
"versionInfo": "1.0.0",
574+
"packageFileName": "",
575+
"downloadLocation": "NOASSERTION",
576+
"filesAnalyzed": false,
577+
"homepage": "NOASSERTION",
578+
"licenseDeclared": "MIT",
579+
"externalRefs": [
580+
{
581+
"referenceCategory": "PACKAGE-MANAGER",
582+
"referenceType": "purl",
583+
"referenceLocator": "pkg:npm/root@1.0.0"
584+
}
585+
]
586+
}
587+
],
588+
"relationships": [
589+
{
590+
"spdxElementId": "SPDXRef-DOCUMENT",
591+
"relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
592+
"relationshipType": "DESCRIBES"
593+
}
594+
]
595+
}
596+
`
597+
598+
exports[`test/lib/utils/sbom-spdx.js TAP single node - with single license > must match snapshot 1`] = `
599+
{
600+
"spdxVersion": "SPDX-2.3",
601+
"dataLicense": "CC0-1.0",
602+
"SPDXID": "SPDXRef-DOCUMENT",
603+
"name": "root@1.0.0",
604+
"documentNamespace": "docns",
605+
"creationInfo": {
606+
"created": "2020-01-01T00:00:00.000Z",
607+
"creators": [
608+
"Tool: npm/cli-10.0.0 "
609+
]
610+
},
611+
"documentDescribes": [
612+
"SPDXRef-Package-root-1.0.0"
613+
],
614+
"packages": [
615+
{
616+
"name": "root",
617+
"SPDXID": "SPDXRef-Package-root-1.0.0",
618+
"versionInfo": "1.0.0",
619+
"packageFileName": "",
620+
"downloadLocation": "NOASSERTION",
621+
"filesAnalyzed": false,
622+
"homepage": "NOASSERTION",
623+
"licenseDeclared": "ISC",
624+
"externalRefs": [
625+
{
626+
"referenceCategory": "PACKAGE-MANAGER",
627+
"referenceType": "purl",
628+
"referenceLocator": "pkg:npm/root@1.0.0"
629+
}
630+
]
631+
}
632+
],
633+
"relationships": [
634+
{
635+
"spdxElementId": "SPDXRef-DOCUMENT",
636+
"relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
637+
"relationshipType": "DESCRIBES"
638+
}
639+
]
640+
}
641+
`

‎test/lib/utils/sbom-cyclonedx.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,20 @@ t.test('single node - with license expression', t => {
190190
t.end()
191191
})
192192

193+
t.test('single node - with license object', t => {
194+
const pkg = {
195+
...rootPkg,
196+
license: {
197+
type: 'MIT',
198+
url: 'http://github.com/kriskowal/q/raw/master/LICENSE',
199+
},
200+
}
201+
const node = { ...root, package: pkg }
202+
const res = cyclonedxOutput({ npm, nodes: [node] })
203+
t.matchSnapshot(JSON.stringify(res))
204+
t.end()
205+
})
206+
193207
t.test('single node - from git url', t => {
194208
const node = { ...root, type: 'git', resolved: 'https://github.com/foo/bar#1234' }
195209
const res = cyclonedxOutput({ npm, nodes: [node] })
@@ -205,13 +219,15 @@ t.test('single node - no package info', t => {
205219
})
206220

207221
t.test('node - with deps', t => {
208-
const node = { ...root,
222+
const node = {
223+
...root,
209224
edgesOut: [
210225
{ to: dep1 },
211226
{ to: dep2 },
212227
{ to: undefined },
213228
{ to: { pkgid: 'foo' } },
214-
] }
229+
],
230+
}
215231
const res = cyclonedxOutput({ npm, nodes: [node, dep1, dep2, dep2Link] })
216232
t.matchSnapshot(JSON.stringify(res))
217233
t.end()

‎test/lib/utils/sbom-spdx.js

+30
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,36 @@ t.test('single node - application package type', t => {
109109
t.end()
110110
})
111111

112+
t.test('single node - with single license', t => {
113+
const pkg = { ...rootPkg, license: 'ISC' }
114+
const node = { ...root, package: pkg }
115+
const res = spdxOutput({ npm, nodes: [node] })
116+
t.matchSnapshot(JSON.stringify(res))
117+
t.end()
118+
})
119+
120+
t.test('single node - with license object', t => {
121+
const pkg = {
122+
...rootPkg,
123+
license: {
124+
type: 'MIT',
125+
url: 'http://github.com/kriskowal/q/raw/master/LICENSE',
126+
},
127+
}
128+
const node = { ...root, package: pkg }
129+
const res = spdxOutput({ npm, nodes: [node] })
130+
t.matchSnapshot(JSON.stringify(res))
131+
t.end()
132+
})
133+
134+
t.test('single node - with license expression', t => {
135+
const pkg = { ...rootPkg, license: '(MIT OR Apache-2.0)' }
136+
const node = { ...root, package: pkg }
137+
const res = spdxOutput({ npm, nodes: [node] })
138+
t.matchSnapshot(JSON.stringify(res))
139+
t.end()
140+
})
141+
112142
t.test('single node - with description', t => {
113143
const pkg = { ...rootPkg, description: 'Package description' }
114144
const node = { ...root, package: pkg }

0 commit comments

Comments
 (0)
Please sign in to comment.