Skip to content

Commit b6d4577

Browse files
authored
fix: remove dummy root component from sbom for python/pip (#58)
Signed-off-by: Zvi Grinberg <[email protected]>
1 parent 0fd0685 commit b6d4577

File tree

10 files changed

+80
-372
lines changed

10 files changed

+80
-372
lines changed

src/cyclone_dx_sbom.js

Lines changed: 63 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,31 @@ export default class CycloneDxSbom {
5858
rootComponent
5959
components
6060
dependencies
61+
6162
constructor() {
6263
this.dependencies = new Array()
6364
this.components = new Array()
6465

6566

6667
}
68+
6769
/**
6870
* @param {PackageURL} root - add main/root component for sbom
6971
* @return {CycloneDxSbom} the CycloneDxSbom Sbom Object
7072
*/
71-
addRoot (root) {
73+
addRoot(root) {
7274

7375
this.rootComponent =
74-
getComponent(root,"application")
76+
getComponent(root, "application")
7577
this.components.push(this.rootComponent)
7678
return this
7779
}
7880

7981

80-
8182
/**
8283
* @return {{{"bom-ref": string, name, purl: string, type, version}}} root component of sbom.
8384
*/
84-
getRoot (){
85+
getRoot() {
8586
return this.rootComponent
8687
}
8788

@@ -90,52 +91,52 @@ export default class CycloneDxSbom {
9091
* @param {PackageURL} targetRef current dependency to add to Dependencies list of component sourceRef
9192
* @return Sbom
9293
*/
93-
addDependency(sourceRef, targetRef){
94+
addDependency(sourceRef, targetRef) {
9495
let componentIndex = this.getComponentIndex(sourceRef);
95-
if(componentIndex < 0)
96-
{
97-
this.components.push(getComponent(sourceRef,"library"))
96+
if (componentIndex < 0) {
97+
this.components.push(getComponent(sourceRef, "library"))
9898
}
9999
let dependencyIndex = this.getDependencyIndex(sourceRef.purl)
100-
if(dependencyIndex < 0)
101-
{
100+
if (dependencyIndex < 0) {
102101
this.dependencies.push(createDependency(sourceRef.purl))
103102
dependencyIndex = this.getDependencyIndex(sourceRef.purl)
104103
}
105104

106105
//Only if the dependency doesn't exists on the dependency list of dependency, then add it to this list.
107-
if(this.dependencies[dependencyIndex].dependsOn.findIndex(dep => dep === targetRef.toString()) === -1)
108-
{
106+
if (this.dependencies[dependencyIndex].dependsOn.findIndex(dep => dep === targetRef.toString()) === -1) {
109107
this.dependencies[dependencyIndex].dependsOn.push(targetRef.toString())
110108
}
111-
if(this.getDependencyIndex(targetRef.toString()) < 0)
112-
{
109+
if (this.getDependencyIndex(targetRef.toString()) < 0) {
113110
this.dependencies.push(createDependency(targetRef.toString()))
114111
}
115-
let newComponent = getComponent(targetRef,"library");
112+
let newComponent = getComponent(targetRef, "library");
116113
// Only if component doesn't exists in component list, add it to the list.
117-
if(this.getComponentIndex(newComponent) < 0)
118-
{
114+
if (this.getComponentIndex(newComponent) < 0) {
119115
this.components.push(newComponent)
120116
}
121117
return this
122118
}
119+
123120
/**
124121
* @return String CycloneDx Sbom json object in a string format
125122
*/
126-
getAsJsonString(){
123+
getAsJsonString() {
127124
this.sbomObject = {
128-
"bomFormat" : "CycloneDX",
129-
"specVersion" : "1.4",
130-
"version" : 1,
131-
"metadata" : {
132-
"timestamp" : new Date(),
133-
"component" : this.rootComponent
125+
"bomFormat": "CycloneDX",
126+
"specVersion": "1.4",
127+
"version": 1,
128+
"metadata": {
129+
"timestamp": new Date(),
130+
"component": this.rootComponent
134131
},
135-
"components" : this.components,
136-
"dependencies" : this.dependencies
132+
"components": this.components,
133+
"dependencies": this.dependencies
134+
}
135+
if (this.rootComponent === undefined)
136+
{
137+
delete this.sbomObject.metadata.component
137138
}
138-
if(process.env["EXHORT_DEBUG"] === "true") {
139+
if (process.env["EXHORT_DEBUG"] === "true") {
139140
console.log("SBOM Generated for manifest, to be sent to exhort service:" + EOL + JSON.stringify(this.sbomObject, null, 4))
140141
}
141142
return JSON.stringify(this.sbomObject)
@@ -146,7 +147,7 @@ export default class CycloneDxSbom {
146147
* @param {String} dependency - purl string of the component.
147148
* @return {int} - the index of the dependency in dependencies Array, returns -1 if not found.
148149
*/
149-
getDependencyIndex(dependency){
150+
getDependencyIndex(dependency) {
150151
return this.dependencies.findIndex(dep => dep.ref === dependency)
151152
}
152153

@@ -156,7 +157,7 @@ export default class CycloneDxSbom {
156157
* @return {int} index of the found component entry, if not found returns -1.
157158
* @private
158159
*/
159-
getComponentIndex(theComponent){
160+
getComponentIndex(theComponent) {
160161

161162
return this.components.findIndex(component => component.purl === theComponent.purl)
162163
}
@@ -165,33 +166,30 @@ export default class CycloneDxSbom {
165166
* @param purl {PackageURL}
166167
* @return component
167168
*/
168-
purlToComponent(purl)
169-
{
170-
return getComponent(purl,"library")
169+
purlToComponent(purl) {
170+
return getComponent(purl, "library")
171171
}
172+
172173
/**
173174
* This method gets an array of dependencies to be ignored, and remove all of them from CycloneDx Sbom
174175
* @param {Array} dependencies to be removed from sbom
175176
* @return {CycloneDxSbom} without ignored dependencies
176177
*/
177-
filterIgnoredDeps(deps){
178+
filterIgnoredDeps(deps) {
178179
deps.forEach(dep => {
179-
let index = this.components.findIndex(component => component.name === dep );
180-
if(index>=0)
181-
{
182-
this.components.splice(index,1)
180+
let index = this.components.findIndex(component => component.name === dep);
181+
if (index >= 0) {
182+
this.components.splice(index, 1)
183183
}
184184
index = this.dependencies.findIndex(dependency => dependency.ref.includes(dep));
185-
if(index>=0)
186-
{
187-
this.dependencies.splice(index,1)
185+
if (index >= 0) {
186+
this.dependencies.splice(index, 1)
188187
}
189188

190189
this.dependencies.forEach(dependency => {
191190
let indexDependsOn = dependency.dependsOn.findIndex(theDep => theDep.includes(dep));
192-
if (indexDependsOn > -1 )
193-
{
194-
dependency.dependsOn.splice(indexDependsOn,1)
191+
if (indexDependsOn > -1) {
192+
dependency.dependsOn.splice(indexDependsOn, 1)
195193
}
196194
})
197195
})
@@ -203,24 +201,21 @@ export default class CycloneDxSbom {
203201
* @param {Array} dependencies to be removed from sbom
204202
* @return {CycloneDxSbom} without ignored dependencies
205203
*/
206-
filterIgnoredDepsIncludingVersion(deps){
204+
filterIgnoredDepsIncludingVersion(deps) {
207205
deps.forEach(dep => {
208-
let index = this.components.findIndex(component => component.purl === dep );
209-
if(index>=0)
210-
{
211-
this.components.splice(index,1)
206+
let index = this.components.findIndex(component => component.purl === dep);
207+
if (index >= 0) {
208+
this.components.splice(index, 1)
212209
}
213210
index = this.dependencies.findIndex(dependency => dependency.ref === dep);
214-
if(index>=0)
215-
{
216-
this.dependencies.splice(index,1)
211+
if (index >= 0) {
212+
this.dependencies.splice(index, 1)
217213
}
218214

219215
this.dependencies.forEach(dependency => {
220-
let indexDependsOn = dependency.dependsOn.findIndex(theDep => theDep === dep );
221-
if (indexDependsOn > -1 )
222-
{
223-
dependency.dependsOn.splice(indexDependsOn,1)
216+
let indexDependsOn = dependency.dependsOn.findIndex(theDep => theDep === dep);
217+
if (indexDependsOn > -1) {
218+
dependency.dependsOn.splice(indexDependsOn, 1)
224219
}
225220
})
226221
})
@@ -233,24 +228,28 @@ export default class CycloneDxSbom {
233228
*
234229
* @return {boolean}
235230
*/
236-
checkIfPackageInsideDependsOnList(component, name)
237-
{
231+
checkIfPackageInsideDependsOnList(component, name) {
238232

239233
let dependencyIndex = this.getDependencyIndex(component.purl)
240-
if(dependencyIndex < 0)
241-
{
234+
if (dependencyIndex < 0) {
242235
return false
243236
}
244237

245238
//Only if the dependency doesn't exists on the dependency list of dependency, then add it to this list.
246-
if(this.dependencies[dependencyIndex].dependsOn.findIndex(dep => dep.includes(name) ) >= 0)
247-
{
239+
if (this.dependencies[dependencyIndex].dependsOn.findIndex(dep => dep.includes(name)) >= 0) {
248240
return true;
249-
}
250-
else {
241+
} else {
251242
return false
252243
}
253244
}
254245

255-
246+
/** Removes the root component from the sbom
247+
*/
248+
removeRootComponent() {
249+
let compIndex = this.getComponentIndex(this.rootComponent)
250+
let depIndex = this.getDependencyIndex(this.rootComponent.purl)
251+
this.components.splice(compIndex, 1)
252+
this.dependencies.splice(depIndex, 1)
253+
this.rootComponent = undefined
254+
}
256255
}

src/providers/python_pip.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ function createSbomStackAnalysis(manifest, opts = {}) {
179179
})
180180
let requirementTxtContent = fs.readFileSync(manifest).toString();
181181
handleIgnoredDependencies(requirementTxtContent,sbom)
182+
// 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
183+
sbom.removeRootComponent()
182184
return sbom.getAsJsonString()
183185

184186

@@ -208,6 +210,8 @@ function getSbomForComponentAnalysis(data, opts = {}) {
208210
})
209211
fs.rmSync(tmpDir, { recursive: true, force: true });
210212
handleIgnoredDependencies(data,sbom)
213+
// 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
214+
sbom.removeRootComponent()
211215
return sbom.getAsJsonString()
212216
}
213217

src/sbom.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ export default class Sbom {
7373
{
7474
return this.sbomModel.checkIfPackageInsideDependsOnList(component,name)
7575
}
76+
77+
/** Removes the root component from the sbom
78+
*/
79+
removeRootComponent()
80+
{
81+
return this.sbomModel.removeRootComponent()
82+
}
7683
}
7784

7885

test/providers/tst_manifests/pip/pip_requirements_txt_ignore/expected_component_sbom.json

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,9 @@
33
"specVersion" : "1.4",
44
"version" : 1,
55
"metadata" : {
6-
"timestamp" : "2023-10-01T00:00:00.000Z",
7-
"component" : {
8-
"name" : "root",
9-
"purl" : "pkg:pypi/root",
10-
"type" : "application",
11-
"bom-ref" : "pkg:pypi/root"
12-
}
6+
"timestamp" : "2023-10-01T00:00:00.000Z"
137
},
148
"components" : [
15-
{
16-
"name" : "root",
17-
"purl" : "pkg:pypi/root",
18-
"type" : "application",
19-
"bom-ref" : "pkg:pypi/root"
20-
},
219
{
2210
"name" : "anyio",
2311
"version" : "3.6.2",
@@ -195,36 +183,6 @@
195183
}
196184
],
197185
"dependencies" : [
198-
{
199-
"ref" : "pkg:pypi/root",
200-
"dependsOn" : [
201-
"pkg:pypi/[email protected]",
202-
"pkg:pypi/[email protected]",
203-
"pkg:pypi/[email protected]",
204-
"pkg:pypi/[email protected]",
205-
"pkg:pypi/[email protected]",
206-
"pkg:pypi/[email protected]",
207-
"pkg:pypi/[email protected]",
208-
"pkg:pypi/[email protected]",
209-
"pkg:pypi/[email protected]",
210-
"pkg:pypi/[email protected]",
211-
"pkg:pypi/[email protected]",
212-
"pkg:pypi/[email protected]",
213-
"pkg:pypi/[email protected]",
214-
"pkg:pypi/[email protected]",
215-
"pkg:pypi/[email protected]",
216-
"pkg:pypi/[email protected]",
217-
"pkg:pypi/[email protected]",
218-
"pkg:pypi/[email protected]",
219-
"pkg:pypi/[email protected]",
220-
"pkg:pypi/[email protected]",
221-
"pkg:pypi/[email protected]",
222-
"pkg:pypi/[email protected]",
223-
"pkg:pypi/[email protected]",
224-
"pkg:pypi/[email protected]",
225-
"pkg:pypi/[email protected]"
226-
]
227-
},
228186
{
229187
"ref" : "pkg:pypi/[email protected]",
230188
"dependsOn" : [ ]

test/providers/tst_manifests/pip/pip_requirements_txt_ignore/expected_stack_sbom.json

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,9 @@
33
"specVersion": "1.4",
44
"version": 1,
55
"metadata": {
6-
"timestamp": "2023-10-01T00:00:00.000Z",
7-
"component": {
8-
"name": "root",
9-
"purl": "pkg:pypi/root",
10-
"type": "application",
11-
"bom-ref": "pkg:pypi/root"
12-
}
6+
"timestamp": "2023-10-01T00:00:00.000Z"
137
},
148
"components": [
15-
{
16-
"name": "root",
17-
"purl": "pkg:pypi/root",
18-
"type": "application",
19-
"bom-ref": "pkg:pypi/root"
20-
},
219
{
2210
"name": "anyio",
2311
"version": "3.6.2",
@@ -195,36 +183,6 @@
195183
}
196184
],
197185
"dependencies": [
198-
{
199-
"ref": "pkg:pypi/root",
200-
"dependsOn": [
201-
"pkg:pypi/[email protected]",
202-
"pkg:pypi/[email protected]",
203-
"pkg:pypi/[email protected]",
204-
"pkg:pypi/[email protected]",
205-
"pkg:pypi/[email protected]",
206-
"pkg:pypi/[email protected]",
207-
"pkg:pypi/[email protected]",
208-
"pkg:pypi/[email protected]",
209-
"pkg:pypi/[email protected]",
210-
"pkg:pypi/[email protected]",
211-
"pkg:pypi/[email protected]",
212-
"pkg:pypi/[email protected]",
213-
"pkg:pypi/[email protected]",
214-
"pkg:pypi/[email protected]",
215-
"pkg:pypi/[email protected]",
216-
"pkg:pypi/[email protected]",
217-
"pkg:pypi/[email protected]",
218-
"pkg:pypi/[email protected]",
219-
"pkg:pypi/[email protected]",
220-
"pkg:pypi/[email protected]",
221-
"pkg:pypi/[email protected]",
222-
"pkg:pypi/[email protected]",
223-
"pkg:pypi/[email protected]",
224-
"pkg:pypi/[email protected]",
225-
"pkg:pypi/[email protected]"
226-
]
227-
},
228186
{
229187
"ref": "pkg:pypi/[email protected]",
230188
"dependsOn": [

0 commit comments

Comments
 (0)