Skip to content

Commit 7405e49

Browse files
authored
SBOM with CycloneDx struct (#467)
1 parent 50ee5ab commit 7405e49

File tree

24 files changed

+1469
-433
lines changed

24 files changed

+1469
-433
lines changed

commands/audit/audit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func createJasScansTasks(auditParallelRunner *utils.SecurityParallelRunner, scan
357357
Module: *module,
358358
ConfigProfile: auditParams.AuditBasicParams.GetConfigProfile(),
359359
ScansToPerform: auditParams.ScansToPerform(),
360-
SourceResultsToCompare: scanner.GetResultsToCompare(utils.GetRelativePath(targetResult.Target, scanResults.GetCommonParentPath())),
360+
SourceResultsToCompare: scanner.GetResultsToCompareByRelativePath(utils.GetRelativePath(targetResult.Target, scanResults.GetCommonParentPath())),
361361
SecretsScanType: secrets.SecretsScannerType,
362362
DirectDependencies: auditParams.DirectDependencies(),
363363
ThirdPartyApplicabilityScan: auditParams.thirdPartyApplicabilityScan,

commands/audit/audit_test.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ package audit
22

33
import (
44
"fmt"
5-
commonCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
6-
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
7-
configTests "github.com/jfrog/jfrog-cli-security/tests"
8-
securityTestUtils "github.com/jfrog/jfrog-cli-security/tests/utils"
9-
clientTests "github.com/jfrog/jfrog-client-go/utils/tests"
105
"net/http"
116
"path/filepath"
127
"sort"
138
"strings"
149
"testing"
1510

11+
"github.com/CycloneDX/cyclonedx-go"
12+
commonCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
13+
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
14+
configTests "github.com/jfrog/jfrog-cli-security/tests"
15+
securityTestUtils "github.com/jfrog/jfrog-cli-security/tests/utils"
16+
clientTests "github.com/jfrog/jfrog-client-go/utils/tests"
17+
1618
"github.com/stretchr/testify/assert"
1719

1820
"github.com/jfrog/jfrog-cli-security/tests/validations"
@@ -957,25 +959,30 @@ func TestAudit_DiffScanFlow(t *testing.T) {
957959
Target: tempDirPath,
958960
Technology: techutils.Pip,
959961
},
960-
Sbom: results.Sbom{
961-
Components: []results.SbomEntry{
962-
{
963-
Component: "werkzeug",
964-
Version: "1.0.2",
965-
Type: "Python",
966-
XrayType: "pypi",
967-
},
968-
{
969-
Component: "pyyaml",
970-
Version: "5.2",
971-
Type: "Python",
972-
XrayType: "pypi",
973-
},
974-
{
975-
Component: "wasabi",
976-
Version: "1.1.3",
977-
Type: "Python",
978-
XrayType: "pypi",
962+
ScaResults: &results.ScaScanResults{
963+
Sbom: &cyclonedx.BOM{
964+
Components: &[]cyclonedx.Component{
965+
{
966+
PackageURL: "pkg:pypi/[email protected]",
967+
BOMRef: "pypi:[email protected]",
968+
Name: "werkzeug",
969+
Version: "1.0.2",
970+
Type: "Python",
971+
},
972+
{
973+
PackageURL: "pkg:pypi/[email protected]",
974+
BOMRef: "pypi:[email protected]",
975+
Name: "pyyaml",
976+
Version: "5.2",
977+
Type: "Python",
978+
},
979+
{
980+
PackageURL: "pkg:pypi/[email protected]",
981+
BOMRef: "pypi:[email protected]",
982+
Name: "wasabi",
983+
Version: "1.1.3",
984+
Type: "Python",
985+
},
979986
},
980987
},
981988
},

commands/audit/scarunner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func buildDepTreeAndRunScaScan(auditParallelRunner *utils.SecurityParallelRunner
9393
// First scan, no diff to compare
9494
log.Debug(fmt.Sprintf("Diff scan - calculated dependencies tree for target %s, skipping scan part", targetResult.Target))
9595
continue
96-
} else if treeResult, bdtErr = buildinfo.GetDiffDependencyTree(targetResult, results.SearchTargetResultsByPath(utils.GetRelativePath(targetResult.Target, cmdResults.GetCommonParentPath()), auditParams.resultsToCompare), treeResult.FullDepTrees...); bdtErr != nil {
96+
} else if treeResult, bdtErr = buildinfo.GetDiffDependencyTree(targetResult, results.SearchTargetResultsByRelativePath(utils.GetRelativePath(targetResult.Target, cmdResults.GetCommonParentPath()), auditParams.resultsToCompare), treeResult.FullDepTrees...); bdtErr != nil {
9797
_ = targetResult.AddTargetError(fmt.Errorf("failed to build diff dependency tree in source branch: %s", bdtErr.Error()), auditParams.AllowPartialResults())
9898
continue
9999
}

commands/scan/scan.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ const (
5353
BypassArchiveLimitsMinXrayVersion = "3.59.0"
5454
indexingCommand = "graph"
5555
fileNotSupportedExitCode = 3
56-
typeJASPackageScanTypeDocker = "docker"
57-
typeJASPackageScanTypeGeneric = "generic"
5856
)
5957

6058
type ScanCommand struct {

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/jfrog/jfrog-cli-security
33
go 1.24.2
44

55
require (
6+
github.com/CycloneDX/cyclonedx-go v0.9.2
67
github.com/beevik/etree v1.4.0
78
github.com/go-git/go-git/v5 v5.14.0
89
github.com/google/go-github/v56 v56.0.0
@@ -16,6 +17,7 @@ require (
1617
github.com/jfrog/jfrog-client-go v1.54.1
1718
github.com/magiconair/properties v1.8.9
1819
github.com/owenrumney/go-sarif/v3 v3.1.4
20+
github.com/package-url/packageurl-go v0.1.3
1921
github.com/stretchr/testify v1.10.0
2022
github.com/urfave/cli v1.22.16
2123
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74
@@ -28,7 +30,6 @@ require (
2830
require (
2931
dario.cat/mergo v1.0.1 // indirect
3032
github.com/BurntSushi/toml v1.4.0 // indirect
31-
github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect
3233
github.com/Microsoft/go-winio v0.6.2 // indirect
3334
github.com/ProtonMail/go-crypto v1.1.6 // indirect
3435
github.com/VividCortex/ewma v1.2.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
187187
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
188188
github.com/owenrumney/go-sarif/v3 v3.1.4 h1:lqx5Cb7162BC+FuAgJZq8A8XXP4XMw7XoAPZl9iqlQs=
189189
github.com/owenrumney/go-sarif/v3 v3.1.4/go.mod h1:Olt8kHDlC+ruWzRfmgIQUD+2hoAk6A6vT+ljDUbae2s=
190+
github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs=
191+
github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0=
190192
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
191193
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
192194
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=

jas/common.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ func getJasEnvVars(serverDetails *config.ServerDetails, validateSecrets bool, di
140140
return utils.MergeMaps(utils.ToEnvVarsMap(os.Environ()), amBasicVars, vars), nil
141141
}
142142

143-
func (js *JasScanner) GetResultsToCompare(target string) (resultsToCompare *results.TargetResults) {
144-
return results.SearchTargetResultsByPath(target, js.ResultsToCompare)
143+
func (js *JasScanner) GetResultsToCompareByRelativePath(relativeTarget string) (resultsToCompare *results.TargetResults) {
144+
return results.SearchTargetResultsByRelativePath(relativeTarget, js.ResultsToCompare)
145145
}
146146

147147
func CreateJFrogAppsConfig(workingDirs []string) (*jfrogappsconfig.JFrogAppsConfig, error) {

jas/common_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ func TestGetResultsToCompare(t *testing.T) {
589589
for _, testCase := range testCases {
590590
t.Run(testCase.name, func(t *testing.T) {
591591
scanner := &JasScanner{ResultsToCompare: testCase.ResultsToCompare}
592-
assert.Equal(t, testCase.expectedTarget, scanner.GetResultsToCompare(testCase.target))
592+
assert.Equal(t, testCase.expectedTarget, scanner.GetResultsToCompareByRelativePath(testCase.target))
593593
})
594594
}
595595
}

sca/bom/buildinfo/buildinfobom.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"os"
66
"time"
77

8+
"github.com/CycloneDX/cyclonedx-go"
89
"github.com/jfrog/gofrog/datastructures"
910
clientutils "github.com/jfrog/jfrog-client-go/utils"
1011
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1112
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
1213
"github.com/jfrog/jfrog-client-go/utils/log"
13-
xrayCmdUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
14+
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
1415

1516
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
1617

@@ -35,8 +36,8 @@ import (
3536
)
3637

3738
type DependencyTreeResult struct {
38-
FlatTree *xrayCmdUtils.GraphNode
39-
FullDepTrees []*xrayCmdUtils.GraphNode
39+
FlatTree *xrayUtils.GraphNode
40+
FullDepTrees []*xrayUtils.GraphNode
4041
DownloadUrls map[string]string
4142
}
4243

@@ -56,7 +57,9 @@ func BuildDependencyTree(scan *results.TargetResults, params technologies.BuildI
5657
if treeResult.FlatTree == nil || len(treeResult.FlatTree.Nodes) == 0 {
5758
return nil, errorutils.CheckErrorf("no dependencies were found. Please try to build your project and re-run the audit command")
5859
}
59-
scan.SetSbom(results.DepTreeToSbom(treeResult.FullDepTrees))
60+
sbom := cyclonedx.NewBOM()
61+
sbom.Components, sbom.Dependencies = results.DepsTreeToSbom(treeResult.FullDepTrees...)
62+
scan.SetSbom(sbom)
6063
return &treeResult, nil
6164
}
6265

@@ -181,44 +184,47 @@ func SetResolutionRepoInParamsIfExists(params *technologies.BuildInfoBomGenerato
181184
return
182185
}
183186

184-
func createFlatTreeWithTypes(uniqueDeps map[string]*xray.DepTreeNode) *xrayCmdUtils.GraphNode {
185-
var uniqueNodes []*xrayCmdUtils.GraphNode
187+
func createFlatTreeWithTypes(uniqueDeps map[string]*xray.DepTreeNode) *xrayUtils.GraphNode {
188+
var uniqueNodes []*xrayUtils.GraphNode
186189
for uniqueDep, nodeAttr := range uniqueDeps {
187-
node := &xrayCmdUtils.GraphNode{Id: uniqueDep}
190+
node := &xrayUtils.GraphNode{Id: uniqueDep}
188191
if nodeAttr != nil {
189192
node.Types = nodeAttr.Types
190193
node.Classifier = nodeAttr.Classifier
191194
}
192195
uniqueNodes = append(uniqueNodes, node)
193196
}
194-
return &xrayCmdUtils.GraphNode{Id: "root", Nodes: uniqueNodes}
197+
return &xrayUtils.GraphNode{Id: "root", Nodes: uniqueNodes}
195198
}
196199

197-
func createFlatTree(uniqueDeps []string) *xrayCmdUtils.GraphNode {
198-
uniqueNodes := []*xrayCmdUtils.GraphNode{}
200+
func createFlatTree(uniqueDeps []string) *xrayUtils.GraphNode {
201+
uniqueNodes := []*xrayUtils.GraphNode{}
199202
for _, uniqueDep := range uniqueDeps {
200-
uniqueNodes = append(uniqueNodes, &xrayCmdUtils.GraphNode{Id: uniqueDep})
203+
uniqueNodes = append(uniqueNodes, &xrayUtils.GraphNode{Id: uniqueDep})
201204
}
202-
return &xrayCmdUtils.GraphNode{Id: "root", Nodes: uniqueNodes}
205+
return &xrayUtils.GraphNode{Id: "root", Nodes: uniqueNodes}
203206
}
204207

205208
// Collect dependencies exists in target and not in resultsToCompare
206-
func GetDiffDependencyTree(scanResults *results.TargetResults, resultsToCompare *results.TargetResults, fullDepTrees ...*xrayCmdUtils.GraphNode) (*DependencyTreeResult, error) {
207-
if resultsToCompare == nil {
209+
func GetDiffDependencyTree(scanResults *results.TargetResults, resultsToCompare *results.TargetResults, fullDepTrees ...*xrayUtils.GraphNode) (*DependencyTreeResult, error) {
210+
if resultsToCompare == nil || resultsToCompare.ScaResults == nil || resultsToCompare.ScaResults.Sbom == nil || resultsToCompare.ScaResults.Sbom.Components == nil {
208211
return nil, fmt.Errorf("failed to get diff dependency tree: no results to compare")
209212
}
213+
if scanResults == nil || scanResults.ScaResults == nil || scanResults.ScaResults.Sbom == nil || scanResults.ScaResults.Sbom.Components == nil {
214+
return nil, fmt.Errorf("failed to get diff dependency tree: no scan results found for target %s", scanResults.Target)
215+
}
210216
log.Debug(fmt.Sprintf("Comparing %s SBOM with %s to get diff", scanResults.Target, resultsToCompare.Target))
211217
// Compare the dependency trees
212218
filterDepsMap := datastructures.MakeSet[string]()
213-
for _, component := range resultsToCompare.Sbom.Components {
214-
filterDepsMap.Add(techutils.ToXrayComponentId(component.XrayType, component.Component, component.Version))
219+
for _, component := range *resultsToCompare.ScaResults.Sbom.Components {
220+
filterDepsMap.Add(techutils.PurlToXrayComponentId(component.PackageURL))
215221
}
216222
addedDepsMap := datastructures.MakeSet[string]()
217-
for _, component := range scanResults.Sbom.Components {
218-
componentId := techutils.ToXrayComponentId(component.XrayType, component.Component, component.Version)
219-
if exists := filterDepsMap.Exists(componentId); !exists {
223+
for _, component := range *scanResults.ScaResults.Sbom.Components {
224+
id := techutils.PurlToXrayComponentId(component.PackageURL)
225+
if exists := filterDepsMap.Exists(id); !exists {
220226
// Dependency in scan results but not in results to compare
221-
addedDepsMap.Add(componentId)
227+
addedDepsMap.Add(id)
222228
}
223229
}
224230
diffDepTree := DependencyTreeResult{

sca/bom/buildinfo/buildinfobom_test.go

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/CycloneDX/cyclonedx-go"
78
"github.com/jfrog/jfrog-cli-security/utils/results"
89

910
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
@@ -14,28 +15,30 @@ import (
1415
func TestGetDiffDependencyTree(t *testing.T) {
1516
targetResults := &results.TargetResults{
1617
ScanTarget: results.ScanTarget{Target: "targetPath"},
17-
Sbom: results.Sbom{
18-
Components: []results.SbomEntry{
19-
{
20-
Component: "pip",
21-
Version: "20.3.4",
22-
Type: "Python",
23-
XrayType: "pypi",
24-
Direct: true,
25-
},
26-
{
27-
Component: "pyyaml",
28-
Version: "5.2",
29-
Type: "Python",
30-
XrayType: "pypi",
31-
Direct: true,
32-
},
33-
{
34-
Component: "werkzeug",
35-
Version: "1.0.1",
36-
Type: "Python",
37-
XrayType: "pypi",
38-
Direct: true,
18+
ScaResults: &results.ScaScanResults{
19+
Sbom: &cyclonedx.BOM{
20+
Components: &[]cyclonedx.Component{
21+
{
22+
BOMRef: "pypi:[email protected]",
23+
PackageURL: "pkg:pypi/[email protected]",
24+
Name: "pip",
25+
Version: "20.3.4",
26+
Type: "library",
27+
},
28+
{
29+
BOMRef: "pypi:[email protected]",
30+
PackageURL: "pkg:pypi/[email protected]",
31+
Name: "pyyaml",
32+
Version: "5.2",
33+
Type: "library",
34+
},
35+
{
36+
BOMRef: "pypi:[email protected]",
37+
PackageURL: "pkg:pypi/[email protected]",
38+
Name: "werkzeug",
39+
Version: "1.0.1",
40+
Type: "library",
41+
},
3942
},
4043
},
4144
},
@@ -59,25 +62,30 @@ func TestGetDiffDependencyTree(t *testing.T) {
5962
name: "different results",
6063
resultsToCompare: &results.TargetResults{
6164
ScanTarget: results.ScanTarget{Target: "targetPath"},
62-
Sbom: results.Sbom{
63-
Components: []results.SbomEntry{
64-
{
65-
Component: "werkzeug",
66-
Version: "1.0.2",
67-
Type: "Python",
68-
XrayType: "pypi",
69-
},
70-
{
71-
Component: "pyyaml",
72-
Version: "5.2",
73-
Type: "Python",
74-
XrayType: "pypi",
75-
},
76-
{
77-
Component: "wasabi",
78-
Version: "1.1.3",
79-
Type: "Python",
80-
XrayType: "pypi",
65+
ScaResults: &results.ScaScanResults{
66+
Sbom: &cyclonedx.BOM{
67+
Components: &[]cyclonedx.Component{
68+
{
69+
BOMRef: "pypi:[email protected]",
70+
PackageURL: "pkg:pypi/[email protected]",
71+
Name: "werkzeug",
72+
Version: "1.0.2",
73+
Type: "library",
74+
},
75+
{
76+
BOMRef: "pypi:[email protected]",
77+
PackageURL: "pkg:pypi/[email protected]",
78+
Name: "pyyaml",
79+
Version: "5.2",
80+
Type: "library",
81+
},
82+
{
83+
BOMRef: "pypi:[email protected]",
84+
PackageURL: "pkg:pypi/[email protected]",
85+
Name: "wasabi",
86+
Version: "1.1.3",
87+
Type: "library",
88+
},
8189
},
8290
},
8391
},

0 commit comments

Comments
 (0)