diff --git a/src/output/cyclonedx.ts b/src/output/cyclonedx.ts index 5b92f263..f3474aff 100644 --- a/src/output/cyclonedx.ts +++ b/src/output/cyclonedx.ts @@ -42,9 +42,16 @@ type CycloneDxMetadataComponent = { version?: string; }; +type CycloneDxToolComponent = { + type: "application"; + name: string; + version: string; + publisher: string; +}; + type CycloneDxMetadata = { timestamp: string; - tools: Array<{ vendor: string; name: string; version: string }>; + tools: { components: CycloneDxToolComponent[] }; component?: CycloneDxMetadataComponent; }; @@ -119,9 +126,16 @@ export function buildCycloneDxBom( const metadata: CycloneDxMetadata = { timestamp: new Date().toISOString(), - tools: [ - { vendor: "OWASP", name: "CVE Lite CLI", version }, - ], + tools: { + components: [ + { + type: "application", + name: "CVE Lite CLI", + version, + publisher: "OWASP", + }, + ], + }, }; if (projectMeta && projectMeta.name) { diff --git a/tests/cyclonedx.test.ts b/tests/cyclonedx.test.ts index 84759fc8..7c7c2e12 100644 --- a/tests/cyclonedx.test.ts +++ b/tests/cyclonedx.test.ts @@ -126,6 +126,21 @@ describe("buildCycloneDxBom", () => { const bom = buildCycloneDxBom(allPackages, [], null, "1.0.0"); expect(bom.metadata.component).toBeUndefined(); }); + + it("emits metadata.tools in the CycloneDX 1.6 tools.components form", () => { + const bom = buildCycloneDxBom(allPackages, [], null, "1.33.0"); + expect(Array.isArray(bom.metadata.tools)).toBe(false); + expect(bom.metadata.tools).toEqual({ + components: [ + { + type: "application", + name: "CVE Lite CLI", + version: "1.33.0", + publisher: "OWASP", + }, + ], + }); + }); }); describe("buildCycloneDxBom - CycloneDX 1.6 schema conformance", () => { @@ -163,6 +178,18 @@ describe("buildCycloneDxBom - CycloneDX 1.6 schema conformance", () => { } }); + it("on CycloneDX 1.6, metadata.tools uses components with publisher (not the deprecated vendor array)", () => { + const bom = buildCycloneDxBom(allPackages, findings, null, "1.0.0"); + expect(bom.specVersion).toBe("1.6"); + expect(Array.isArray(bom.metadata.tools)).toBe(false); + const tool = bom.metadata.tools.components[0]; + expect(tool.type).toBe("application"); + expect(tool.name).toBe("CVE Lite CLI"); + expect(tool.version).toBe("1.0.0"); + expect(tool.publisher).toBe("OWASP"); + expect((tool as { vendor?: string }).vendor).toBeUndefined(); + }); + it("on CycloneDX 1.6, every rating.method is one of the scoreMethod enum values, including 1.6-only additions", () => { // CVSSv4 and SSVC are 1.6 additions; they are absent from the 1.4 // scoreMethod enum, so this list itself is version-specific.