diff --git a/HISTORY.md b/HISTORY.md
index 0bdf245f9..fdbc0315e 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -5,10 +5,14 @@ All notable changes to this project will be documented in this file.
## unreleased
* Added
- * CycloneDX spec version 1.4 made element `bom.component.version` optional.
- Therefore, serialization/normalization with this spec version will no longer render this element,
- when its value is empty. (via [#137], [#138])
-
+ * Support for nested/bundled (sub-)components via `Models.Component.components` was added, including
+ serialization/normalization of models and impact on dependency graphs rendering. ([#132] via [#136])
+ * CycloneDX spec version 1.4 made element `Models.Component.version` optional.
+ Therefore, serialization/normalization with this spec version will no longer render this element
+ if its value is empty. (via [#137], [#138])
+
+[#132]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/132
+[#136]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/136
[#137]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/137
[#138]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/138
diff --git a/src/helpers/tree.ts b/src/helpers/tree.ts
new file mode 100644
index 000000000..ceee9b189
--- /dev/null
+++ b/src/helpers/tree.ts
@@ -0,0 +1,20 @@
+/*!
+This file is part of CycloneDX JavaScript Library.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+SPDX-License-Identifier: Apache-2.0
+Copyright (c) OWASP Foundation. All Rights Reserved.
+*/
+
+export const treeIterator = Symbol('iterator of a tree/nesting-like structure')
diff --git a/src/models/component.ts b/src/models/component.ts
index 51ca99a55..aa2c60242 100644
--- a/src/models/component.ts
+++ b/src/models/component.ts
@@ -28,6 +28,7 @@ import { ExternalReferenceRepository } from './externalReference'
import { LicenseRepository } from './license'
import { SWID } from './swid'
import { Comparable, SortableSet } from '../helpers/sortableSet'
+import { treeIterator } from '../helpers/tree'
interface OptionalProperties {
bomRef?: BomRef['value']
@@ -45,6 +46,7 @@ interface OptionalProperties {
swid?: Component['swid']
version?: Component['version']
dependencies?: Component['dependencies']
+ components?: Component['components']
cpe?: Component['cpe']
}
@@ -65,6 +67,7 @@ export class Component implements Comparable {
swid?: SWID
version?: string
dependencies: BomRefRepository
+ components: ComponentRepository
/** @see bomRef */
readonly #bomRef: BomRef
@@ -93,6 +96,7 @@ export class Component implements Comparable {
this.version = op.version
this.description = op.description
this.dependencies = op.dependencies ?? new BomRefRepository()
+ this.components = op.components ?? new ComponentRepository()
this.cpe = op.cpe
}
@@ -134,4 +138,10 @@ export class Component implements Comparable {
}
export class ComponentRepository extends SortableSet {
+ * [treeIterator] (): Generator {
+ for (const component of this) {
+ yield component
+ yield * component.components[treeIterator]()
+ }
+ }
}
diff --git a/src/serialize/json/normalize.ts b/src/serialize/json/normalize.ts
index 74d0ab758..b7f918844 100644
--- a/src/serialize/json/normalize.ts
+++ b/src/serialize/json/normalize.ts
@@ -23,6 +23,7 @@ import * as Models from '../../models'
import { Protocol as Spec, Version as SpecVersion } from '../../spec'
import { NormalizerOptions } from '../types'
import { JsonSchema, Normalized } from './types'
+import { treeIterator } from '../../helpers/tree'
export class Factory {
readonly #spec: Spec
@@ -270,6 +271,9 @@ export class ComponentNormalizer extends Base {
: this._factory.makeForSWID().normalize(data.swid, options),
externalReferences: data.externalReferences.size > 0
? this._factory.makeForExternalReference().normalizeRepository(data.externalReferences, options)
+ : undefined,
+ components: data.components.size > 0
+ ? this.normalizeRepository(data.components, options)
: undefined
}
: undefined
@@ -394,9 +398,12 @@ export class DependencyGraphNormalizer extends Base {
const allRefs = new Map()
if (data.metadata.component !== undefined) {
allRefs.set(data.metadata.component.bomRef, data.metadata.component.dependencies)
+ for (const component of data.metadata.component.components[treeIterator]()) {
+ allRefs.set(component.bomRef, component.dependencies)
+ }
}
- for (const c of data.components) {
- allRefs.set(c.bomRef, new Models.BomRefRepository(c.dependencies))
+ for (const component of data.components[treeIterator]()) {
+ allRefs.set(component.bomRef, component.dependencies)
}
const normalized: Normalized.Dependency[] = []
diff --git a/src/serialize/xml/normalize.ts b/src/serialize/xml/normalize.ts
index ff19d5dbb..bcdeb0088 100644
--- a/src/serialize/xml/normalize.ts
+++ b/src/serialize/xml/normalize.ts
@@ -23,6 +23,7 @@ import * as Models from '../../models'
import { Protocol as Spec, Version as SpecVersion } from '../../spec'
import { NormalizerOptions } from '../types'
import { SimpleXml, XmlSchema } from './types'
+import { treeIterator } from '../../helpers/tree'
export class Factory {
readonly #spec: Spec
@@ -335,6 +336,13 @@ export class ComponentNormalizer extends Base {
.normalizeRepository(data.externalReferences, options, 'reference')
}
: undefined
+ const components: SimpleXml.Element | undefined = data.components.size > 0
+ ? {
+ type: 'element',
+ name: 'components',
+ children: this.normalizeRepository(data.components, options, 'component')
+ }
+ : undefined
return {
type: 'element',
name: elementName,
@@ -357,7 +365,8 @@ export class ComponentNormalizer extends Base {
makeOptionalTextElement(data.cpe, 'cpe'),
makeOptionalTextElement(data.purl, 'purl'),
swid,
- extRefs
+ extRefs,
+ components
].filter(isNotUndefined)
}
}
@@ -509,9 +518,12 @@ export class DependencyGraphNormalizer extends Base {
const allRefs = new Map()
if (data.metadata.component !== undefined) {
allRefs.set(data.metadata.component.bomRef, data.metadata.component.dependencies)
+ for (const component of data.metadata.component.components[treeIterator]()) {
+ allRefs.set(component.bomRef, component.dependencies)
+ }
}
- for (const c of data.components) {
- allRefs.set(c.bomRef, new Models.BomRefRepository(c.dependencies))
+ for (const component of data.components[treeIterator]()) {
+ allRefs.set(component.bomRef, component.dependencies)
}
const normalized: Array<(SimpleXml.Element & { attributes: { ref: string } })> = []
diff --git a/tests/_data/models.js b/tests/_data/models.js
index 2df057f35..eff41b143 100644
--- a/tests/_data/models.js
+++ b/tests/_data/models.js
@@ -128,7 +128,7 @@ module.exports.createComplexStructure = function () {
component.scope = Enums.ComponentScope.Required
component.supplier = new Models.OrganizationalEntity({ name: 'Component Supplier' })
component.supplier.url.add(new URL('https://localhost/componentSupplier-B'))
- component.supplier.url.add(new URL('https://localhost/componentSupplier-A'))
+ component.supplier.url.add('https://localhost/componentSupplier-A')
component.supplier.contact.add(new Models.OrganizationalContact({ name: 'The quick brown fox' }))
component.supplier.contact.add((function (contact) {
contact.name = 'Franz'
@@ -150,12 +150,12 @@ module.exports.createComplexStructure = function () {
return component
})(new Models.Component(Enums.ComponentType.Library, 'dummy-component', { version: '1337-beta' })))
- bom.components.add(function (component) {
+ bom.components.add((function (component) {
// interlink everywhere
bom.metadata.component.dependencies.add(component.bomRef)
bom.components.forEach(c => c.dependencies.add(component.bomRef))
return component
- }(new Models.Component(Enums.ComponentType.Library, 'a-component', {
+ })(new Models.Component(Enums.ComponentType.Library, 'a-component', {
bomRef: 'a-component',
version: '', // empty string - not undefined
dependencies: new Models.BomRefRepository([
@@ -163,5 +163,28 @@ module.exports.createComplexStructure = function () {
])
})))
+ bom.components.add((function (component) {
+ // scenario:
+ // * `subComponentA` is a bundled dependency, that itself depends on `subComponentB`.
+ // * `subComponentB` is a transitive bundled dependency.
+ const subComponentA = new Models.Component(Enums.ComponentType.Library, 'SubComponentA', {
+ bomRef: `${component.bomRef.value}#SubComponentA`
+ })
+ component.dependencies.add(subComponentA.bomRef)
+ component.components.add(subComponentA)
+ const subComponentB = new Models.Component(Enums.ComponentType.Library, 'SubComponentB', {
+ bomRef: `${component.bomRef.value}#SubComponentB`
+ })
+ subComponentA.dependencies.add(subComponentB.bomRef)
+ component.components.add(subComponentB)
+
+ bom.metadata.component.dependencies.add(component.bomRef)
+
+ return component
+ })(new Models.Component(
+ Enums.ComponentType.Framework, 'SomeFrameworkBundle', {
+ bomRef: 'SomeFrameworkBundle'
+ })))
+
return bom
}
diff --git a/tests/_data/normalizeResults/json_sortedLists_spec1.2.json b/tests/_data/normalizeResults/json_sortedLists_spec1.2.json
index 232bfd92e..8b287bf97 100644
--- a/tests/_data/normalizeResults/json_sortedLists_spec1.2.json
+++ b/tests/_data/normalizeResults/json_sortedLists_spec1.2.json
@@ -161,6 +161,26 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -177,8 +197,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/normalizeResults/json_sortedLists_spec1.3.json b/tests/_data/normalizeResults/json_sortedLists_spec1.3.json
index 5009f5e82..c2505cc1d 100644
--- a/tests/_data/normalizeResults/json_sortedLists_spec1.3.json
+++ b/tests/_data/normalizeResults/json_sortedLists_spec1.3.json
@@ -161,6 +161,26 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -177,8 +197,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/normalizeResults/json_sortedLists_spec1.4.json b/tests/_data/normalizeResults/json_sortedLists_spec1.4.json
index 4970604de..55f07f029 100644
--- a/tests/_data/normalizeResults/json_sortedLists_spec1.4.json
+++ b/tests/_data/normalizeResults/json_sortedLists_spec1.4.json
@@ -170,6 +170,23 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -186,8 +203,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/normalizeResults/xml_sortedLists_spec1.2.json b/tests/_data/normalizeResults/xml_sortedLists_spec1.2.json
index 46a118278..82f215338 100644
--- a/tests/_data/normalizeResults/xml_sortedLists_spec1.2.json
+++ b/tests/_data/normalizeResults/xml_sortedLists_spec1.2.json
@@ -479,6 +479,72 @@
]
}
]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "framework",
+ "bom-ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SomeFrameworkBundle"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ },
+ {
+ "type": "element",
+ "name": "components",
+ "children": [
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentA"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentB"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
}
]
},
@@ -530,8 +596,55 @@
"attributes": {
"ref": "dummy-component"
}
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ }
}
]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": []
}
]
}
diff --git a/tests/_data/normalizeResults/xml_sortedLists_spec1.3.json b/tests/_data/normalizeResults/xml_sortedLists_spec1.3.json
index 5d305ab27..ec12ebed3 100644
--- a/tests/_data/normalizeResults/xml_sortedLists_spec1.3.json
+++ b/tests/_data/normalizeResults/xml_sortedLists_spec1.3.json
@@ -479,6 +479,72 @@
]
}
]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "framework",
+ "bom-ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SomeFrameworkBundle"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ },
+ {
+ "type": "element",
+ "name": "components",
+ "children": [
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentA"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentB"
+ },
+ {
+ "type": "element",
+ "name": "version",
+ "children": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
}
]
},
@@ -530,8 +596,55 @@
"attributes": {
"ref": "dummy-component"
}
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ }
}
]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": []
}
]
}
diff --git a/tests/_data/normalizeResults/xml_sortedLists_spec1.4.json b/tests/_data/normalizeResults/xml_sortedLists_spec1.4.json
index c112ed5e4..b2b190060 100644
--- a/tests/_data/normalizeResults/xml_sortedLists_spec1.4.json
+++ b/tests/_data/normalizeResults/xml_sortedLists_spec1.4.json
@@ -508,6 +508,57 @@
]
}
]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "framework",
+ "bom-ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SomeFrameworkBundle"
+ },
+ {
+ "type": "element",
+ "name": "components",
+ "children": [
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentA"
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "component",
+ "attributes": {
+ "type": "library",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "name",
+ "children": "SubComponentB"
+ }
+ ]
+ }
+ ]
+ }
+ ]
}
]
},
@@ -559,8 +610,55 @@
"attributes": {
"ref": "dummy-component"
}
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ }
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ }
}
]
+ },
+ {
+ "type": "element",
+ "name": "dependency",
+ "attributes": {
+ "ref": "SomeFrameworkBundle#SubComponentB"
+ },
+ "children": []
}
]
}
diff --git a/tests/_data/serializeResults/json_complex_spec1.2.json.bin b/tests/_data/serializeResults/json_complex_spec1.2.json.bin
index 511c6c211..dcd2cce31 100644
--- a/tests/_data/serializeResults/json_complex_spec1.2.json.bin
+++ b/tests/_data/serializeResults/json_complex_spec1.2.json.bin
@@ -161,6 +161,26 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -177,8 +197,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/serializeResults/json_complex_spec1.3.json.bin b/tests/_data/serializeResults/json_complex_spec1.3.json.bin
index 82c32eff2..7743a5345 100644
--- a/tests/_data/serializeResults/json_complex_spec1.3.json.bin
+++ b/tests/_data/serializeResults/json_complex_spec1.3.json.bin
@@ -161,6 +161,26 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "version": "",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -177,8 +197,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/serializeResults/json_complex_spec1.4.json.bin b/tests/_data/serializeResults/json_complex_spec1.4.json.bin
index e49067c6f..bd68effde 100644
--- a/tests/_data/serializeResults/json_complex_spec1.4.json.bin
+++ b/tests/_data/serializeResults/json_complex_spec1.4.json.bin
@@ -170,6 +170,23 @@
"comment": "testing"
}
]
+ },
+ {
+ "type": "framework",
+ "name": "SomeFrameworkBundle",
+ "bom-ref": "SomeFrameworkBundle",
+ "components": [
+ {
+ "type": "library",
+ "name": "SubComponentA",
+ "bom-ref": "SomeFrameworkBundle#SubComponentA"
+ },
+ {
+ "type": "library",
+ "name": "SubComponentB",
+ "bom-ref": "SomeFrameworkBundle#SubComponentB"
+ }
+ ]
}
],
"dependencies": [
@@ -186,8 +203,24 @@
"ref": "dummy.metadata.component",
"dependsOn": [
"a-component",
- "dummy-component"
+ "dummy-component",
+ "SomeFrameworkBundle"
]
+ },
+ {
+ "ref": "SomeFrameworkBundle",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentA"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentA",
+ "dependsOn": [
+ "SomeFrameworkBundle#SubComponentB"
+ ]
+ },
+ {
+ "ref": "SomeFrameworkBundle#SubComponentB"
}
]
}
\ No newline at end of file
diff --git a/tests/_data/serializeResults/xml_complex_spec1.2.xml.bin b/tests/_data/serializeResults/xml_complex_spec1.2.xml.bin
index 891cf0ffa..880c0464e 100644
--- a/tests/_data/serializeResults/xml_complex_spec1.2.xml.bin
+++ b/tests/_data/serializeResults/xml_complex_spec1.2.xml.bin
@@ -106,6 +106,20 @@
+
+ SomeFrameworkBundle
+
+
+
+ SubComponentA
+
+
+
+ SubComponentB
+
+
+
+
@@ -115,6 +129,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/_data/serializeResults/xml_complex_spec1.3.xml.bin b/tests/_data/serializeResults/xml_complex_spec1.3.xml.bin
index bee5e1826..f77bb6104 100644
--- a/tests/_data/serializeResults/xml_complex_spec1.3.xml.bin
+++ b/tests/_data/serializeResults/xml_complex_spec1.3.xml.bin
@@ -106,6 +106,20 @@
+
+ SomeFrameworkBundle
+
+
+
+ SubComponentA
+
+
+
+ SubComponentB
+
+
+
+
@@ -115,6 +129,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/_data/serializeResults/xml_complex_spec1.4.xml.bin b/tests/_data/serializeResults/xml_complex_spec1.4.xml.bin
index 71348bc40..cd7146ac0 100644
--- a/tests/_data/serializeResults/xml_complex_spec1.4.xml.bin
+++ b/tests/_data/serializeResults/xml_complex_spec1.4.xml.bin
@@ -113,6 +113,17 @@
+
+ SomeFrameworkBundle
+
+
+ SubComponentA
+
+
+ SubComponentB
+
+
+
@@ -122,6 +133,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/integration/Serialize.JsonNormalize.test.js b/tests/integration/Serialize.JsonNormalize.test.js
index 929fe186f..d19feb60f 100644
--- a/tests/integration/Serialize.JsonNormalize.test.js
+++ b/tests/integration/Serialize.JsonNormalize.test.js
@@ -24,7 +24,7 @@ const { describe, beforeEach, afterEach, it } = require('mocha')
const { createComplexStructure } = require('../_data/models')
const { loadNormalizeResult } = require('../_data/normalize')
/* uncomment next line to dump data */
-// const { writeNormalizeResult } = require('../_data/normalize')
+const { writeNormalizeResult } = require('../_data/normalize')
const {
Serialize: {
@@ -63,8 +63,9 @@ describe('Serialize.JsonNormalize', function () {
const json = JSON.stringify(normalized, null, 2)
- /* uncomment next line to dump data */
- // writeNormalizeResult(json, 'json_sortedLists', spec.version, 'json')
+ if (process.env.CJL_TEST_UPDATE_SNAPSHOTS) {
+ writeNormalizeResult(json, 'json_sortedLists', spec.version, 'json')
+ }
assert.deepStrictEqual(
JSON.parse(json),
diff --git a/tests/integration/Serialize.JsonSerialize.test.js b/tests/integration/Serialize.JsonSerialize.test.js
index 50fcaf8e9..9dd49d4d9 100644
--- a/tests/integration/Serialize.JsonSerialize.test.js
+++ b/tests/integration/Serialize.JsonSerialize.test.js
@@ -24,7 +24,7 @@ const { describe, beforeEach, afterEach, it } = require('mocha')
const { createComplexStructure } = require('../_data/models')
const { loadSerializeResult } = require('../_data/serialize')
/* uncomment next line to dump data */
-// const { writeSerializeResult } = require('../_data/serialize')
+const { writeSerializeResult } = require('../_data/serialize')
const {
Serialize: {
@@ -60,8 +60,9 @@ describe('Serialize.JsonSerialize', function () {
space: 4
})
- /* uncomment next line to dump data */
- // writeSerializeResult(serialized, 'json_complex', spec.version, 'json')
+ if (process.env.CJL_TEST_UPDATE_SNAPSHOTS) {
+ writeSerializeResult(serialized, 'json_complex', spec.version, 'json')
+ }
assert.strictEqual(
serialized,
diff --git a/tests/integration/Serialize.XmlNormalize.test.js b/tests/integration/Serialize.XmlNormalize.test.js
index ce2f8c3ef..9cd2ca599 100644
--- a/tests/integration/Serialize.XmlNormalize.test.js
+++ b/tests/integration/Serialize.XmlNormalize.test.js
@@ -24,7 +24,7 @@ const { describe, beforeEach, afterEach, it } = require('mocha')
const { createComplexStructure } = require('../_data/models')
const { loadNormalizeResult } = require('../_data/normalize')
/* uncomment next line to dump data */
-// const { writeNormalizeResult } = require('../_data/normalize')
+const { writeNormalizeResult } = require('../_data/normalize')
const {
Serialize: {
@@ -63,8 +63,9 @@ describe('Serialize.XmlNormalize', function () {
const json = JSON.stringify(normalized, null, 2)
- /* uncomment next line to dump data */
- // writeNormalizeResult(json, 'xml_sortedLists', spec.version, 'json')
+ if (process.env.CJL_TEST_UPDATE_SNAPSHOTS) {
+ writeNormalizeResult(json, 'xml_sortedLists', spec.version, 'json')
+ }
assert.deepStrictEqual(
JSON.parse(json),
diff --git a/tests/integration/Serialize.XmlSerialize.test.js b/tests/integration/Serialize.XmlSerialize.test.js
index a4a486610..b62bd954e 100644
--- a/tests/integration/Serialize.XmlSerialize.test.js
+++ b/tests/integration/Serialize.XmlSerialize.test.js
@@ -24,7 +24,7 @@ const { describe, beforeEach, afterEach, it } = require('mocha')
const { createComplexStructure } = require('../_data/models')
const { loadSerializeResult } = require('../_data/serialize')
/* uncomment next line to dump data */
-// const { writeSerializeResult } = require('../_data/serialize')
+const { writeSerializeResult } = require('../_data/serialize')
const {
Serialize: {
@@ -60,8 +60,9 @@ describe('Serialize.XmlSerialize', function () {
space: 4
})
- /* uncomment next line to dump data */
- // writeSerializeResult(serialized, 'xml_complex', spec.version, 'xml')
+ if (process.env.CJL_TEST_UPDATE_SNAPSHOTS) {
+ writeSerializeResult(serialized, 'xml_complex', spec.version, 'xml')
+ }
assert.strictEqual(
serialized,
diff --git a/tests/unit/Models.Component.spec.js b/tests/unit/Models.Component.spec.js
index 770c487ac..8c6e99e93 100644
--- a/tests/unit/Models.Component.spec.js
+++ b/tests/unit/Models.Component.spec.js
@@ -26,7 +26,7 @@ const { PackageURL } = require('packageurl-js')
const {
Models: {
- Component,
+ Component, ComponentRepository,
BomRef, BomRefRepository,
ExternalReferenceRepository, ExternalReference,
HashRepository,
@@ -57,22 +57,24 @@ suite('Models.Component', () => {
assert.strictEqual(component.supplier, undefined)
assert.strictEqual(component.swid, undefined)
assert.strictEqual(component.version, undefined)
+ assert.strictEqual(component.components.size, 0)
})
test('constructor with OptionalProperties', () => {
- const dummnBomRef = new BomRef('testing')
+ const dummyBomRef = new BomRef('testing')
const dummyExtRef = new ExternalReference('../', 'other')
const dummyLicense = new NamedLicense('mine')
const dummyPurl = new PackageURL('npm', 'ns', 'app', '1.33.7', {}, undefined)
const dummySupplier = new OrganizationalEntity({ name: 'dummySupplier' })
const dummySWID = new SWID('my-fake-swid', 'foo-bar')
+ const subComponent = new Component('library', 'MySubComponent')
const component = new Component('application', 'foobar', {
author: 'my author',
bomRef: 'my-bomref',
copyright: 'my copyright',
cpe: 'cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*',
- dependencies: new BomRefRepository([dummnBomRef]),
+ dependencies: new BomRefRepository([dummyBomRef]),
description: 'this is a test',
externalReferences: new ExternalReferenceRepository([dummyExtRef]),
group: 'the-crew',
@@ -82,7 +84,8 @@ suite('Models.Component', () => {
scope: 'optional',
supplier: dummySupplier,
swid: dummySWID,
- version: '1.33.7'
+ version: '1.33.7',
+ components: new ComponentRepository([subComponent])
})
assert.strictEqual(component.type, 'application')
@@ -92,7 +95,7 @@ suite('Models.Component', () => {
assert.strictEqual(component.copyright, 'my copyright')
assert.strictEqual(component.cpe, 'cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*')
assert.strictEqual(component.dependencies.size, 1)
- assert.strictEqual(Array.from(component.dependencies)[0], dummnBomRef)
+ assert.strictEqual(Array.from(component.dependencies)[0], dummyBomRef)
assert.strictEqual(component.description, 'this is a test')
assert.strictEqual(component.externalReferences.size, 1)
assert.strictEqual(Array.from(component.externalReferences)[0], dummyExtRef)
@@ -106,5 +109,7 @@ suite('Models.Component', () => {
assert.strictEqual(component.supplier, dummySupplier)
assert.strictEqual(component.swid, dummySWID)
assert.strictEqual(component.version, '1.33.7')
+ assert.strictEqual(component.components.size, 1)
+ assert.strictEqual(Array.from(component.components)[0], subComponent)
})
})