Skip to content
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
42 changes: 42 additions & 0 deletions sca/bom/buildinfo/technologies/java/sbom_regression_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package java

import (
"testing"

"github.com/jfrog/jfrog-cli-security/utils/results"
"github.com/jfrog/jfrog-cli-security/utils/techutils"
"github.com/stretchr/testify/require"
)

// Exercise the Maven plugin JSON -> module graphs -> SBOM pipeline without a server.
func TestMavenModuleExclusionSbom(t *testing.T) {
excluded := `{"root":"example:a:1.0","nodes":{
"example:a:1.0":{"children":["example:x:1.0"]},
"example:x:1.0":{"children":[]}}}`
included := `{"root":"example:b:1.0","nodes":{
"example:b:1.0":{"children":["example:x:1.0"]},
"example:x:1.0":{"children":["example:y:1.0"]},
"example:y:1.0":{"children":[]}}}`
for _, modules := range [][]string{{excluded, included}, {included, excluded}} {
trees, unique, _, _, err := getGraphAndPluginDepsFromDepTree(writeDepTreeModuleFiles(t, modules...))
require.NoError(t, err)
components, dependencies := results.DepsTreeToSbom(trees...)
require.NotNil(t, components)
refs := []string{}
for _, component := range *components {
refs = append(refs, component.BOMRef)
}
expected := []string{}
for id := range unique {
expected = append(expected, techutils.XrayComponentIdToCdxComponentRef(id))
}
require.Len(t, expected, 4)
require.ElementsMatch(t, expected, refs)
require.NotNil(t, dependencies)
edges := map[string][]string{}
for _, dep := range *dependencies {
edges[dep.Ref] = *dep.Dependencies
}
require.ElementsMatch(t, []string{techutils.XrayComponentIdToCdxComponentRef("gav://example:y:1.0")}, edges[techutils.XrayComponentIdToCdxComponentRef("gav://example:x:1.0")])
}
}
12 changes: 7 additions & 5 deletions utils/results/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,9 @@ func SearchTargetResultsByRelativePath(relativeTarget string, resultsToCompare *
}

func DepsTreeToSbom(trees ...*xrayUtils.GraphNode) (components *[]cyclonedx.Component, dependencies *[]cyclonedx.Dependency) {
parsed := datastructures.MakeSet[string]()
// Distinct occurrences of the same component can have different children.
// Visit each node object; AppendComponents and AppendDependencies merge by ref.
parsed := datastructures.MakeSet[*xrayUtils.GraphNode]()
bom := cyclonedx.NewBOM()
bom.Components = &[]cyclonedx.Component{}
bom.Dependencies = &[]cyclonedx.Dependency{}
Expand All @@ -966,15 +968,15 @@ func DepsTreeToSbom(trees ...*xrayUtils.GraphNode) (components *[]cyclonedx.Comp
return bom.Components, bom.Dependencies
}

func getDataFromNode(node *xrayUtils.GraphNode, parsed *datastructures.Set[string]) (*[]cyclonedx.Component, *[]cyclonedx.Dependency) {
func getDataFromNode(node *xrayUtils.GraphNode, parsed *datastructures.Set[*xrayUtils.GraphNode]) (*[]cyclonedx.Component, *[]cyclonedx.Dependency) {
bom := cyclonedx.NewBOM()
bom.Components = &[]cyclonedx.Component{}
bom.Dependencies = &[]cyclonedx.Dependency{}
if parsed.Exists(node.Id) {
// The node was already parsed, no need to parse it again
if parsed.Exists(node) {
// This exact node was already traversed (including shared subgraphs and cycles).
return bom.Components, bom.Dependencies
}
parsed.Add(node.Id)
parsed.Add(node)
// Create a new component and add it to the sbom
*bom.Components = append(*bom.Components, CreateScaComponentFromXrayCompId(node.Id))
if len(node.Nodes) > 0 {
Expand Down
41 changes: 41 additions & 0 deletions utils/results/sbom_occurrences_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package results

import (
"testing"

"github.com/jfrog/jfrog-cli-security/utils/techutils"
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
"github.com/stretchr/testify/require"
)

func TestDepsTreeToSbomDistinctOccurrences(t *testing.T) {
node := func(name string, children ...*xrayUtils.GraphNode) *xrayUtils.GraphNode {
return &xrayUtils.GraphNode{Id: "gav://example:" + name + ":1.0", Nodes: children}
}
// The same edge occurs twice, but only its later occurrence has a grandchild.
a := node("a", node("x", node("y")))
b := node("b", node("x", node("y", node("z"))))
c := node("c", node("x", node("w")))
for _, roots := range [][]*xrayUtils.GraphNode{{a, b, c}, {c, b, a}, {b, a, c}} {
components, dependencies := DepsTreeToSbom(roots...)
require.NotNil(t, components)
require.Len(t, *components, 7)
edges := map[string][]string{}
for _, dep := range *dependencies {
edges[dep.Ref] = *dep.Dependencies
}
ref := func(name string) string { return techutils.XrayComponentIdToCdxComponentRef(node(name).Id) }
require.ElementsMatch(t, []string{ref("y"), ref("w")}, edges[ref("x")])
require.ElementsMatch(t, []string{ref("z")}, edges[ref("y")])
require.Len(t, *dependencies, 5)
}
}

func TestDepsTreeToSbomPointerCycle(t *testing.T) {
a := &xrayUtils.GraphNode{Id: "gav://example:a:1.0"}
b := &xrayUtils.GraphNode{Id: "gav://example:b:1.0", Nodes: []*xrayUtils.GraphNode{a}}
a.Nodes = []*xrayUtils.GraphNode{b}
components, dependencies := DepsTreeToSbom(a, b)
require.Len(t, *components, 2)
require.Len(t, *dependencies, 2)
}
Loading