Skip to content

Commit f1a09d8

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/10.0.0-dev
2 parents b626de8 + d0cab76 commit f1a09d8

22 files changed

Lines changed: 377 additions & 35 deletions

.github/workflows/nodejs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ jobs:
177177
- ubuntu-latest
178178
- macos-latest
179179
- windows-latest
180+
include:
181+
- node: "25"
182+
experimental: true
183+
continue-on-error: ${{ matrix.experimental == true }}
180184
timeout-minutes: 10
181185
steps:
182186
- name: Checkout

HISTORY.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ All notable changes to this project will be documented in this file.
4444
* Deprecated symbol `Utils.LicenseUtility` ([#1346] via [#1377])
4545
* Deprecated symbol `Utils.LicenseUtility.FsUtils` ([#1346] via [#1377])
4646
Use `Contrib.License.Utils.FsUtils` instead.
47-
* Deprecated symbol `Utils.LicenseUtility.PathUtils` ([#1346] via [#1377])
47+
* Deprecated symbol `Utils.LicenseUtility.PathUtils` ([#1346] via [#1377])
4848
* Use `Contrib.License.Utils.PathUtils` instead.
4949
* Deprecated symbol `Utils.LicenseUtility.FileAttachment` ([#1346] via [#1377])
5050
Use `Contrib.License.Utils.FileAttachment` instead.
@@ -69,25 +69,33 @@ All notable changes to this project will be documented in this file.
6969
Suggested implementation is `spdx-expression-parse`.
7070
* Dependencies
7171
* Dependency `packageurl-js` became a suggested (optional peer-dependency) library ([#1348] via [#1378])
72-
You may use it to craft and parse PackageURLs downstream.
72+
You may use it to craft and parse PackageURLs downstream.
7373
* Dependency `spdx-expression-parse` became a suggested (optional peer-dependency) library ([#1348] via [#1382])
7474
Used as an injectable in `Contrib.License.Factories.LicenseFactory.constructor`.
75-
* Build
76-
* Use _webpack_ `5.105.2` now, was `v5.103.0` (via [#1360], [#1374])
7775
* Chore
7876
* Set dev-engines in `package.json` ([#1301] via [#1380])
7977

8078
[#1301]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1301
8179
[#1346]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1346
8280
[#1348]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1348
83-
[#1360]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1360
84-
[#1374]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1374
8581
[#1377]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1377
8682
[#1378]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1378
8783
[#1379]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1379
8884
[#1380]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1380
8985
[#1382]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1382
9086

87+
## 9.5.0 -- 2026-03-02
88+
89+
* Added
90+
* Classes `Models.NamedLicense` and `Models.SpdxLicense` support `properties` as per CycloneDX 1.5 (via [#1383])
91+
* Build
92+
* Use _webpack_ `5.105.3` now, was `v5.103.0` (via [#1360], [#1374], [#1390])
93+
94+
[#1360]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1360
95+
[#1374]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1374
96+
[#1383]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1383
97+
[#1390]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1390
98+
9199
## 9.4.1 -- 2025-12-04
92100

93101
* Fixed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134
"@types/mocha": "^10",
135135
"@types/node": "ts5.7",
136136
"@types/spdx-expression-parse": "^3",
137-
"c8": "^10",
137+
"c8": "^11",
138138
"copyfiles": "^2.4.1",
139139
"deepmerge": "^4.2.2",
140140
"fast-glob": "^3.3.1",
@@ -144,7 +144,7 @@
144144
"rimraf": "^6",
145145
"ts-loader": "9.5.4",
146146
"typescript": "5.9.3",
147-
"webpack": "5.105.2",
147+
"webpack": "5.105.3",
148148
"webpack-cli": "6.0.1",
149149
"webpack-node-externals": "3.0.0"
150150
},

src/models/license.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type { Sortable } from '../_helpers/sortable'
2121
import type { LicenseAcknowledgement } from '../enums/licenseAcknowledgement'
2222
import type { SpdxId } from '../spdx'
2323
import type { Attachment } from './attachment'
24+
import { PropertyRepository } from './property'
2425

2526
/**
2627
* (SPDX) License Expression.
@@ -64,11 +65,13 @@ class DisjunctiveLicenseBase {
6465
acknowledgement?: LicenseAcknowledgement
6566
text?: Attachment
6667
#url?: URL | string
68+
properties: PropertyRepository
6769

6870
constructor (op: OptionalDisjunctiveLicenseProperties = {}) {
6971
this.acknowledgement = op.acknowledgement
7072
this.text = op.text
7173
this.url = op.url
74+
this.properties = op.properties ?? new PropertyRepository()
7275
}
7376

7477
get url (): URL | string | undefined {
@@ -86,6 +89,7 @@ interface OptionalDisjunctiveLicenseProperties {
8689
acknowledgement?: DisjunctiveLicenseBase['acknowledgement']
8790
text?: DisjunctiveLicenseBase['text']
8891
url?: DisjunctiveLicenseBase['url']
92+
properties?: DisjunctiveLicenseBase['properties']
8993
}
9094

9195
export interface OptionalNamedLicenseProperties extends OptionalDisjunctiveLicenseProperties {}

src/serialize/json/normalize.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,36 +529,44 @@ export class LicenseNormalizer extends BaseJsonNormalizer<Models.License> {
529529
}
530530

531531
#normalizeNamedLicense (data: Models.NamedLicense, options: NormalizerOptions): Normalized.NamedLicense {
532+
const spec = this._factory.spec
532533
const url = escapeUri(data.url?.toString())
533534
return {
534535
license: {
535536
name: data.name,
536-
acknowledgement: this._factory.spec.supportsLicenseAcknowledgement
537+
acknowledgement: spec.supportsLicenseAcknowledgement
537538
? data.acknowledgement
538539
: undefined,
539540
text: data.text === undefined
540541
? undefined
541542
: this._factory.makeForAttachment().normalize(data.text, options),
542543
url: JsonSchema.isIriReference(url)
543544
? url
545+
: undefined,
546+
properties: spec.supportsProperties(data) && data.properties.size > 0
547+
? this._factory.makeForProperty().normalizeIterable(data.properties, options)
544548
: undefined
545549
}
546550
}
547551
}
548552

549553
#normalizeSpdxLicense (data: Models.SpdxLicense, options: NormalizerOptions): Normalized.SpdxLicense {
554+
const spec = this._factory.spec
550555
const url = escapeUri(data.url?.toString())
551556
return {
552557
license: {
553558
id: data.id,
554-
acknowledgement: this._factory.spec.supportsLicenseAcknowledgement
559+
acknowledgement: spec.supportsLicenseAcknowledgement
555560
? data.acknowledgement
556561
: undefined,
557562
text: data.text === undefined
558563
? undefined
559564
: this._factory.makeForAttachment().normalize(data.text, options),
560565
url: JsonSchema.isIriReference(url)
561566
? url
567+
: undefined,
568+
properties: spec.supportsProperties(data) && data.properties.size > 0
569+
? this._factory.makeForProperty().normalizeIterable(data.properties, options)
562570
: undefined
563571
}
564572
}

src/serialize/json/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ export namespace Normalized {
196196
acknowledgement?: Enums.LicenseAcknowledgement
197197
text?: Attachment
198198
url?: string
199+
properties?: Property[]
199200
}
200201
}
201202

@@ -206,6 +207,7 @@ export namespace Normalized {
206207
acknowledgement?: Enums.LicenseAcknowledgement
207208
text?: Attachment
208209
url?: string
210+
properties?: Property[]
209211
}
210212
}
211213

src/serialize/xml/normalize.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -699,12 +699,20 @@ export class LicenseNormalizer extends BaseXmlNormalizer<Models.License> {
699699
}
700700

701701
#normalizeNamedLicense (data: Models.NamedLicense, options: NormalizerOptions): SimpleXml.Element {
702-
const url = escapeUri(data.url?.toString())
702+
const spec = this._factory.spec
703+
const url = escapeUri(data.url?.toString())
704+
const properties: SimpleXml.Element | undefined = spec.supportsProperties(data) && data.properties.size > 0
705+
? {
706+
type: 'element',
707+
name: 'properties',
708+
children: this._factory.makeForProperty().normalizeIterable(data.properties, options, 'property')
709+
}
710+
: undefined
703711
return {
704712
type: 'element',
705713
name: 'license',
706714
attributes: {
707-
acknowledgement: this._factory.spec.supportsLicenseAcknowledgement
715+
acknowledgement: spec.supportsLicenseAcknowledgement
708716
? data.acknowledgement
709717
: undefined
710718
},
@@ -715,18 +723,27 @@ export class LicenseNormalizer extends BaseXmlNormalizer<Models.License> {
715723
: this._factory.makeForAttachment().normalize(data.text, options, 'text'),
716724
XmlSchema.isAnyURI(url)
717725
? makeTextElement(url, 'url')
718-
: undefined
726+
: undefined,
727+
properties
719728
].filter(isNotUndefined)
720729
}
721730
}
722731

723732
#normalizeSpdxLicense (data: Models.SpdxLicense, options: NormalizerOptions): SimpleXml.Element {
724-
const url = escapeUri(data.url?.toString())
733+
const spec = this._factory.spec
734+
const url = escapeUri(data.url?.toString())
735+
const properties: SimpleXml.Element | undefined = spec.supportsProperties(data) && data.properties.size > 0
736+
? {
737+
type: 'element',
738+
name: 'properties',
739+
children: this._factory.makeForProperty().normalizeIterable(data.properties, options, 'property')
740+
}
741+
: undefined
725742
return {
726743
type: 'element',
727744
name: 'license',
728745
attributes: {
729-
acknowledgement: this._factory.spec.supportsLicenseAcknowledgement
746+
acknowledgement: spec.supportsLicenseAcknowledgement
730747
? data.acknowledgement
731748
: undefined
732749
},
@@ -737,7 +754,8 @@ export class LicenseNormalizer extends BaseXmlNormalizer<Models.License> {
737754
: this._factory.makeForAttachment().normalize(data.text, options, 'text'),
738755
XmlSchema.isAnyURI(url)
739756
? makeTextElement(url, 'url')
740-
: undefined
757+
: undefined,
758+
properties
741759
].filter(isNotUndefined)
742760
}
743761
}

src/spec/_protocol.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

2020
import type { ComponentType, ExternalReferenceType, HashAlgorithm, Vulnerability } from '../enums'
21-
import type { HashContent } from '../models'
21+
import { type HashContent, NamedLicense, SpdxLicense } from '../models'
2222
import type { Format, Version } from './enums'
2323

2424
/**
@@ -79,6 +79,7 @@ export class _Spec implements _SpecProtocol {
7979
readonly #supportsLicenseAcknowledgement: boolean
8080
readonly #supportsServices: boolean
8181
readonly #supportsToolsComponentsServices: boolean
82+
readonly #supportsLicenseProperties: boolean
8283

8384
/* eslint-disable-next-line @typescript-eslint/max-params -- architectural decision */
8485
constructor (
@@ -101,7 +102,8 @@ export class _Spec implements _SpecProtocol {
101102
supportsExternalReferenceHashes: boolean,
102103
supportsLicenseAcknowledgement: boolean,
103104
supportsServices: boolean,
104-
supportsToolsComponentsServices: boolean
105+
supportsToolsComponentsServices: boolean,
106+
supportsLicenseProperties: boolean
105107
) {
106108
this.#version = version
107109
this.#formats = new Set(formats)
@@ -123,6 +125,7 @@ export class _Spec implements _SpecProtocol {
123125
this.#supportsLicenseAcknowledgement = supportsLicenseAcknowledgement
124126
this.#supportsServices = supportsServices
125127
this.#supportsToolsComponentsServices = supportsToolsComponentsServices
128+
this.#supportsLicenseProperties = supportsLicenseProperties
126129
}
127130

128131
get version (): Version {
@@ -166,9 +169,13 @@ export class _Spec implements _SpecProtocol {
166169
return this.#requiresComponentVersion
167170
}
168171

169-
supportsProperties (): boolean {
170-
// currently a global allow/deny -- might work based on input, in the future
171-
return this.#supportsProperties
172+
supportsProperties (model: any): boolean {
173+
switch (true) {
174+
case model instanceof NamedLicense || model instanceof SpdxLicense:
175+
return this.#supportsLicenseProperties
176+
default:
177+
return this.#supportsProperties
178+
}
172179
}
173180

174181
get supportsVulnerabilities (): boolean {

src/spec/consts.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export const Spec1dot2: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
8989
false,
9090
false,
9191
true,
92+
false,
9293
false
9394
))
9495

@@ -154,6 +155,7 @@ export const Spec1dot3: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
154155
true,
155156
false,
156157
true,
158+
false,
157159
false
158160
))
159161

@@ -226,6 +228,7 @@ export const Spec1dot4: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
226228
true,
227229
false,
228230
true,
231+
false,
229232
false
230233
))
231234

@@ -327,6 +330,7 @@ export const Spec1dot5: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
327330
true,
328331
false,
329332
true,
333+
true,
330334
true
331335
))
332336

@@ -433,6 +437,7 @@ export const Spec1dot6: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
433437
true,
434438
true,
435439
true,
440+
true,
436441
true
437442
))
438443

@@ -547,6 +552,7 @@ export const Spec1dot7: Readonly<_SpecProtocol> = Object.freeze(new _Spec(
547552
true,
548553
true,
549554
true,
555+
true,
550556
true
551557
))
552558

tests/_data/models.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ function createComplexStructure () {
156156
contentType: 'text/plain',
157157
encoding: Enums.AttachmentEncoding.Base64
158158
}),
159-
url: 'https://localhost/license'
159+
url: 'https://localhost/license',
160+
properties: new Models.PropertyRepository([
161+
new Models.Property('a', 'b'),
162+
new Models.Property('primaryLicense', 'true')
163+
])
160164
}))
161165
component.licenses.add((function (license) {
162166
license.text = new Models.Attachment('TUlUIExpY2Vuc2UKLi4uClRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLi4u')
@@ -165,7 +169,12 @@ function createComplexStructure () {
165169
license.url = new URL('https://spdx.org/licenses/MIT.html')
166170
license.acknowledgement = Enums.LicenseAcknowledgement.Declared
167171
return license
168-
})(new Models.SpdxLicense('MIT')))
172+
})(new Models.SpdxLicense('MIT', {
173+
properties: new Models.PropertyRepository([
174+
new Models.Property('c', 'd'),
175+
new Models.Property('primaryLicense', 'false')
176+
])
177+
})))
169178
component.publisher = 'the publisher'
170179
component.purl = 'pkg:npm/acme/dummy-component@1337-beta'
171180
component.scope = Enums.ComponentScope.Required

0 commit comments

Comments
 (0)