diff --git a/HISTORY.md b/HISTORY.md index f1fff431f..6ce0aa59f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,9 +6,12 @@ All notable changes to this project will be documented in this file. * Added * Components' install path/location will be visible in the SBOM result ([#305] via [#308]) + * new CLI option `--deduplicate-components` ... write something([#306] via [#309]) [#305]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/305 [#308]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/308 +[#306]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/306 +[#309]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/309 ## 1.4.1 - 2022-11-06 diff --git a/README.md b/README.md index 36256e171..57935f088 100644 --- a/README.md +++ b/README.md @@ -77,15 +77,19 @@ Options: This might be used, if "npm install" was run with "--force" or "--legacy-peer-deps". (default: false) --package-lock-only Whether to only use the lock file, ignoring "node_modules". - This means the output will be based only on the few details in and the tree described by the "npm-shrinkwrap.json" or "package-lock.json", rather than the contents of "node_modules" directory. + Enabling this feature means the output will be based only on the few details in and the tree described by the "npm-shrinkwrap.json" or "package-lock.json", rather than the contents of "node_modules" directory. (default: false) --omit Dependency types to omit from the installation tree. (can be set multiple times) (choices: "dev", "optional", "peer", default: "dev" if the NODE_ENV environment variable is set to "production", otherwise empty) --flatten-components Whether to flatten the components. - This means the actual nesting of node packages is not represented in the SBOM result. + Enabling this feature means the actual nesting of node packages is not represented in the SBOM result. + (default: false) + --deduplicate-components Whether to artificially de-duplicate the node packages. + Enabling this feature implies option "--flatten-components=true" + (default: false) --short-PURLs Omit all qualifiers from PackageURLs. - This causes information loss in trade of shorter PURLs, which might improve digesting these strings. + Enabling this feature causes information loss in trade of shorter PURLs, which might improve digesting these strings. (default: false) --spec-version Which version of CycloneDX spec to use. (choices: "1.2", "1.3", "1.4", default: "1.4") diff --git a/docs/component_deduplication.md b/docs/component_deduplication.md new file mode 100644 index 000000000..55dd06f05 --- /dev/null +++ b/docs/component_deduplication.md @@ -0,0 +1,3 @@ +# Component De-duplication + +TODO diff --git a/src/builders.ts b/src/builders.ts index 6e2a784f7..c50848d34 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -35,6 +35,7 @@ interface BomBuilderOptions { omitDependencyTypes?: Iterable reproducible?: BomBuilder['reproducible'] flattenComponents?: BomBuilder['flattenComponents'] + deduplicateComponents?: BomBuilder['deduplicateComponents'] shortPURLs?: BomBuilder['shortPURLs'] } @@ -54,6 +55,7 @@ export class BomBuilder { omitDependencyTypes: Set reproducible: boolean flattenComponents: boolean + deduplicateComponents: boolean shortPURLs: boolean console: Console @@ -77,6 +79,7 @@ export class BomBuilder { this.omitDependencyTypes = new Set(options.omitDependencyTypes ?? []) this.reproducible = options.reproducible ?? false this.flattenComponents = options.flattenComponents ?? false + this.deduplicateComponents = options.deduplicateComponents ?? false this.shortPURLs = options.shortPURLs ?? false this.console = console_ @@ -249,6 +252,7 @@ export class BomBuilder { component.properties.forEach(p => { if (p.name === PropertyNames.PackageBundled) { // bundle-markers have no value when components are flattened, because it is impossible to identify where a component was bundled into. + // this is no lnger true after this got implemented: https://github.com/CycloneDX/cyclonedx-node-npm/issues/305 component.properties.delete(p) } }) @@ -257,6 +261,9 @@ export class BomBuilder { bom.components.add(component) } } + if (this.deduplicateComponents) { + // TODO + } } // endregion components diff --git a/src/cli.ts b/src/cli.ts index a928527c3..e698058fc 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -43,6 +43,7 @@ interface CommandOptions { omit: Omittable[] specVersion: Spec.Version flattenComponents: boolean + deduplicateComponents: boolean shortPURLs: boolean outputReproducible: boolean outputFormat: OutputFormat @@ -67,7 +68,7 @@ function makeCommand (process: NodeJS.Process): Command { new Option( '--package-lock-only', 'Whether to only use the lock file, ignoring "node_modules".\n' + - 'This means the output will be based only on the few details in and the tree described by the "npm-shrinkwrap.json" or "package-lock.json", rather than the contents of "node_modules" directory.' + 'Enabling this feature means the output will be based only on the few details in and the tree described by the "npm-shrinkwrap.json" or "package-lock.json", rather than the contents of "node_modules" directory.' ).default(false) ).addOption( new Option( @@ -86,13 +87,19 @@ function makeCommand (process: NodeJS.Process): Command { new Option( '--flatten-components', 'Whether to flatten the components.\n' + - 'This means the actual nesting of node packages is not represented in the SBOM result.' + 'Enabling this feature means the actual nesting of node packages is not represented in the SBOM result.' + ).default(false) + ).addOption( + new Option( + '--deduplicate-components', + 'Whether to artificially de-duplicate the node packages.\n' + + 'Enabling this feature implies option "--flatten-components=true"' ).default(false) ).addOption( new Option( '--short-PURLs', 'Omit all qualifiers from PackageURLs.\n' + - 'This causes information loss in trade of shorter PURLs, which might improve digesting these strings.' + 'Enabling this feature causes information loss in trade of shorter PURLs, which might improve digesting these strings.' ).default(false) ).addOption( new Option( @@ -107,7 +114,7 @@ function makeCommand (process: NodeJS.Process): Command { new Option( '--output-reproducible', 'Whether to go the extra mile and make the output reproducible.\n' + - 'This requires more resources, and might result in loss of time- and random-based-values.' + 'Enabling this feature requires more resources, and might result in loss of time- and random-based-values.' ).env( 'BOM_REPRODUCIBLE' ) @@ -178,6 +185,10 @@ export function run (process: NodeJS.Process): void { program.parse(process.argv) const options: CommandOptions = program.opts() + if (options.deduplicateComponents && !options.flattenComponents) { + myConsole.info('INFO | Found option --deduplicate-components=true - therefore, forced option --flatten-components=true') + options.flattenComponents = true + } myConsole.debug('DEBUG | options: %j', options) const packageFile = resolve(process.cwd(), program.args[0] ?? 'package.json') @@ -221,6 +232,7 @@ export function run (process: NodeJS.Process): void { omitDependencyTypes: options.omit, reproducible: options.outputReproducible, flattenComponents: options.flattenComponents, + deduplicateComponents: options.deduplicateComponents, shortPURLs: options.shortPURLs }, myConsole