Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: couple of related problems with exhortignore in go.mod #56

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/cyclone_dx_sbom.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,5 +227,30 @@ export default class CycloneDxSbom {
return this
}

/** This method gets a component object, and a string name, and checks if the name is a substring of the component' purl.
* @param {} component to search in its dependencies
* @param {String} name to be checked.
*
* @return {boolean}
*/
checkIfPackageInsideDependsOnList(component, name)
{

let dependencyIndex = this.getDependencyIndex(component.purl)
if(dependencyIndex < 0)
{
return false
}

//Only if the dependency doesn't exists on the dependency list of dependency, then add it to this list.
if(this.dependencies[dependencyIndex].dependsOn.findIndex(dep => dep.includes(name) ) >= 0)
{
return true;
}
else {
return false
}
}


}
31 changes: 23 additions & 8 deletions src/providers/golang_gomodules.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function ignoredLine(line) {
&& !trimmedRow.startsWith("exclude ") && !trimmedRow.startsWith("replace ") && !trimmedRow.startsWith("retract ") && !trimmedRow.startsWith("use ")
&& !trimmedRow.includes("=>"))
{
if( trimmedRow.startsWith("require ") || trimmedRow.match("^[a-z./-]+\\s[vV][0-9]\\.[0-9](\\.[0-9])?.*"))
if( trimmedRow.startsWith("require ") || trimmedRow.match("^[a-z.0-9/-]+\\s{1,2}[vV][0-9]\\.[0-9](\\.[0-9]){0,2}.*"))
{
result = true
}
Expand Down Expand Up @@ -150,7 +150,7 @@ function extractPackageName(line) {
function getIgnoredDeps(manifest) {
let goMod = fs.readFileSync(manifest).toString().trim()
let lines = goMod.split(EOL);
return lines.filter(line => ignoredLine(line)).map(line=> extractPackageName(line)).map(dep => toPurl(dep," ",undefined))
return lines.filter(line => ignoredLine(line)).map(line=> extractPackageName(line)).map(dep => toPurl(dep,/[ ]{1,3}/,undefined))
}

/**
Expand All @@ -163,6 +163,17 @@ function dependencyNotIgnored(allIgnoredDeps, purl) {
return allIgnoredDeps.find(element => element.toString() === purl.toString()) === undefined;
}

function enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps, sbom) {
// In case there is a dependency commented with exhortignore , but it is still in the list of direct dependencies of root, then
// the reason for that is that go mod graph changed automatically the version of package/module to different version, and because of
// mismatch between the version in go.mod manifest and go mod graph, it wasn't delete ==> in this case need to remove from sbom according to name only.
ignoredDeps.forEach(packageUrl => {
if (sbom.checkIfPackageInsideDependsOnList(sbom.getRoot(), packageUrl.name)) {
sbom.filterIgnoredDeps(ignoredDeps.filter(purl => purl.name === packageUrl.name).map(purl => purl.name))
}
})
}

/**
* Create SBOM json string for go Module.
* @param {string} manifest - path for go.mod
Expand All @@ -185,7 +196,8 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
let options = {cwd: manifestDir}
let goGraphOutput
goGraphOutput = getGoModGraph(goGraphCommand, options);
let allIgnoredDeps = getIgnoredDeps(manifest);
let ignoredDeps = getIgnoredDeps(manifest);
let allIgnoredDeps = ignoredDeps.map((dep) => dep.toString())
let sbom = new Sbom();
let rows = goGraphOutput.split(EOL);
let root = getParentVertexFromEdge(rows[0])
Expand All @@ -205,20 +217,23 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
sourceComponent = sbom.purlToComponent(source);
}
let target = toPurl(getChildVertexFromEdge(row), "@", undefined);
if(dependencyNotIgnored(allIgnoredDeps, target) && dependencyNotIgnored(allIgnoredDeps, source) ) {
sbom.addDependency(sourceComponent, target)
}
sbom.addDependency(sourceComponent, target)

})
// at the end, filter out all ignored dependencies including versions.
sbom.filterIgnoredDepsIncludingVersion(allIgnoredDeps)
enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps, sbom);
} else {
let directDependencies = rows.filter(row => row.startsWith(root));
directDependencies.forEach(pair => {
let dependency = getChildVertexFromEdge(pair)
let depPurl = toPurl(dependency, "@", undefined);
let mainModuleComponent = sbom.purlToComponent(mainModule);
if(dependencyNotIgnored(allIgnoredDeps, depPurl)) {
if(dependencyNotIgnored(ignoredDeps, depPurl)) {
sbom.addDependency(mainModuleComponent, depPurl)
}
})
enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps,sbom)
}

return sbom.getAsJsonString()
Expand All @@ -229,7 +244,7 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
* Utility function for creating Purl String

* @param {string }dependency the name of the artifact, can include a namespace(group) or not - namespace/artifactName.
* @param {string} delimiter delimiter between name of dependency and version
* @param {RegExp} delimiter delimiter between name of dependency and version
* @param { object } qualifiers - contains key values related to the go environment
* @private
* @returns {PackageURL|null} PackageUrl Object ready to be used in SBOM
Expand Down
11 changes: 11 additions & 0 deletions src/sbom.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ export default class Sbom {
purlToComponent(purl){
return this.sbomModel.purlToComponent(purl)
}

/** This method gets a component object, and a string name, and checks if the name is a substring of the component' purl.
* @param {} component to search in its dependencies
* @param {String} name to be checked.
*
* @return {boolean}
*/
checkIfPackageInsideDependsOnList(component, name)
{
return this.sbomModel.checkIfPackageInsideDependsOnList(component,name)
}
}


Expand Down
3 changes: 2 additions & 1 deletion test/providers/golang_gomodules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ suite('testing the golang-go-modules data provider', () => {
"go_mod_light_no_ignore",
"go_mod_no_ignore",
"go_mod_with_ignore",
"go_mod_test_ignore"
"go_mod_test_ignore",
"go_mod_with_all_ignore"
].forEach(testCase => {
let scenario = testCase.replace('go_mod_', '').replaceAll('_', ' ')
test(`verify go.mod sbom provided for stack analysis with scenario ${scenario}`, async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"version": 1,
"metadata": {
"timestamp": "2023-08-07T00:00:00.000Z",
"component": {
"group": "github.com/devfile-samples",
"name": "devfile-sample-go-basic",
"version": "v0.0.0",
"purl": "pkg:golang/github.com/devfile-samples/[email protected]",
"type": "application",
"bom-ref": "pkg:golang/github.com/devfile-samples/[email protected]"
}
},
"components": [
{
"group": "github.com/devfile-samples",
"name": "devfile-sample-go-basic",
"version": "v0.0.0",
"purl": "pkg:golang/github.com/devfile-samples/[email protected]",
"type": "application",
"bom-ref": "pkg:golang/github.com/devfile-samples/[email protected]"
}
],
"dependencies": [
{
"ref": "pkg:golang/github.com/devfile-samples/[email protected]",
"dependsOn": []
}
]
}
Loading