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) }) })