diff --git a/audit_test.go b/audit_test.go index 0f08f63e..54856d43 100644 --- a/audit_test.go +++ b/audit_test.go @@ -808,7 +808,7 @@ type auditCommandTestParams struct { // --project flag value if provided. ProjectKey string // --fail flag value if provided, must be provided with 'createWatchesFuncs' to create watches for the test - FailOnFailedBuildFlag bool + DisableFailOnFailedBuildFlag bool // -- vuln flag 'True' value must be provided with 'createWatchesFuncs' to create watches for the test WithVuln bool // --licenses flag value if provided @@ -836,12 +836,9 @@ func testAuditCommand(t *testing.T, testCli *coreTests.JfrogCli, params auditCom if len(params.Watches) > 0 { args = append(args, "--watches="+strings.Join(params.Watches, ",")) } - if params.FailOnFailedBuildFlag { - if len(params.Watches) == 0 { - // Verify params consistency no fail flag - assert.False(t, params.FailOnFailedBuildFlag, "Fail flag provided without watches") - } - args = append(args, "--fail") + // Default value for --fail flag is 'true'. Unless we directly pass DisableFailOnFailedBuildFlag=true, the flow will fail when security issues are found + if params.DisableFailOnFailedBuildFlag { + args = append(args, "--fail=false") } if params.WithVuln { args = append(args, "--vuln") diff --git a/commands/audit/sca/python/python.go b/commands/audit/sca/python/python.go index c8578fbd..c9005130 100644 --- a/commands/audit/sca/python/python.go +++ b/commands/audit/sca/python/python.go @@ -112,7 +112,7 @@ func getDependencies(auditPython *AuditPython) (dependenciesGraph map[string][]s if err != nil { return } - dependenciesGraph, directDependencies, err = pythonutils.GetPythonDependencies(auditPython.Tool, tempDirPath, localDependenciesPath) + dependenciesGraph, directDependencies, err = pythonutils.GetPythonDependencies(auditPython.Tool, tempDirPath, localDependenciesPath, log.GetLogger()) if err != nil { sca.LogExecutableVersion("python") sca.LogExecutableVersion(string(auditPython.Tool)) diff --git a/commands/audit/scarunner.go b/commands/audit/scarunner.go index abbe9451..df4fbc43 100644 --- a/commands/audit/scarunner.go +++ b/commands/audit/scarunner.go @@ -169,7 +169,7 @@ func runScaWithTech(tech techutils.Technology, params *AuditParams, serverDetail if err != nil { return } - log.Debug(fmt.Sprintf("Finished '%s' dependency tree scan. %s", tech.ToFormal(), utils.GetScanFindingsLog(utils.ScaScan, len(techResults[0].Vulnerabilities), len(techResults[0].Violations), -1))) + log.Info(fmt.Sprintf("Finished '%s' dependency tree scan. %s", tech.ToFormal(), utils.GetScanFindingsLog(utils.ScaScan, len(techResults[0].Vulnerabilities), len(techResults[0].Violations), -1))) techResults = sca.BuildImpactPathsForScanResponse(techResults, fullDependencyTrees) return } diff --git a/go.mod b/go.mod index bb324514..63e4fc20 100644 --- a/go.mod +++ b/go.mod @@ -6,18 +6,18 @@ require ( github.com/beevik/etree v1.4.0 github.com/google/go-github/v56 v56.0.0 github.com/gookit/color v1.5.4 - github.com/jfrog/build-info-go v1.10.7 + github.com/jfrog/build-info-go v1.10.8 github.com/jfrog/froggit-go v1.16.2 github.com/jfrog/gofrog v1.7.6 github.com/jfrog/jfrog-apps-config v1.0.1 - github.com/jfrog/jfrog-cli-core/v2 v2.57.5 - github.com/jfrog/jfrog-client-go v1.48.6 + github.com/jfrog/jfrog-cli-core/v2 v2.57.6 + github.com/jfrog/jfrog-client-go v1.49.0 github.com/magiconair/properties v1.8.9 github.com/owenrumney/go-sarif/v2 v2.3.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli v1.22.16 github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 @@ -100,21 +100,21 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.27.0 // indirect + golang.org/x/tools v0.29.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) -replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20241230154616-e342ed5065f1 +// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go dev -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250101110857-b26e9a6644c6 +// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 dev // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev diff --git a/go.sum b/go.sum index 25024829..3ce66d00 100644 --- a/go.sum +++ b/go.sum @@ -119,18 +119,18 @@ github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3N github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI= github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw= -github.com/jfrog/build-info-go v1.10.7 h1:10NVHYg0193gJpQft+S4WQfvYMtj5jlwwhJRvkFJtBE= -github.com/jfrog/build-info-go v1.10.7/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= +github.com/jfrog/build-info-go v1.10.8 h1:8D4wtvKzLS1hzfDWtfH4OliZLtLCgL62tXCnGWDXuac= +github.com/jfrog/build-info-go v1.10.8/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= github.com/jfrog/froggit-go v1.16.2 h1:F//S83iXH14qsCwYzv0zB2JtjS2pJVEsUoEmYA+37dQ= github.com/jfrog/froggit-go v1.16.2/go.mod h1:5VpdQfAcbuyFl9x/x8HGm7kVk719kEtW/8YJFvKcHPA= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY= github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250101110857-b26e9a6644c6 h1:/i1sIQS0q0gRN531ChVToQWcjaVZOKZ4KuGk7j7vDTc= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250101110857-b26e9a6644c6/go.mod h1:LfKvCRXbvwgE0V6aX3/GabkzCedghXq0Y6lmsEuxr44= -github.com/jfrog/jfrog-client-go v1.28.1-0.20241230154616-e342ed5065f1 h1:JQvbTSPDkPNpts1NLHGTKvtG4cMFY1ptBHTNMYFyMhs= -github.com/jfrog/jfrog-client-go v1.28.1-0.20241230154616-e342ed5065f1/go.mod h1:2ySOMva54L3EYYIlCBYBTcTgqfrrQ19gtpA/MWfA/ec= +github.com/jfrog/jfrog-cli-core/v2 v2.57.6 h1:kI5BqDW8Q4R5HkTUPSAObTqyIgQ9z7DqeFYGOEC1zPk= +github.com/jfrog/jfrog-cli-core/v2 v2.57.6/go.mod h1:h5pzOZUb5ChGcGrXCYr3nPyXcTZjeGW2Rm1Zceo8Afg= +github.com/jfrog/jfrog-client-go v1.49.0 h1:NaTK6+LQBEJafL//6ntnS/eVx1dZMJnxydALwWHKORQ= +github.com/jfrog/jfrog-client-go v1.49.0/go.mod h1:ohIfKpMBCQsE9kunrKQ1wvoExpqsPLaluRFO186B5EM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -287,10 +287,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= @@ -308,8 +308,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -346,16 +346,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -373,8 +373,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/jas/runner/jasrunner.go b/jas/runner/jasrunner.go index 21f1d543..534a2053 100644 --- a/jas/runner/jasrunner.go +++ b/jas/runner/jasrunner.go @@ -1,7 +1,7 @@ package runner import ( - "encoding/json" + "errors" "fmt" "github.com/jfrog/gofrog/parallel" @@ -141,7 +141,7 @@ func runSecretsScan(securityParallelRunner *utils.SecurityParallelRunner, scanne if err = jas.ParseAnalyzerManagerError(jasutils.Secrets, err); err != nil { return fmt.Errorf("%s%s", clientutils.GetLogMsgPrefix(threadId, false), err.Error()) } - return dumpSarifRunToFileIfNeeded(vulnerabilitiesResults, scansOutputDir, jasutils.Secrets) + return dumpSarifRunToFileIfNeeded(scansOutputDir, jasutils.Secrets, vulnerabilitiesResults, violationsResults) } } @@ -159,7 +159,7 @@ func runIacScan(securityParallelRunner *utils.SecurityParallelRunner, scanner *j if err = jas.ParseAnalyzerManagerError(jasutils.IaC, err); err != nil { return fmt.Errorf("%s%s", clientutils.GetLogMsgPrefix(threadId, false), err.Error()) } - return dumpSarifRunToFileIfNeeded(vulnerabilitiesResults, scansOutputDir, jasutils.IaC) + return dumpSarifRunToFileIfNeeded(scansOutputDir, jasutils.IaC, vulnerabilitiesResults, violationsResults) } } @@ -177,7 +177,7 @@ func runSastScan(securityParallelRunner *utils.SecurityParallelRunner, scanner * if err = jas.ParseAnalyzerManagerError(jasutils.Sast, err); err != nil { return fmt.Errorf("%s%s", clientutils.GetLogMsgPrefix(threadId, false), err.Error()) } - return dumpSarifRunToFileIfNeeded(vulnerabilitiesResults, scansOutputDir, jasutils.Sast) + return dumpSarifRunToFileIfNeeded(scansOutputDir, jasutils.Sast, vulnerabilitiesResults, violationsResults) } } @@ -193,22 +193,29 @@ func runContextualScan(securityParallelRunner *utils.SecurityParallelRunner, sca securityParallelRunner.ResultsMu.Lock() defer securityParallelRunner.ResultsMu.Unlock() // We first add the scan results and only then check for errors, so we can store the exit code in order to report it in the end - scanResults.JasResults.NewApplicabilityScanResults(jas.GetAnalyzerManagerExitCode(err), caScanResults...) + scanResults.JasResults.AddApplicabilityScanResults(jas.GetAnalyzerManagerExitCode(err), caScanResults...) if err = jas.ParseAnalyzerManagerError(jasutils.Applicability, err); err != nil { return fmt.Errorf("%s%s", clientutils.GetLogMsgPrefix(threadId, false), err.Error()) } - return dumpSarifRunToFileIfNeeded(caScanResults, scansOutputDir, jasutils.Applicability) + return dumpSarifRunToFileIfNeeded(scansOutputDir, jasutils.Applicability, caScanResults) } } // If an output dir was provided through --output-dir flag, we create in the provided path new file containing the scan results -func dumpSarifRunToFileIfNeeded(results []*sarif.Run, scanResultsOutputDir string, scanType jasutils.JasScanType) (err error) { - if scanResultsOutputDir == "" || results == nil { +func dumpSarifRunToFileIfNeeded(scanResultsOutputDir string, scanType jasutils.JasScanType, scanResults ...[]*sarif.Run) (err error) { + if scanResultsOutputDir == "" || len(scanResults) == 0 { return } - fileContent, err := json.Marshal(results) - if err != nil { - return fmt.Errorf("failed to write %s scan results to file: %s", scanType, err.Error()) + var fileContent []byte + for _, resultsToDump := range scanResults { + if len(resultsToDump) == 0 { + continue + } + if fileContent, err = utils.GetAsJsonBytes(resultsToDump, true, true); err != nil { + err = errors.Join(err, fmt.Errorf("failed to write %s scan results to file", scanType)) + } else { + err = errors.Join(err, utils.DumpContentToFile(fileContent, scanResultsOutputDir, scanType.String())) + } } - return utils.DumpContentToFile(fileContent, scanResultsOutputDir, scanType.String()) + return } diff --git a/tests/testdata/output/jobSummary/binary_analytics_vulnerabilities.md b/tests/testdata/output/jobSummary/binary_analytics_vulnerabilities.md index 90496612..fe04d2d3 100644 --- a/tests/testdata/output/jobSummary/binary_analytics_vulnerabilities.md +++ b/tests/testdata/output/jobSummary/binary_analytics_vulnerabilities.md @@ -1 +1 @@ -
44 Security issues are grouped by CVE number:	44 SCA

❗️ 33 Critical

🟡 11 Low

See the results of the scan in JFrog
\ No newline at end of file +
44 Security issues are grouped by CVE number:	44 SCA

❗️ 33 Critical

🟡 11 Low

See the results of the scan in JFrog
\ No newline at end of file diff --git a/tests/testdata/output/jobSummary/build_scan_analytics_vulnerabilities.md b/tests/testdata/output/jobSummary/build_scan_analytics_vulnerabilities.md index 904377a1..ffd0946a 100644 --- a/tests/testdata/output/jobSummary/build_scan_analytics_vulnerabilities.md +++ b/tests/testdata/output/jobSummary/build_scan_analytics_vulnerabilities.md @@ -1 +1 @@ -
24 Security Issues:	24 SCA

🔴 3 High

🟠 1 Medium

⚪️ 20 Unknown

See the results of the scan in JFrog
\ No newline at end of file +
24 Security Issues:	24 SCA

🔴 3 High

🟠 1 Medium

⚪️ 20 Unknown

See the results of the scan in JFrog
\ No newline at end of file diff --git a/tests/testdata/output/jobSummary/violations_analytics.md b/tests/testdata/output/jobSummary/violations_analytics.md index 15766051..4fcf9906 100644 --- a/tests/testdata/output/jobSummary/violations_analytics.md +++ b/tests/testdata/output/jobSummary/violations_analytics.md @@ -1 +1 @@ -
watches: 
watch1, watch2, watch3, watch4
watch5

23 Policy Violations:	17 Security	2 Operational	1 License	3 Secrets

❗️ 8 Critical (2 Not Applicable)

🔴 6 High

🟠 3 Medium

🟡 5 Low (3 Not Applicable)

⚪️ 1 Unknown

See the results of the scan in JFrog
\ No newline at end of file +
watches: 
watch1, watch2, watch3, watch4
watch5

23 Policy Violations:	17 Security	2 Operational	1 License	3 Secrets

❗️ 8 Critical (2 Not Applicable)

🔴 6 High

🟠 3 Medium

🟡 5 Low (3 Not Applicable)

⚪️ 1 Unknown

See the results of the scan in JFrog
\ No newline at end of file diff --git a/tests/testdata/projects/package-managers/python/poetry/poetry-project/poetry.lock b/tests/testdata/projects/package-managers/python/poetry/poetry-project/poetry.lock index 832846f6..f071fc52 100644 --- a/tests/testdata/projects/package-managers/python/poetry/poetry-project/poetry.lock +++ b/tests/testdata/projects/package-managers/python/poetry/poetry-project/poetry.lock @@ -1,10 +1,15 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + [[package]] name = "django" version = "1.11.15" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." -category = "main" optional = false python-versions = "*" +files = [ + {file = "Django-1.11.15-py2.py3-none-any.whl", hash = "sha256:8176ac7985fe6737ce3d6b2531b4a2453cb7c3377c9db00bacb2b3320f4a1311"}, + {file = "Django-1.11.15.tar.gz", hash = "sha256:b18235d82426f09733d2de9910cee975cf52ff05e5f836681eb957d105a05a40"}, +] [package.dependencies] pytz = "*" @@ -15,50 +20,41 @@ bcrypt = ["bcrypt"] [[package]] name = "pytz" -version = "2022.2.1" +version = "2024.2" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] [[package]] name = "urllib3" version = "1.22" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = "*" +files = [ + {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, + {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, +] [package.extras] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "werkzeug" version = "0.9.6" description = "The Swiss Army knife of Python web development" -category = "main" optional = false python-versions = "*" +files = [ + {file = "Werkzeug-0.9.6.tar.gz", hash = "sha256:7f11e7e2e73eb22677cac1b11113eb6106f66cedef13d140e83cf6563c90b79c"}, +] [metadata] -lock-version = "1.1" +lock-version = "2.0" python-versions = "*" -content-hash = "ca39a9fcd59a5de98126ed6aa586f5730905182cc671df01d5df02f4a15ef417" - -[metadata.files] -django = [ - {file = "Django-1.11.15-py2.py3-none-any.whl", hash = "sha256:8176ac7985fe6737ce3d6b2531b4a2453cb7c3377c9db00bacb2b3320f4a1311"}, - {file = "Django-1.11.15.tar.gz", hash = "sha256:b18235d82426f09733d2de9910cee975cf52ff05e5f836681eb957d105a05a40"}, -] -pytz = [ - {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, - {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, -] -urllib3 = [ - {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, - {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, -] -werkzeug = [ - {file = "Werkzeug-0.9.6.tar.gz", hash = "sha256:7f11e7e2e73eb22677cac1b11113eb6106f66cedef13d140e83cf6563c90b79c"}, -] +content-hash = "7b951bed28e7a8ff330c4741df89a9ff94d508e1922c43a6bd27940460be400e" diff --git a/tests/testdata/projects/package-managers/python/poetry/poetry-project/pyproject.toml b/tests/testdata/projects/package-managers/python/poetry/poetry-project/pyproject.toml index 71980436..d1293a06 100644 --- a/tests/testdata/projects/package-managers/python/poetry/poetry-project/pyproject.toml +++ b/tests/testdata/projects/package-managers/python/poetry/poetry-project/pyproject.toml @@ -3,6 +3,7 @@ name = "poetry-project" version = "0.1.0" description = "" authors = ["Your Name "] +package-mode = false [tool.poetry.dependencies] python = "*" @@ -10,7 +11,7 @@ urllib3 = "<1.24" django = "<1.11.16" Werkzeug = "<0.10" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/testdata/projects/package-managers/python/poetry/poetry/pyproject.toml b/tests/testdata/projects/package-managers/python/poetry/poetry/pyproject.toml index 00c4eb5a..5c4946d9 100644 --- a/tests/testdata/projects/package-managers/python/poetry/poetry/pyproject.toml +++ b/tests/testdata/projects/package-managers/python/poetry/poetry/pyproject.toml @@ -3,6 +3,7 @@ name = "my-poetry-project" version = "1.1.0" description = "" authors = ["Severus Snape "] +package-mode = false [tool.poetry.dependencies] python = "^3.10" diff --git a/tests/utils/test_utils.go b/tests/utils/test_utils.go index d7e5771e..97ed1b9b 100644 --- a/tests/utils/test_utils.go +++ b/tests/utils/test_utils.go @@ -59,13 +59,8 @@ func ValidateXrayVersion(t *testing.T, minVersion string) { } } -func ValidateXscVersion(t *testing.T, minVersion string) { - xscVersion, err := getTestsXscVersion() - if err != nil { - t.Skip(err) - } - err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, xscVersion.GetVersion(), minVersion) - if err != nil { +func ValidateXscVersion(t *testing.T, xscVersion, minVersion string) { + if err := clientUtils.ValidateMinimumVersion(clientUtils.Xsc, xscVersion, minVersion); err != nil { t.Skip(err) } } @@ -99,11 +94,6 @@ func GetTestsXrayVersion() (version.Version, error) { return *version.NewVersion(xrayVersion), err } -func getTestsXscVersion() (version.Version, error) { - xscVersion, err := configTests.XscAuth.GetVersion() - return *version.NewVersion(xscVersion), err -} - func ChangeWD(t *testing.T, newPath string) string { prevDir, err := os.Getwd() assert.NoError(t, err, "Failed to get current dir") @@ -297,11 +287,11 @@ func CreateSecurityPolicy(t *testing.T, policyName string, rules ...xrayUtils.Po } } -func CreateTestSecurityPolicy(t *testing.T, policyName string, severity xrayUtils.Severity, failBuild bool) (string, func()) { +func CreateTestSecurityPolicy(t *testing.T, policyName string, severity xrayUtils.Severity, failBuild bool, skipNotApplicable bool) (string, func()) { return CreateSecurityPolicy(t, policyName, xrayUtils.PolicyRule{ Name: "sca_rule", - Criteria: *xrayUtils.CreateSeverityPolicyCriteria(severity), + Criteria: *xrayUtils.CreateSeverityPolicyCriteria(severity, skipNotApplicable), Actions: getBuildFailAction(failBuild), Priority: 1, }, @@ -382,7 +372,7 @@ func CreateTestPolicyAndWatch(t *testing.T, policyName, watchName string, severi Type: xrayUtils.Security, Rules: []xrayUtils.PolicyRule{{ Name: "sec_rule", - Criteria: *xrayUtils.CreateSeverityPolicyCriteria(severity), + Criteria: *xrayUtils.CreateSeverityPolicyCriteria(severity, false), Priority: 1, Actions: &xrayUtils.PolicyAction{ FailBuild: clientUtils.Pointer(true), diff --git a/utils/results/common.go b/utils/results/common.go index 1eb57635..a9c987f7 100644 --- a/utils/results/common.go +++ b/utils/results/common.go @@ -116,7 +116,7 @@ func ApplyHandlerToScaVulnerabilities(target ScanTarget, vulnerabilities []servi return nil } -// ApplyHandlerToScaViolations allows to iterate over the provided SCA violations and call the provided handler for each impacted component/package with a violation to process it. +// Allows to iterate over the provided SCA violations and call the provided handler for each impacted component/package with a violation to process it. func ApplyHandlerToScaViolations(target ScanTarget, violations []services.Violation, entitledForJas bool, applicabilityRuns []*sarif.Run, securityHandler ParseScaViolationFunc, licenseHandler ParseScaViolationFunc, operationalRiskHandler ParseScaViolationFunc) (watches []string, failBuild bool, err error) { if securityHandler == nil && licenseHandler == nil && operationalRiskHandler == nil { return @@ -145,6 +145,13 @@ func ApplyHandlerToScaViolations(target ScanTarget, violations []services.Violat // No handler was provided for security violations continue } + + var skipNotApplicable bool + if skipNotApplicable, err = shouldSkipNotApplicable(violation, applicabilityStatus); skipNotApplicable { + log.Debug("A non-applicable violation was found and will be removed from final results as requested by its policies") + continue + } + for compIndex := 0; compIndex < len(impactedPackagesNames); compIndex++ { if e := securityHandler( violation, cves, applicabilityStatus, severity, @@ -645,3 +652,31 @@ func ScanResultsToRuns(results []ScanResult[[]*sarif.Run]) (runs []*sarif.Run) { } return } + +// Resolve the actual technology from multiple sources: +func GetIssueTechnology(responseTechnology string, targetTech techutils.Technology) techutils.Technology { + if responseTechnology != "" { + // technology returned in the vulnerability/violation obj is the most specific technology + return techutils.Technology(responseTechnology) + } + // if no technology is provided, use the target technology + return targetTech +} + +// Checks if the violation's applicability status is NotApplicable and if all of its policies states that non-applicable CVEs should be skipped +func shouldSkipNotApplicable(violation services.Violation, applicabilityStatus jasutils.ApplicabilityStatus) (bool, error) { + if applicabilityStatus != jasutils.NotApplicable { + return false, nil + } + + if len(violation.Policies) == 0 { + return false, errors.New("A violation with no policies was provided") + } + + for _, policy := range violation.Policies { + if !policy.SkipNotApplicable { + return false, nil + } + } + return true, nil +} diff --git a/utils/results/common_test.go b/utils/results/common_test.go index f56b2036..6fc8da1e 100644 --- a/utils/results/common_test.go +++ b/utils/results/common_test.go @@ -701,3 +701,109 @@ func TestGetFinalApplicabilityStatus(t *testing.T) { }) } } + +func TestShouldSkipNotApplicable(t *testing.T) { + testCases := []struct { + name string + violation services.Violation + applicabilityStatus jasutils.ApplicabilityStatus + shouldSkip bool + errorExpected bool + }{ + { + name: "Applicable CVE - should NOT skip", + violation: services.Violation{}, + applicabilityStatus: jasutils.Applicable, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Undetermined CVE - should NOT skip", + violation: services.Violation{}, + applicabilityStatus: jasutils.ApplicabilityUndetermined, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Not covered CVE - should NOT skip", + violation: services.Violation{}, + applicabilityStatus: jasutils.NotCovered, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Missing Context CVE - should NOT skip", + violation: services.Violation{}, + applicabilityStatus: jasutils.MissingContext, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Not scanned CVE - should NOT skip", + violation: services.Violation{}, + applicabilityStatus: jasutils.NotScanned, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Non applicable CVE with skip-non-applicable in ALL policies - SHOULD skip", + violation: services.Violation{ + Policies: []services.Policy{ + { + Policy: "policy-1", + SkipNotApplicable: true, + }, + { + Policy: "policy-2", + SkipNotApplicable: true, + }, + }, + }, + applicabilityStatus: jasutils.NotApplicable, + shouldSkip: true, + errorExpected: false, + }, + { + name: "Non applicable CVE with skip-non-applicable in SOME policies - should NOT skip", + violation: services.Violation{ + Policies: []services.Policy{ + { + Policy: "policy-1", + SkipNotApplicable: true, + }, + { + Policy: "policy-2", + SkipNotApplicable: false, + }, + }, + }, + applicabilityStatus: jasutils.NotApplicable, + shouldSkip: false, + errorExpected: false, + }, + { + name: "Violation without policy - error expected", + violation: services.Violation{}, + applicabilityStatus: jasutils.NotApplicable, + shouldSkip: false, + errorExpected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + shouldSkip, err := shouldSkipNotApplicable(tc.violation, tc.applicabilityStatus) + if tc.errorExpected { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + + if tc.shouldSkip { + assert.True(t, shouldSkip) + } else { + assert.False(t, shouldSkip) + } + }) + } +} diff --git a/utils/results/conversion/convertor_test.go b/utils/results/conversion/convertor_test.go index 540b6b2a..19a63f09 100644 --- a/utils/results/conversion/convertor_test.go +++ b/utils/results/conversion/convertor_test.go @@ -335,7 +335,7 @@ func getAuditTestResults(unique bool) (*results.SecurityCommandResults, validati ScannedStatus: "completed", }) // Contextual analysis scan results - npmTargetResults.JasResults.NewApplicabilityScanResults(0, + npmTargetResults.JasResults.AddApplicabilityScanResults(0, &sarif.Run{ Tool: sarif.Tool{ Driver: sarifutils.CreateDummyDriver(validations.ContextualAnalysisToolName, @@ -537,7 +537,7 @@ func getDockerScanTestResults(unique bool) (*results.SecurityCommandResults, val ScannedStatus: "completed", }) // Contextual analysis scan results - dockerImageTarget.JasResults.NewApplicabilityScanResults(0, + dockerImageTarget.JasResults.AddApplicabilityScanResults(0, &sarif.Run{ Tool: sarif.Tool{ Driver: sarifutils.CreateDummyDriver(validations.ContextualAnalysisToolName, diff --git a/utils/results/conversion/sarifparser/sarifparser.go b/utils/results/conversion/sarifparser/sarifparser.go index f017ed73..fdf90dd3 100644 --- a/utils/results/conversion/sarifparser/sarifparser.go +++ b/utils/results/conversion/sarifparser/sarifparser.go @@ -649,7 +649,7 @@ func patchRules(platformBaseUrl string, commandType utils.CommandType, subScanTy } // Add analytics hidden pixel to the help content if needed (Github code scanning) if analytics := getAnalyticsHiddenPixel(platformBaseUrl, subScanType); rule.Help != nil && analytics != "" { - rule.Help.Markdown = utils.NewStringPtr(fmt.Sprintf("%s %s", sarifutils.GetRuleHelpMarkdown(rule), analytics)) + rule.Help.Markdown = utils.NewStringPtr(fmt.Sprintf("%s\n%s", analytics, sarifutils.GetRuleHelpMarkdown(rule))) } patched = append(patched, rule) } @@ -936,7 +936,7 @@ func getAnalyticsHiddenPixel(baseUrl string, resultOfSubScan utils.SubScanType) return fmt.Sprintf( "![](%sui/api/v1/u?s=1&m=2&job_id=%s&run_id=%s&git_repo=%s&type=%s)", baseUrl, - url.PathEscape(jobId), + url.QueryEscape(jobId), runId, gitRepo, resultOfSubScan.String(), diff --git a/utils/results/conversion/sarifparser/sarifparser_test.go b/utils/results/conversion/sarifparser/sarifparser_test.go index 498302db..3aa9bd93 100644 --- a/utils/results/conversion/sarifparser/sarifparser_test.go +++ b/utils/results/conversion/sarifparser/sarifparser_test.go @@ -371,7 +371,7 @@ func TestPatchRunsToPassIngestionRules(t *testing.T) { ), }, expectedResults: []*sarif.Run{ - sarifutils.CreateRunWithDummyResultsWithRuleInformation("", "", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)", wd, + sarifutils.CreateRunWithDummyResultsWithRuleInformation("", "", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)\nrule-markdown", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)\nrule-markdown", wd, sarifutils.CreateDummyResultWithFingerprint(fmt.Sprintf("some-msg\nGithub Actions Workflow: %s\nRun: 123\nImage: dockerImage:imageVersion\nLayer (sha256): f752cb05a39e65f231a3c47c2e08cbeac1c15e4daff0188cb129c12a3ea3049d", filepath.Join(GithubBaseWorkflowDir, "workflowFile.yml")), "some-msg", jfrogFingerprintAlgorithmName, "eda26ae830c578197aeda65a82d7f093", sarifutils.CreateDummyLocationWithPathAndLogicalLocation("", "f752cb05a39e65f231a3c47c2e08cbeac1c15e4daff0188cb129c12a3ea3049d", "layer", "algorithm", "sha256").WithPhysicalLocation( sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewSimpleArtifactLocation(filepath.Join(GithubBaseWorkflowDir, "workflowFile.yml"))), @@ -393,7 +393,7 @@ func TestPatchRunsToPassIngestionRules(t *testing.T) { ), }, expectedResults: []*sarif.Run{ - sarifutils.CreateRunWithDummyResultsWithRuleInformation("", "", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)", dockerfileDir, + sarifutils.CreateRunWithDummyResultsWithRuleInformation("", "", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)\nrule-markdown", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=sca)\nrule-markdown", dockerfileDir, sarifutils.CreateDummyResultWithFingerprint(fmt.Sprintf("some-msg\nGithub Actions Workflow: %s\nRun: 123\nImage: dockerImage:imageVersion\nLayer (sha256): f752cb05a39e65f231a3c47c2e08cbeac1c15e4daff0188cb129c12a3ea3049d", filepath.Join(GithubBaseWorkflowDir, "workflowFile.yml")), "some-msg", jfrogFingerprintAlgorithmName, "8cbd7268a4d20f2358ba2667ebd18956", sarifutils.CreateDummyLocationWithPathAndLogicalLocation("", "f752cb05a39e65f231a3c47c2e08cbeac1c15e4daff0188cb129c12a3ea3049d", "layer", "algorithm", "sha256").WithPhysicalLocation( sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewSimpleArtifactLocation("Dockerfile")), @@ -436,7 +436,7 @@ func TestPatchRunsToPassIngestionRules(t *testing.T) { }), }, expectedResults: []*sarif.Run{ - sarifutils.CreateRunWithDummyResultsWithRuleInformation(BinarySecretScannerToolName, "[Secret in Binary found] ", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=secrets)", "rule-msg", "rule-markdown ![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=secrets)", wd, + sarifutils.CreateRunWithDummyResultsWithRuleInformation(BinarySecretScannerToolName, "[Secret in Binary found] ", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=secrets)\nrule-markdown", "rule-msg", "![](url/ui/api/v1/u?s=1&m=2&job_id=job-id&run_id=run-id&git_repo=repo&type=secrets)\nrule-markdown", wd, sarifutils.CreateDummyResultWithFingerprint(fmt.Sprintf("🔒 Found Secrets in Binary docker scanning:\nGithub Actions Workflow: %s\nRun: 123\nImage: dockerImage:imageVersion\nLayer (sha1): 9e88ea9de1b44baba5e96a79e33e4af64334b2bf129e838e12f6dae71b5c86f0\nFilepath: %s\nEvidence: snippet", filepath.Join(GithubBaseWorkflowDir, "workflowFile.yml"), filepath.Join("usr", "src", "app", "server", "index.js")), "result-msg", jfrogFingerprintAlgorithmName, "e721eacf317da6090eca3522308abd28", sarifutils.CreateDummyLocationWithPathAndLogicalLocation("", "9e88ea9de1b44baba5e96a79e33e4af64334b2bf129e838e12f6dae71b5c86f0", "layer", "algorithm", "sha1").WithPhysicalLocation( sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewSimpleArtifactLocation(filepath.Join(GithubBaseWorkflowDir, "workflowFile.yml"))), diff --git a/utils/results/conversion/simplejsonparser/simplejsonparser.go b/utils/results/conversion/simplejsonparser/simplejsonparser.go index d2ac34b7..269c7706 100644 --- a/utils/results/conversion/simplejsonparser/simplejsonparser.go +++ b/utils/results/conversion/simplejsonparser/simplejsonparser.go @@ -10,7 +10,6 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/jasutils" "github.com/jfrog/jfrog-cli-security/utils/results" "github.com/jfrog/jfrog-cli-security/utils/severityutils" - "github.com/jfrog/jfrog-cli-security/utils/techutils" "github.com/jfrog/jfrog-client-go/xray/services" "github.com/owenrumney/go-sarif/v2/sarif" ) @@ -236,10 +235,6 @@ func PrepareSimpleJsonVulnerabilities(target results.ScanTarget, scaResponse ser func addSimpleJsonVulnerability(target results.ScanTarget, vulnerabilitiesRows *[]formats.VulnerabilityOrViolationRow, pretty bool) results.ParseScaVulnerabilityFunc { return func(vulnerability services.Vulnerability, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesName, impactedPackagesVersion, impactedPackagesType string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) error { - tech := target.Technology - if tech == "" { - tech = techutils.Technology(impactedPackagesType) - } *vulnerabilitiesRows = append(*vulnerabilitiesRows, formats.VulnerabilityOrViolationRow{ Summary: vulnerability.Summary, @@ -256,7 +251,7 @@ func addSimpleJsonVulnerability(target results.ScanTarget, vulnerabilitiesRows * References: vulnerability.References, JfrogResearchInformation: convertJfrogResearchInformation(vulnerability.ExtendedInformation), ImpactPaths: impactPaths, - Technology: tech, + Technology: results.GetIssueTechnology(vulnerability.Technology, target.Technology), Applicable: applicabilityStatus.ToString(pretty), }, ) @@ -266,10 +261,6 @@ func addSimpleJsonVulnerability(target results.ScanTarget, vulnerabilitiesRows * func addSimpleJsonSecurityViolation(target results.ScanTarget, securityViolationsRows *[]formats.VulnerabilityOrViolationRow, pretty bool) results.ParseScaViolationFunc { return func(violation services.Violation, cves []formats.CveRow, applicabilityStatus jasutils.ApplicabilityStatus, severity severityutils.Severity, impactedPackagesName, impactedPackagesVersion, impactedPackagesType string, fixedVersion []string, directComponents []formats.ComponentRow, impactPaths [][]formats.ComponentRow) error { - tech := target.Technology - if tech == "" { - tech = techutils.Technology(impactedPackagesType) - } *securityViolationsRows = append(*securityViolationsRows, formats.VulnerabilityOrViolationRow{ Summary: violation.Summary, @@ -290,7 +281,7 @@ func addSimpleJsonSecurityViolation(target results.ScanTarget, securityViolation References: violation.References, JfrogResearchInformation: convertJfrogResearchInformation(violation.ExtendedInformation), ImpactPaths: impactPaths, - Technology: tech, + Technology: results.GetIssueTechnology(violation.Technology, target.Technology), Applicable: applicabilityStatus.ToString(pretty), }, ) diff --git a/utils/results/output/securityJobSummary.go b/utils/results/output/securityJobSummary.go index dffc3106..4e95c27f 100644 --- a/utils/results/output/securityJobSummary.go +++ b/utils/results/output/securityJobSummary.go @@ -549,7 +549,8 @@ func addAnalyticsQueryParamsIfNeeded(platformUrl string, index commandsummary.In // Not running in Github no need to add analytics return platformUrl } - suffixValues := []string{fmt.Sprintf("gh_job_id=%s", url.PathEscape(githubJobId))} + // M=3 (type of event) + suffixValues := []string{"s=1", "m=3", fmt.Sprintf("gh_job_id=%s", url.QueryEscape(githubJobId))} // Add section analytics indexValue := "gh_section=" switch index { @@ -561,7 +562,7 @@ func addAnalyticsQueryParamsIfNeeded(platformUrl string, index commandsummary.In suffixValues = append(suffixValues, indexValue) // Add the suffix to the url if strings.Contains(platformUrl, "?") { - return fmt.Sprintf("%s%s", platformUrl, strings.Join(suffixValues, "&")) + return fmt.Sprintf("%s&%s", platformUrl, strings.Join(suffixValues, "&")) } return fmt.Sprintf("%s?%s", platformUrl, strings.Join(suffixValues, "&")) } diff --git a/utils/results/results.go b/utils/results/results.go index 925e58f8..0c3f2a6f 100644 --- a/utils/results/results.go +++ b/utils/results/results.go @@ -442,7 +442,7 @@ func (ssr *ScaScanResults) HasFindings() bool { return false } -func (jsr *JasScansResults) NewApplicabilityScanResults(exitCode int, runs ...*sarif.Run) { +func (jsr *JasScansResults) AddApplicabilityScanResults(exitCode int, runs ...*sarif.Run) { jsr.ApplicabilityScanResults = append(jsr.ApplicabilityScanResults, ScanResult[[]*sarif.Run]{Scan: runs, StatusCode: exitCode}) } diff --git a/xsc_test.go b/xsc_test.go index 96f92eba..740afc15 100644 --- a/xsc_test.go +++ b/xsc_test.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "errors" + "os" "path/filepath" "testing" @@ -30,7 +31,8 @@ import ( ) func TestReportError(t *testing.T) { - xrayVersion, xscVersion, cleanUp := integration.InitXscTest(t, func() { securityTestUtils.ValidateXscVersion(t, xsc.MinXscVersionForErrorReport) }) + xrayVersion, xscVersion, cleanUp := integration.InitXscTest(t) + securityTestUtils.ValidateXscVersion(t, xscVersion, xsc.MinXscVersionForErrorReport) defer cleanUp() errorToReport := errors.New("THIS IS NOT A REAL ERROR! This Error is posted as part of TestReportError test") assert.NoError(t, xsc.ReportError(xrayVersion, xscVersion, tests.XscDetails, errorToReport, "cli")) @@ -66,7 +68,7 @@ func TestXscAuditViolationsWithIgnoreRule(t *testing.T) { _, cleanUpProject := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "projects", "jas", "jas")) defer cleanUpProject() // Create policy and watch for the git repo so we will also get violations (unknown = all vulnerabilities will be reported as violations) - policyName, cleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "git-repo-ignore-rule-policy", utils.Unknown, true) + policyName, cleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "git-repo-ignore-rule-policy", utils.Unknown, true, false) defer cleanUpPolicy() _, cleanUpWatch := securityTestUtils.CreateWatchForTests(t, policyName, "git-repo-ignore-rule-watch", xscutils.GetGitRepoUrlKey(validations.TestMockGitInfo.GitRepoHttpsCloneUrl)) defer cleanUpWatch() @@ -100,10 +102,63 @@ func TestXscAuditViolationsWithIgnoreRule(t *testing.T) { validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{ExactResultsMatch: true, Total: &validations.TotalCount{}, Violations: &validations.ViolationCount{ValidateScan: &validations.ScanCount{}}}) } +func TestXrayAuditJasSkipNotApplicableCvesViolations(t *testing.T) { + // Init XSC tests also enabled analytics reporting. + _, _, cleanUpXsc := integration.InitXscTest(t, func() { securityTestUtils.ValidateXrayVersion(t, services.MinXrayVersionGitRepoKey) }) + defer cleanUpXsc() + // Create the audit command with git repo context injected. + cliToRun, cleanUpHome := integration.InitTestWithMockCommandOrParams(t, false, getAuditCommandWithXscGitContext(validations.TestMockGitInfo)) + defer cleanUpHome() + // Create the project to scan + _, cleanUpProject := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "projects", "jas", "jas")) + defer cleanUpProject() + // Create policy and watch for the git repo so we will also get violations - This watch DO NOT skip not-applicable results + var firstPolicyCleaned, firstWatchCleaned bool + policyName, cleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "without-skip-non-applicable-policy", utils.Low, false, false) + defer func() { + if !firstPolicyCleaned { + cleanUpPolicy() + } + }() + watchName, cleanUpWatch := securityTestUtils.CreateWatchForTests(t, policyName, "without-skip-not-applicable-watch", xscutils.GetGitRepoUrlKey(validations.TestMockGitInfo.GitRepoHttpsCloneUrl)) + defer func() { + if !firstWatchCleaned { + cleanUpWatch() + } + }() + // Run the audit command with git repo and verify violations are reported to the platform. + output, err := testAuditCommand(t, cliToRun, auditCommandTestParams{Format: string(format.SimpleJson), Watches: []string{watchName}, DisableFailOnFailedBuildFlag: true}) + assert.NoError(t, err) + validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{ + Violations: &validations.ViolationCount{ValidateScan: &validations.ScanCount{Sca: 17, Sast: 1, Secrets: 15}}, + ExactResultsMatch: true, + }) + + // We clean the initially created Policy and Watch that are related to the Git Repo resource, because we must have all related policies with skipNotApplicable=true + cleanUpWatch() + firstWatchCleaned = true + cleanUpPolicy() + firstPolicyCleaned = true + + // Create policy and watch for the git repo so we will also get violations - This watch DO NOT skip not-applicable results + skipPolicyName, skipCleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "skip-non-applicable-policy", utils.Low, false, true) + defer skipCleanUpPolicy() + skipWatchName, skipCleanUpWatch := securityTestUtils.CreateWatchForTests(t, skipPolicyName, "skip-not-applicable-watch", xscutils.GetGitRepoUrlKey(validations.TestMockGitInfo.GitRepoHttpsCloneUrl)) + defer skipCleanUpWatch() + output, err = testAuditCommand(t, cliToRun, auditCommandTestParams{Format: string(format.SimpleJson), Watches: []string{skipWatchName}, DisableFailOnFailedBuildFlag: true}) + assert.NoError(t, err) + validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{ + Violations: &validations.ViolationCount{ValidateScan: &validations.ScanCount{Sca: 12, Sast: 1, Secrets: 15}}, + ExactResultsMatch: true, + }) + +} + func TestAuditJasViolationsProjectKeySimpleJson(t *testing.T) { _, _, cleanUpXsc := integration.InitXscTest(t, func() { securityTestUtils.ValidateXrayVersion(t, services.MinXrayVersionGitRepoKey) }) defer cleanUpXsc() - if tests.TestJfrogPlatformProjectKeyEnvVar == "" { + testsJfrogPlatformProjectKey := os.Getenv(tests.TestJfrogPlatformProjectKeyEnvVar) + if testsJfrogPlatformProjectKey == "" { t.Skipf("skipping test. %s is not set.", tests.TestJfrogPlatformProjectKeyEnvVar) } // Create the audit command with git repo context injected. @@ -114,7 +169,7 @@ func TestAuditJasViolationsProjectKeySimpleJson(t *testing.T) { _, cleanUpProject := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "projects", "jas", "jas")) defer cleanUpProject() // Create policy and watch for the project so we will get violations (unknown = all vulnerabilities will be reported as violations) - policyName, cleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "project-key-jas-violations-policy", utils.Unknown, true) + policyName, cleanUpPolicy := securityTestUtils.CreateTestSecurityPolicy(t, "project-key-jas-violations-policy", utils.Unknown, true, false) defer cleanUpPolicy() _, cleanUpWatch := securityTestUtils.CreateTestProjectKeyWatch(t, policyName, "project-key-jas-violations-watch", *tests.JfrogTestProjectKey) defer cleanUpWatch()