diff --git a/src/analysis.js b/src/analysis.js index fd72b04..826fc34 100644 --- a/src/analysis.js +++ b/src/analysis.js @@ -1,3 +1,5 @@ +import fs from "node:fs"; +import path from "node:path"; import {EOL} from "os"; import {RegexNotToBeLogged, getCustom} from "./tools.js"; @@ -17,7 +19,10 @@ const rhdaOperationTypeHeader = "rhda-operation-type" * @returns {Promise} */ async function requestStack(provider, manifest, url, html = false, opts = {}) { + opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64') + opts["manifest-type"] = path.parse(manifest).base let provided = provider.provideStack(manifest, opts) // throws error if content providing failed + opts["source-manifest"]= "" opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-","_")] = "stack-analysis" let startTime = new Date() let EndTime @@ -69,7 +74,9 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) { * @returns {Promise} */ async function requestComponent(provider, data, url, opts = {}, path = '') { + opts["source-manifest"]= Buffer.from(data).toString('base64') let provided = provider.provideComponent(data, opts,path) // throws error if content providing failed + opts["source-manifest"]= "" opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-","_")] = "component-analysis" if (process.env["EXHORT_DEBUG"] === "true") { console.log("Starting time of sending component analysis request to exhort server= " + new Date()) diff --git a/src/cyclone_dx_sbom.js b/src/cyclone_dx_sbom.js index 9efce1c..66d25f3 100644 --- a/src/cyclone_dx_sbom.js +++ b/src/cyclone_dx_sbom.js @@ -58,6 +58,7 @@ export default class CycloneDxSbom { rootComponent components dependencies + sourceManifestForAuditTrail constructor() { this.dependencies = new Array() @@ -117,17 +118,20 @@ export default class CycloneDxSbom { return this } - /** - * @return String CycloneDx Sbom json object in a string format + /** @param {{}} opts - various options, settings and configuration of application. + * @return String CycloneDx Sbom json object in a string format */ - getAsJsonString() { + getAsJsonString(opts) { + let manifestType = opts["manifest-type"] + this.setSourceManifest(opts["source-manifest"]) this.sbomObject = { "bomFormat": "CycloneDX", "specVersion": "1.4", "version": 1, "metadata": { "timestamp": new Date(), - "component": this.rootComponent + "component": this.rootComponent, + "properties": new Array() }, "components": this.components, "dependencies": this.dependencies @@ -136,6 +140,14 @@ export default class CycloneDxSbom { { delete this.sbomObject.metadata.component } + if(this.sourceManifestForAuditTrail !== undefined && manifestType !== undefined) { + this.sbomObject.metadata.properties.push({"name" : "rhda:manifest:content" , "value" : this.sourceManifestForAuditTrail}) + this.sbomObject.metadata.properties.push({"name" : "rhda:manifest:filename" , "value" : manifestType}) + } + else { + delete this.sbomObject.metadata.properties + } + if (process.env["EXHORT_DEBUG"] === "true") { console.log("SBOM Generated for manifest, to be sent to exhort service:" + EOL + JSON.stringify(this.sbomObject, null, 4)) } @@ -252,4 +264,8 @@ export default class CycloneDxSbom { this.dependencies.splice(depIndex, 1) this.rootComponent = undefined } + + setSourceManifest(manifestData) { + this.sourceManifestForAuditTrail = manifestData + } } diff --git a/src/index.js b/src/index.js index 4f2dcc1..5838680 100644 --- a/src/index.js +++ b/src/index.js @@ -119,6 +119,7 @@ async function stackAnalysis(manifest, html = false, opts = {}) { */ async function componentAnalysis(manifestType, data, opts = {}, path = '') { theUrl = selectExhortBackend(opts) + opts["manifest-type"] = manifestType let provider = match(manifestType, availableProviders) // throws error if no matching provider return await analysis.requestComponent(provider, data, theUrl, opts,path) // throws error request sending failed } diff --git a/src/providers/golang_gomodules.js b/src/providers/golang_gomodules.js index 16083aa..45bed5a 100644 --- a/src/providers/golang_gomodules.js +++ b/src/providers/golang_gomodules.js @@ -332,7 +332,7 @@ function getSBOM(manifest, opts = {}, includeTransitive) { enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps,sbom) } - return sbom.getAsJsonString() + return sbom.getAsJsonString(opts) } diff --git a/src/providers/java_gradle.js b/src/providers/java_gradle.js index 4797b19..bc64fda 100644 --- a/src/providers/java_gradle.js +++ b/src/providers/java_gradle.js @@ -141,7 +141,7 @@ export default class Java_gradle extends Base_java { if (process.env["EXHORT_DEBUG"] === "true") { console.log("Dependency tree that will be used as input for creating the BOM =>" + EOL + EOL + content) } - let sbom = this.#buildSbomFileFromTextFormat(content, properties, "runtimeClasspath", manifest) + let sbom = this.#buildSbomFileFromTextFormat(content, properties, "runtimeClasspath", manifest,opts) return sbom } @@ -204,7 +204,7 @@ export default class Java_gradle extends Base_java { } } - let sbom = this.#buildSbomFileFromTextFormat(content, properties, configName, manifestPath) + let sbom = this.#buildSbomFileFromTextFormat(content, properties, configName, manifestPath, opts) return sbom } @@ -251,7 +251,7 @@ export default class Java_gradle extends Base_java { * @param configName {string} - the configuration name of dependencies to include in sbom. * @return {string} return sbom json string of the build.gradle manifest file */ - #buildSbomFileFromTextFormat(content, properties, configName, manifestPath) { + #buildSbomFileFromTextFormat(content, properties, configName, manifestPath, opts = {}) { let sbom = new Sbom(); let root = `${properties.group}:${properties[ROOT_PROJECT_KEY_NAME].match(/Root project '(.+)'/)[1]}:jar:${properties.version}` let rootPurl = this.parseDep(root) @@ -272,7 +272,7 @@ export default class Java_gradle extends Base_java { } this.parseDependencyTree(root + ":compile", 0, arrayForSbom, sbom) let ignoredDeps = this.#getIgnoredDeps(manifestPath) - return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString(); + return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString(opts); } /** diff --git a/src/providers/java_maven.js b/src/providers/java_maven.js index 8c8fa3b..a496334 100644 --- a/src/providers/java_maven.js +++ b/src/providers/java_maven.js @@ -96,7 +96,7 @@ export default class Java_maven extends Base_java { if (process.env["EXHORT_DEBUG"] === "true") { console.log("Dependency tree that will be used as input for creating the BOM =>" + EOL + EOL + content.toString()) } - let sbom = this.createSbomFileFromTextFormat(content.toString(), ignoredDeps); + let sbom = this.createSbomFileFromTextFormat(content.toString(), ignoredDeps,opts); // delete temp file and directory fs.rmSync(tmpDir, {recursive: true, force: true}) // return dependency graph as string @@ -110,7 +110,7 @@ export default class Java_maven extends Base_java { * @param {[String]} ignoredDeps List of ignored dependencies to be omitted from sbom * @return {String} formatted sbom Json String with all dependencies */ - createSbomFileFromTextFormat(textGraphList, ignoredDeps) { + createSbomFileFromTextFormat(textGraphList, ignoredDeps, opts) { let lines = textGraphList.split(EOL); // get root component let root = lines[0]; @@ -118,7 +118,7 @@ export default class Java_maven extends Base_java { let sbom = new Sbom(); sbom.addRoot(rootPurl); this.parseDependencyTree(root, 0, lines.slice(1), sbom); - return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString(); + return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString(opts); } /** @@ -174,7 +174,7 @@ export default class Java_maven extends Base_java { } // return dependencies list - return sbom.getAsJsonString() + return sbom.getAsJsonString(opts) } /** diff --git a/src/providers/javascript_npm.js b/src/providers/javascript_npm.js index 1ef1175..b506f5e 100644 --- a/src/providers/javascript_npm.js +++ b/src/providers/javascript_npm.js @@ -154,7 +154,7 @@ function getSBOM(manifest, opts = {}, includeTransitive) { let ignoredDeps = Array.from(packageJsonObject.exhortignore); sbom.filterIgnoredDeps(ignoredDeps) } - return sbom.getAsJsonString() + return sbom.getAsJsonString(opts) } diff --git a/src/providers/python_pip.js b/src/providers/python_pip.js index bbedeeb..a5f8050 100644 --- a/src/providers/python_pip.js +++ b/src/providers/python_pip.js @@ -218,8 +218,7 @@ function createSbomStackAnalysis(manifest, opts = {}) { handleIgnoredDependencies(requirementTxtContent,sbom,opts) // In python there is no root component, then we must remove the dummy root we added, so the sbom json will be accepted by exhort backend // sbom.removeRootComponent() - return sbom.getAsJsonString() - + return sbom.getAsJsonString(opts) } @@ -248,7 +247,7 @@ function getSbomForComponentAnalysis(data, opts = {}) { handleIgnoredDependencies(data,sbom,opts) // In python there is no root component, then we must remove the dummy root we added, so the sbom json will be accepted by exhort backend // sbom.removeRootComponent() - return sbom.getAsJsonString() + return sbom.getAsJsonString(opts) } diff --git a/src/sbom.js b/src/sbom.js index 9a4b75f..0a995a8 100644 --- a/src/sbom.js +++ b/src/sbom.js @@ -56,14 +56,14 @@ export default class Sbom { /** * @return String sbom json in a string format */ - getAsJsonString(){ + getAsJsonString(opts = {}){ if (process.env["EXHORT_DEBUG"] === "true") { this.#endTime = new Date() console.log("Ending time to create sbom = " + this.#endTime) let time = (this.#endTime - this.#startTime) / 1000 console.log("Total time in seconds to create sbom = " + time) } - return this.sbomModel.getAsJsonString() + return this.sbomModel.getAsJsonString(opts) } /** @@ -92,6 +92,7 @@ export default class Sbom { { return this.sbomModel.removeRootComponent() } + }