diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 9d1980802..c5233bd3d 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -130,7 +130,7 @@ jobs: if: github.repository == 'CycloneDX/cdxgen' runs-on: ubuntu-latest permissions: - contents: read + contents: write packages: write id-token: write steps: diff --git a/deno.json b/deno.json index 11c79e97e..189fae464 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@cyclonedx/cdxgen", - "version": "11.1.3", + "version": "11.1.4", "exports": "./lib/cli/index.js", "compilerOptions": { "lib": ["deno.window"], diff --git a/jsr.json b/jsr.json index c6ba7738d..b5628d989 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@cyclonedx/cdxgen", - "version": "11.1.3", + "version": "11.1.4", "exports": "./lib/cli/index.js", "include": ["*.js", "lib/**", "bin/**", "data/**", "types/**"], "exclude": [ diff --git a/lib/cli/index.js b/lib/cli/index.js index 155fd4b7a..baf54489f 100644 --- a/lib/cli/index.js +++ b/lib/cli/index.js @@ -14,7 +14,7 @@ import { writeFileSync, } from "node:fs"; import { platform as _platform, arch, homedir } from "node:os"; -import { basename, dirname, join, resolve, sep } from "node:path"; +import { basename, dirname, join, relative, resolve, sep } from "node:path"; import process from "node:process"; import { URL } from "node:url"; import got from "got"; @@ -2277,7 +2277,11 @@ export async function createNodejsBom(path, options) { let allImports = {}; let allExports = {}; if ( - !hasAnyProjectType(["docker", "oci", "container", "os"], options, false) && + !hasAnyProjectType( + ["docker", "oci", "container", "os", "pnpm"], + options, + false, + ) && !options.noBabel ) { if (DEBUG_MODE) { @@ -2504,6 +2508,7 @@ export async function createNodejsBom(path, options) { const depsWorkspaceRefs = {}; let workspaceCatalogs = {}; let workspaceWarningShown = false; + const seenPkgJsonFiles = {}; // Is this a pnpm workspace? for (const f of pnpmWorkspaceFiles) { if (DEBUG_MODE) { @@ -2524,15 +2529,34 @@ export async function createNodejsBom(path, options) { continue; } for (const apj of wpkgJsonFiles) { + if (seenPkgJsonFiles[apj]) { + continue; + } + seenPkgJsonFiles[apj] = true; const pkgData = JSON.parse(readFileSync(apj, "utf-8")); if (pkgData?.name) { + const relativePkgJsonFile = relative(path, apj); let workspaceRef = `pkg:npm/${pkgData.name}`; if (pkgData?.version) { workspaceRef = `${workspaceRef}@${pkgData.version}`; } - // Track all workspace purls - workspacePackages.push(workspaceRef); - workspaceSrcFiles[workspaceRef] = apj; + // Track all workspace purls. When we face duplicates, let's try to expand the purl to + // include the subpath. + if (!workspacePackages.includes(workspaceRef)) { + workspacePackages.push(workspaceRef); + } else { + console.log( + `Found a duplicate workspace with the name: ${pkgData.name}, ref: ${workspaceRef} at ${relativePkgJsonFile} and ${workspaceSrcFiles[workspaceRef]}. This is likely an error in the project that needs fixing.`, + ); + workspaceRef = `${workspaceRef}#${relativePkgJsonFile.replace(`${sep}package.json`, "")}`; + if (!workspacePackages.includes(workspaceRef)) { + workspacePackages.push(workspaceRef); + console.log( + `Duplicate workspace tracked as ${workspaceRef} under metadata.component.components`, + ); + } + } + workspaceSrcFiles[workspaceRef] = relativePkgJsonFile; // Track the direct dependencies of each workspace and workspace refs for each direct deps. const allDeps = { ...(pkgData.dependencies || {}), @@ -2559,6 +2583,21 @@ export async function createNodejsBom(path, options) { ...(workspaceObj.catalogs || {}), }; } + if (DEBUG_MODE && Object.keys(seenPkgJsonFiles).length) { + console.log( + `${Object.keys(seenPkgJsonFiles).length} package.json files were parsed to identify workspace names. Total number of package.json files: ${pkgJsonFiles.length}`, + ); + if (Object.keys(seenPkgJsonFiles).length < pkgJsonFiles.length - 1) { + const seenfilenames = Object.keys(seenPkgJsonFiles); + console.log( + "Following files were not parsed:", + pkgJsonFiles.filter((p) => !seenfilenames.includes(p)), + ); + console.log( + "TIP: Check the configuration in pnpm-workspace.yaml to ensure all the required workspaces are included correctly.", + ); + } + } for (const f of pnpmLockFiles) { if (DEBUG_MODE) { console.log(`Parsing ${f}`); diff --git a/package.json b/package.json index 15aa7c445..35522aafb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cyclonedx/cdxgen", - "version": "11.1.3", + "version": "11.1.4", "description": "Creates CycloneDX Software Bill of Materials (SBOM) from source or container image", "homepage": "http://github.com/cyclonedx/cdxgen", "author": "Prabhu Subramanian ", diff --git a/types/lib/cli/index.d.ts.map b/types/lib/cli/index.d.ts.map index 01a2c66cc..47a9cee41 100644 --- a/types/lib/cli/index.d.ts.map +++ b/types/lib/cli/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/cli/index.js"],"names":[],"mappings":"AAsxBA;;;;;;;;GAQG;AACH,gFAFW,MAAM,SAchB;AAuXD;;;;;;;GAOG;AACH,mCALW,MAAM,qBAiEhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM;;;;EAKhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM;;;;EAkBhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAs7BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAgqBhB;AAED;;;;;;;;;;GAUG;AACH,+DAsEC;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BA+dhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BA+YhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqIhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAkEhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBA+KhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBAsHhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,0CAHW,MAAM,qBAuBhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,8BAqDhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,8BA4ChB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BA6FhB;AAED;;;;;GAKG;AACH,iDAHW,MAAM,qBAmUhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAiJhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqNhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAkahB;AAED;;;;;GAKG;AACH,2CAHW,MAAM;;;;;;;;;;;;;;;;;;;;GAoChB;AAED;;;;;;;;KA+DC;AAED;;;;;;GAMG;AACH,yDA+FC;AAED;;;;;;;;;GASG;AACH,2GA6BC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,EAAE,8BA2iBlB;AAED;;;;;GAKG;AACH,iCAHW,MAAM,8BAgUhB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,qBAiPhB;AAED;;;;;;GAMG;AACH,wDAFY,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAAC,CAwHxE"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/cli/index.js"],"names":[],"mappings":"AAsxBA;;;;;;;;GAQG;AACH,gFAFW,MAAM,SAchB;AAuXD;;;;;;;GAOG;AACH,mCALW,MAAM,qBAiEhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM;;;;EAKhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM;;;;EAkBhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAs7BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAusBhB;AAED;;;;;;;;;;GAUG;AACH,+DAsEC;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BA+dhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BA+YhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqIhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAkEhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBA+KhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBAsHhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,0CAHW,MAAM,qBAuBhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,8BAqDhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,8BA4ChB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BA6FhB;AAED;;;;;GAKG;AACH,iDAHW,MAAM,qBAmUhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAiJhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqNhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAkahB;AAED;;;;;GAKG;AACH,2CAHW,MAAM;;;;;;;;;;;;;;;;;;;;GAoChB;AAED;;;;;;;;KA+DC;AAED;;;;;;GAMG;AACH,yDA+FC;AAED;;;;;;;;;GASG;AACH,2GA6BC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,EAAE,8BA2iBlB;AAED;;;;;GAKG;AACH,iCAHW,MAAM,8BAgUhB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,qBAiPhB;AAED;;;;;;GAMG;AACH,wDAFY,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAAC,CAwHxE"} \ No newline at end of file