Skip to content

Commit

Permalink
Flatten audit graph (#736)
Browse files Browse the repository at this point in the history
  • Loading branch information
omerzi authored Apr 5, 2023
1 parent 078de27 commit 8ceb2b5
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 5 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230404093618-e0aec23ce1c3
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230405060150-93ab1ed18406

replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230403064815-ea83b399ac8e

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ github.com/jfrog/build-info-go v1.8.9-0.20230403064815-ea83b399ac8e h1:MB5u0Kbq9
github.com/jfrog/build-info-go v1.8.9-0.20230403064815-ea83b399ac8e/go.mod h1:HIrpwf4p4XHpAx+N+rb8SX9yrWYWs7X4rT/s0GOJfW8=
github.com/jfrog/gofrog v1.2.5 h1:jCgJC0iGQ8bU7jCC+YEFJTNINyngApIrhd8BjZAVRIE=
github.com/jfrog/gofrog v1.2.5/go.mod h1:o00tSRff6IapTgaCMuX1Cs9MH08Y1JqnsKgRtx91Gc4=
github.com/jfrog/jfrog-client-go v1.28.1-0.20230404093618-e0aec23ce1c3 h1:I2cxiZvfEF5Gc5sL3nPsV+04ONjLVVG7v4nUn6HAVeU=
github.com/jfrog/jfrog-client-go v1.28.1-0.20230404093618-e0aec23ce1c3/go.mod h1:XJhlPfi6iayIVc2SQ/RbztDQOnbnNatsUSQr7wbJ8Ag=
github.com/jfrog/jfrog-client-go v1.28.1-0.20230405060150-93ab1ed18406 h1:3YQ8lTZGTRASgYp2wkWCid18UcAg//Bkrm8zpjlN4b0=
github.com/jfrog/jfrog-client-go v1.28.1-0.20230405060150-93ab1ed18406/go.mod h1:XJhlPfi6iayIVc2SQ/RbztDQOnbnNatsUSQr7wbJ8Ag=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
85 changes: 85 additions & 0 deletions xray/audit/commonutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,88 @@ func GetExecutableVersion(executable string) (version string, err error) {
log.Debug(fmt.Sprintf("Used %q version: %s", executable, version))
return
}

// BuildImpactPathsForScanResponse builds the full impact paths for each vulnerability found in the scanResult argument, using the dependencyTrees argument.
// Returns the updated services.ScanResponse slice.
func BuildImpactPathsForScanResponse(scanResult []services.ScanResponse, dependencyTrees []*services.GraphNode) []services.ScanResponse {
for _, result := range scanResult {
if len(result.Vulnerabilities) > 0 {
buildVulnerabilitiesImpactPaths(result.Vulnerabilities, dependencyTrees)
}
if len(result.Violations) > 0 {
buildViolationsImpactPaths(result.Violations, dependencyTrees)
}
if len(result.Licenses) > 0 {
buildLicensesImpactPaths(result.Licenses, dependencyTrees)
}
}
return scanResult
}

// Initialize map of issues to their components with empty impact paths
func fillImpactPathsMapWithIssues(issuesImpactPathsMap map[string]*services.Component, components map[string]services.Component) {
for dependencyName := range components {
emptyPathsComponent := &services.Component{
ImpactPaths: [][]services.ImpactPathNode{},
FixedVersions: components[dependencyName].FixedVersions,
Cpes: components[dependencyName].Cpes,
}
issuesImpactPathsMap[dependencyName] = emptyPathsComponent
}
}

// Set the impact paths for each issue in the map
func buildImpactPaths(issuesImpactPathsMap map[string]*services.Component, dependencyTrees []*services.GraphNode) {
for _, dependency := range dependencyTrees {
setPathsForIssues(dependency, issuesImpactPathsMap, []services.ImpactPathNode{})
}
}

func buildVulnerabilitiesImpactPaths(vulnerabilities []services.Vulnerability, dependencyTrees []*services.GraphNode) {
issuesMap := make(map[string]*services.Component)
for _, vulnerability := range vulnerabilities {
fillImpactPathsMapWithIssues(issuesMap, vulnerability.Components)
}
buildImpactPaths(issuesMap, dependencyTrees)
for i := range vulnerabilities {
updateComponentsWithImpactPaths(vulnerabilities[i].Components, issuesMap)
}
}

func buildViolationsImpactPaths(violations []services.Violation, dependencyTrees []*services.GraphNode) {
issuesMap := make(map[string]*services.Component)
for _, violation := range violations {
fillImpactPathsMapWithIssues(issuesMap, violation.Components)
}
buildImpactPaths(issuesMap, dependencyTrees)
for i := range violations {
updateComponentsWithImpactPaths(violations[i].Components, issuesMap)
}
}

func buildLicensesImpactPaths(licenses []services.License, dependencyTrees []*services.GraphNode) {
issuesMap := make(map[string]*services.Component)
for _, license := range licenses {
fillImpactPathsMapWithIssues(issuesMap, license.Components)
}
buildImpactPaths(issuesMap, dependencyTrees)
for i := range licenses {
updateComponentsWithImpactPaths(licenses[i].Components, issuesMap)
}
}

func updateComponentsWithImpactPaths(components map[string]services.Component, issuesMap map[string]*services.Component) {
for dependencyName := range components {
components[dependencyName] = *issuesMap[dependencyName]
}
}

func setPathsForIssues(dependency *services.GraphNode, issuesImpactPathsMap map[string]*services.Component, pathFromRoot []services.ImpactPathNode) {
pathFromRoot = append(pathFromRoot, services.ImpactPathNode{ComponentId: dependency.Id})
if _, exists := issuesImpactPathsMap[dependency.Id]; exists {
issuesImpactPathsMap[dependency.Id].ImpactPaths = append(issuesImpactPathsMap[dependency.Id].ImpactPaths, pathFromRoot)
}
for _, depChild := range dependency.Nodes {
setPathsForIssues(depChild, issuesImpactPathsMap, pathFromRoot)
}
}
132 changes: 132 additions & 0 deletions xray/audit/commonutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package audit

import (
"github.com/jfrog/jfrog-client-go/xray/services"
"github.com/stretchr/testify/assert"
"testing"
)

func TestSetPathsForIssues(t *testing.T) {
// Create a test dependency tree
rootNode := &services.GraphNode{Id: "root"}
childNode1 := &services.GraphNode{Id: "child1"}
childNode2 := &services.GraphNode{Id: "child2"}
childNode3 := &services.GraphNode{Id: "child3"}
childNode4 := &services.GraphNode{Id: "child4"}
childNode5 := &services.GraphNode{Id: "child5"}
rootNode.Nodes = []*services.GraphNode{childNode1, childNode2, childNode3}
childNode2.Nodes = []*services.GraphNode{childNode4}
childNode3.Nodes = []*services.GraphNode{childNode5}

// Create a test issues map
issuesMap := make(map[string]*services.Component)
issuesMap["child1"] = &services.Component{ImpactPaths: [][]services.ImpactPathNode{}}
issuesMap["child4"] = &services.Component{ImpactPaths: [][]services.ImpactPathNode{}}
issuesMap["child5"] = &services.Component{ImpactPaths: [][]services.ImpactPathNode{}}

// Call setPathsForIssues with the test data
setPathsForIssues(rootNode, issuesMap, []services.ImpactPathNode{})

// Check the results
assert.Equal(t, issuesMap["child1"].ImpactPaths[0][0].ComponentId, "root")
assert.Equal(t, issuesMap["child1"].ImpactPaths[0][1].ComponentId, "child1")

assert.Equal(t, issuesMap["child4"].ImpactPaths[0][0].ComponentId, "root")
assert.Equal(t, issuesMap["child4"].ImpactPaths[0][1].ComponentId, "child2")
assert.Equal(t, issuesMap["child4"].ImpactPaths[0][2].ComponentId, "child4")

assert.Equal(t, issuesMap["child5"].ImpactPaths[0][0].ComponentId, "root")
assert.Equal(t, issuesMap["child5"].ImpactPaths[0][1].ComponentId, "child3")
assert.Equal(t, issuesMap["child5"].ImpactPaths[0][2].ComponentId, "child5")
}

func TestUpdateVulnerableComponent(t *testing.T) {
// Create test data
components := map[string]services.Component{
"dependency1": {
FixedVersions: []string{"1.0.0"},
ImpactPaths: [][]services.ImpactPathNode{},
},
}
dependencyName := "dependency1"
issuesMap := map[string]*services.Component{
dependencyName: {
FixedVersions: []string{"1.0.0"},
ImpactPaths: [][]services.ImpactPathNode{
{{ComponentId: "dependency2"}},
},
},
}

updateComponentsWithImpactPaths(components, issuesMap)

// Check the result
expected := services.Component{
FixedVersions: []string{"1.0.0"},
ImpactPaths: issuesMap[dependencyName].ImpactPaths,
}
assert.Equal(t, expected, components[dependencyName])
}

func TestBuildImpactPaths(t *testing.T) {
// create sample scan result and dependency trees
scanResult := []services.ScanResponse{
{
Vulnerabilities: []services.Vulnerability{
{
Components: map[string]services.Component{
"dep1": {
FixedVersions: []string{"1.2.3"},
Cpes: []string{"cpe:/o:vendor:product:1.2.3"},
},
},
},
},
Violations: []services.Violation{
{
Components: map[string]services.Component{
"dep2": {
FixedVersions: []string{"4.5.6"},
Cpes: []string{"cpe:/o:vendor:product:4.5.6"},
},
},
},
},
Licenses: []services.License{
{
Components: map[string]services.Component{
"dep3": {
FixedVersions: []string{"7.8.9"},
Cpes: []string{"cpe:/o:vendor:product:7.8.9"},
},
},
},
},
},
}
dependencyTrees := []*services.GraphNode{
{
Id: "dep1",
Nodes: []*services.GraphNode{
{
Id: "dep2",
Nodes: []*services.GraphNode{
{
Id: "dep3",
Nodes: []*services.GraphNode{},
},
},
},
},
},
}

scanResult = BuildImpactPathsForScanResponse(scanResult, dependencyTrees)
// assert that the components were updated with impact paths
expectedImpactPaths := [][]services.ImpactPathNode{{{ComponentId: "dep1"}}}
assert.Equal(t, expectedImpactPaths, scanResult[0].Vulnerabilities[0].Components["dep1"].ImpactPaths)
expectedImpactPaths = [][]services.ImpactPathNode{{{ComponentId: "dep1"}, {ComponentId: "dep2"}}}
assert.Equal(t, expectedImpactPaths, scanResult[0].Violations[0].Components["dep2"].ImpactPaths)
expectedImpactPaths = [][]services.ImpactPathNode{{{ComponentId: "dep1"}, {ComponentId: "dep2"}, {ComponentId: "dep3"}}}
assert.Equal(t, expectedImpactPaths, scanResult[0].Licenses[0].Components["dep3"].ImpactPaths)
}
8 changes: 6 additions & 2 deletions xray/commands/audit/generic/auditmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Params struct {
xrayGraphScanParams services.XrayGraphScanParams
serverDetails *config.ServerDetails
progress ioUtils.ProgressMgr
dependencyTrees []*services.GraphNode
ignoreConfigFile bool
excludeTestDeps bool
insecureTls bool
Expand Down Expand Up @@ -240,6 +241,7 @@ func doAudit(params *Params) (results []services.ScanResponse, isMultipleRoot bo
errorList.WriteString(fmt.Sprintf("'%s' audit command failed:\n%s\n", tech, e.Error()))
continue
}
techResults = audit.BuildImpactPathsForScanResponse(techResults, params.dependencyTrees)
results = append(results, techResults...)
isMultipleRoot = len(dependencyTrees) > 1
}
Expand Down Expand Up @@ -273,8 +275,10 @@ func getTechDependencyTree(params *Params, tech coreutils.Technology) (dependenc
default:
e = errorutils.CheckError(fmt.Errorf("%s is currently not supported", string(tech)))
}

return dependencyTrees, e
// Save the full dependencyTree to build impact paths for vulnerable dependencies
params.dependencyTrees = dependencyTrees
// Flatten the graph to speed up the ScanGraph request
return services.FlattenGraph(dependencyTrees), e
}

func getJavaDependencyTree(params *Params, tech coreutils.Technology) ([]*services.GraphNode, error) {
Expand Down

0 comments on commit 8ceb2b5

Please sign in to comment.