diff --git a/src/_helpers.ts b/src/_helpers.ts index 9b5286a8..57107bac 100644 --- a/src/_helpers.ts +++ b/src/_helpers.ts @@ -20,6 +20,9 @@ Copyright (c) OWASP Foundation. All Rights Reserved. import { existsSync, readFileSync } from 'node:fs' import { dirname, isAbsolute, join, sep } from 'node:path' +import normalizePackageData from 'normalize-package-data' + + export function isNonNullable(value: T): value is NonNullable { // NonNullable: not null and not undefined return value !== null && value !== undefined @@ -116,3 +119,22 @@ export function iterableSome(i: Iterable, t: (v: T) => boolean): boolean { } // endregion polyfills + + +export function isString (v: any): v is string { + return typeof v === 'string' +} + +export function normalizePackageManifest (data: any, warn?: normalizePackageData.WarnFn): asserts data is normalizePackageData.Package { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access -- ack*/ + const oVersion = data.version + + /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ack */ + normalizePackageData(data as normalizePackageData.Input, warn) + + if (isString(oVersion)) { + // normalizer might have stripped version or sanitized it to SemVer -- we want the original + /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- ack */ + data.version = oVersion.trim() + } +} diff --git a/src/extractor.ts b/src/extractor.ts index 85c10514..20bb566a 100644 --- a/src/extractor.ts +++ b/src/extractor.ts @@ -20,15 +20,14 @@ Copyright (c) OWASP Foundation. All Rights Reserved. import { dirname } from 'node:path' import * as CDX from '@cyclonedx/cyclonedx-library' -import normalizePackageJson from 'normalize-package-data' import type { Compilation, Module } from 'webpack' import { getPackageDescription, isNonNullable, + normalizePackageManifest, type PackageDescription, - structuredClonePolyfill -} from './_helpers' + structuredClonePolyfill} from './_helpers' type WebpackLogger = Compilation['logger'] @@ -93,29 +92,19 @@ export class Extractor { */ makeComponent (pkg: PackageDescription, collectEvidence: boolean, logger?: WebpackLogger): CDX.Models.Component { try { - /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- expected */ + // work with a deep copy, because `normalizePackageManifest()` might modify the data + /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ach */ const _packageJson = structuredClonePolyfill(pkg.packageJson) - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hont */ - normalizePackageJson(_packageJson as normalizePackageJson.Input /* add debug for warnings? */) - // region fix normalizations - /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- expected */ - if (typeof pkg.packageJson.version === 'string') { - // allow non-SemVer strings - /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - , @typescript-eslint/no-unsafe-type-assertion - -- hint hint */ - _packageJson.version = (pkg.packageJson.version as string).trim() - } - // endregion fix normalizations - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - pkg.packageJson = _packageJson as normalizePackageJson.Package + normalizePackageManifest(_packageJson) + pkg.packageJson = _packageJson } catch (e) { logger?.warn('normalizePackageJson from PkgPath', pkg.path, 'failed:', e) } const component = this.#componentBuilder.makeComponent( - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - pkg.packageJson as normalizePackageJson.Package) + /* @ts-expect-error TS2559 */ + pkg.packageJson as PackageDescription) /* eslint-disable-line @typescript-eslint/no-unsafe-type-assertion -- ack */ + if (component === undefined) { throw new Error(`failed building Component from PkgPath ${pkg.path}`) } diff --git a/src/plugin.ts b/src/plugin.ts index 1c9bb4ed..f1628831 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -21,10 +21,15 @@ import { existsSync } from 'node:fs' import { join as joinPath, resolve } from 'node:path' import * as CDX from '@cyclonedx/cyclonedx-library' -import normalizePackageJson from 'normalize-package-data' import { Compilation, type Compiler, sources, version as WEBPACK_VERSION } from 'webpack' -import { getPackageDescription, iterableSome, loadJsonFile, type PackageDescription } from './_helpers' +import { + getPackageDescription, + iterableSome, + loadJsonFile, + normalizePackageManifest, + type PackageDescription +} from './_helpers' import { Extractor } from './extractor' type WebpackLogger = Compilation['logger'] @@ -381,13 +386,13 @@ export class CycloneDxWebpackPlugin { ? getPackageDescription(path)?.packageJson : { name: this.rootComponentName, version: this.rootComponentVersion } if (thisPackageJson === undefined) { return undefined } - normalizePackageJson( - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - thisPackageJson as normalizePackageJson.Input, + normalizePackageManifest( + + thisPackageJson, w => { logger.debug('normalizePackageJson from PkgPath', path, 'caused:', w) } ) - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - return builder.makeComponent(thisPackageJson as normalizePackageJson.Package) + + return builder.makeComponent(thisPackageJson) } #finalizeBom ( @@ -447,15 +452,11 @@ export class CycloneDxWebpackPlugin { logger.log('try to build new Tool from PkgPath', packageJsonPath) /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- expected */ const packageJson: PackageDescription['packageJson'] = loadJsonFile(packageJsonPath) ?? {} - normalizePackageJson( - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - packageJson as normalizePackageJson.Input, + normalizePackageManifest( + packageJson, w => { logger.debug('normalizePackageJson from PkgPath', packageJsonPath, 'caused:', w) } ) - const tool = builder.makeComponent( - /* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- hint hint */ - packageJson as normalizePackageJson.Package, - cType) + const tool = builder.makeComponent(packageJson, cType) if (tool !== undefined) { yield tool }