Skip to content

Commit 4170f2e

Browse files
authored
curation go support (#67)
1 parent e233194 commit 4170f2e

23 files changed

+353
-74
lines changed

commands/audit/sca/common.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222

2323
var DefaultExcludePatterns = []string{"*.git*", "*node_modules*", "*target*", "*venv*", "*test*"}
2424

25-
var curationErrorMsgToUserTemplate = "Failed to retrieve the dependencies tree for the %s project. Please contact your " +
25+
var CurationErrorMsgToUserTemplate = "Failed to retrieve the dependencies tree for the %s project. Please contact your " +
2626
"Artifactory administrator to verify pass-through for Curation audit is enabled for your project"
2727

2828
func GetExcludePattern(params utils.AuditParams) string {
@@ -180,11 +180,15 @@ func SuspectCurationBlockedError(isCurationCmd bool, tech techutils.Technology,
180180
case techutils.Maven:
181181
if strings.Contains(cmdOutput, "status code: 403") || strings.Contains(strings.ToLower(cmdOutput), "403 forbidden") ||
182182
strings.Contains(cmdOutput, "status code: 500") {
183-
msgToUser = fmt.Sprintf(curationErrorMsgToUserTemplate, techutils.Maven)
183+
msgToUser = fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Maven)
184184
}
185185
case techutils.Pip:
186186
if strings.Contains(strings.ToLower(cmdOutput), "http error 403") {
187-
msgToUser = fmt.Sprintf(curationErrorMsgToUserTemplate, techutils.Pip)
187+
msgToUser = fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Pip)
188+
}
189+
case techutils.Go:
190+
if strings.Contains(strings.ToLower(cmdOutput), "403 forbidden") {
191+
msgToUser = fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Go)
188192
}
189193
}
190194
return

commands/audit/sca/common_test.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ func TestSuspectCurationBlockedError(t *testing.T) {
280280
mvnOutput1 := "status code: 403, reason phrase: Forbidden (403)"
281281
mvnOutput2 := "status code: 500, reason phrase: Server Error (500)"
282282
pipOutput := "because of HTTP error 403 Client Error: Forbidden for url"
283+
goOutput := "Failed running Go command: 403 Forbidden"
283284

284285
tests := []struct {
285286
name string
@@ -293,21 +294,21 @@ func TestSuspectCurationBlockedError(t *testing.T) {
293294
isCurationCmd: true,
294295
tech: techutils.Maven,
295296
output: mvnOutput1,
296-
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, techutils.Maven),
297+
expect: fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Maven),
297298
},
298299
{
299300
name: "mvn 500 error",
300301
isCurationCmd: true,
301302
tech: techutils.Maven,
302303
output: mvnOutput2,
303-
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, techutils.Maven),
304+
expect: fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Maven),
304305
},
305306
{
306307
name: "pip 403 error",
307308
isCurationCmd: true,
308-
tech: techutils.Maven,
309+
tech: techutils.Pip,
309310
output: pipOutput,
310-
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, techutils.Pip),
311+
expect: fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Pip),
311312
},
312313
{
313314
name: "pip not pass through error",
@@ -322,15 +323,22 @@ func TestSuspectCurationBlockedError(t *testing.T) {
322323
output: "http error 401",
323324
},
324325
{
325-
name: "nota supported tech",
326+
name: "golang 403 error",
327+
isCurationCmd: true,
328+
tech: techutils.Go,
329+
output: goOutput,
330+
expect: fmt.Sprintf(CurationErrorMsgToUserTemplate, techutils.Go),
331+
},
332+
{
333+
name: "not a supported tech",
326334
isCurationCmd: true,
327335
tech: coreutils.CI,
328336
output: pipOutput,
329337
},
330338
}
331339
for _, tt := range tests {
332340
t.Run(tt.name, func(t *testing.T) {
333-
SuspectCurationBlockedError(tt.isCurationCmd, tt.tech, tt.output)
341+
assert.Equal(t, SuspectCurationBlockedError(tt.isCurationCmd, tt.tech, tt.output), tt.expect)
334342
})
335343
}
336344
}

commands/audit/sca/go/golang.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package _go
22

33
import (
4+
"errors"
45
"fmt"
56
biutils "github.com/jfrog/build-info-go/utils"
67
"github.com/jfrog/gofrog/datastructures"
78
goartifactoryutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/golang"
89
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
910
goutils "github.com/jfrog/jfrog-cli-core/v2/utils/golang"
11+
"github.com/jfrog/jfrog-cli-security/commands/audit/sca"
1012
"github.com/jfrog/jfrog-cli-security/utils"
13+
"github.com/jfrog/jfrog-cli-security/utils/techutils"
1114
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
1215
"strings"
1316
)
@@ -28,20 +31,36 @@ func BuildDependencyTree(params utils.AuditParams) (dependencyTree []*xrayUtils.
2831
err = fmt.Errorf("failed while getting server details: %s", err.Error())
2932
return
3033
}
34+
goProxyParams := goutils.GoProxyUrlParams{Direct: true}
35+
// in case of curation command, we set an alternative cache folder when building go dep tree,
36+
// also, it's not using the "direct" option, artifacts should be resolved only from the configured repo.
37+
if params.IsCurationCmd() {
38+
goProxyParams.EndpointPrefix = coreutils.CurationPassThroughApi
39+
goProxyParams.Direct = false
40+
projCacheDir, errCacheFolder := utils.GetCurationCacheFolderByTech(techutils.Go)
41+
if errCacheFolder != nil {
42+
err = errCacheFolder
43+
return
44+
}
45+
if err = goartifactoryutils.SetGoModCache(projCacheDir); err != nil {
46+
return
47+
}
48+
}
3149

3250
remoteGoRepo := params.DepsRepo()
3351
if remoteGoRepo != "" {
34-
if err = goartifactoryutils.SetArtifactoryAsResolutionServer(server, remoteGoRepo); err != nil {
52+
if err = goartifactoryutils.SetArtifactoryAsResolutionServer(server, remoteGoRepo, goProxyParams); err != nil {
3553
return
3654
}
3755
}
56+
3857
// Calculate go dependencies graph
39-
dependenciesGraph, err := goutils.GetDependenciesGraph(currentDir)
58+
dependenciesGraph, err := utils.GetDependenciesGraph(currentDir)
4059
if err != nil || len(dependenciesGraph) == 0 {
4160
return
4261
}
4362
// Calculate go dependencies list
44-
dependenciesList, err := goutils.GetDependenciesList(currentDir)
63+
dependenciesList, err := utils.GetDependenciesList(currentDir, handleCurationGoError)
4564
if err != nil {
4665
return
4766
}
@@ -58,16 +77,37 @@ func BuildDependencyTree(params utils.AuditParams) (dependencyTree []*xrayUtils.
5877
uniqueDepsSet := datastructures.MakeSet[string]()
5978
populateGoDependencyTree(rootNode, dependenciesGraph, dependenciesList, uniqueDepsSet)
6079

80+
// In case of curation command, go version is not relevant as it can't be resolved from go repo
81+
if !params.IsCurationCmd() {
82+
if gotErr := addGoVersionToTree(rootNode, uniqueDepsSet); gotErr != nil {
83+
err = gotErr
84+
return
85+
}
86+
}
87+
88+
dependencyTree = []*xrayUtils.GraphNode{rootNode}
89+
uniqueDeps = uniqueDepsSet.ToSlice()
90+
return
91+
}
92+
93+
func addGoVersionToTree(rootNode *xrayUtils.GraphNode, uniqueDepsSet *datastructures.Set[string]) error {
6194
goVersionDependency, err := getGoVersionAsDependency()
6295
if err != nil {
63-
return
96+
return err
6497
}
6598
rootNode.Nodes = append(rootNode.Nodes, goVersionDependency)
6699
uniqueDepsSet.Add(goVersionDependency.Id)
100+
return err
101+
}
67102

68-
dependencyTree = []*xrayUtils.GraphNode{rootNode}
69-
uniqueDeps = uniqueDepsSet.ToSlice()
70-
return
103+
func handleCurationGoError(err error) (bool, error) {
104+
if err == nil {
105+
return false, nil
106+
}
107+
if msgToUser := sca.SuspectCurationBlockedError(true, techutils.Go, err.Error()); msgToUser != "" {
108+
return true, errors.New(msgToUser)
109+
}
110+
return false, nil
71111
}
72112

73113
func populateGoDependencyTree(currNode *xrayUtils.GraphNode, dependenciesGraph map[string][]string, dependenciesList map[string]bool, uniqueDepsSet *datastructures.Set[string]) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package _go
22

33
import (
4+
"errors"
5+
"fmt"
6+
"github.com/jfrog/jfrog-cli-security/utils/techutils"
47
"os"
58
"path/filepath"
69
"strings"
@@ -77,3 +80,32 @@ func removeTxtSuffix(txtFileName string) error {
7780
// go.sum.txt >> go.sum
7881
return fileutils.MoveFile(txtFileName, strings.TrimSuffix(txtFileName, ".txt"))
7982
}
83+
84+
func Test_handleCurationGoError(t *testing.T) {
85+
86+
tests := []struct {
87+
name string
88+
err error
89+
expectedError error
90+
}{
91+
{
92+
name: "curation error 403",
93+
err: errors.New("package download failed due to 403 forbidden test failure"),
94+
expectedError: fmt.Errorf(sca.CurationErrorMsgToUserTemplate, techutils.Go),
95+
},
96+
{
97+
name: "not curation error 500",
98+
err: errors.New("package download failed due to 500 internal server error test failure"),
99+
},
100+
{
101+
name: "no error",
102+
},
103+
}
104+
for _, tt := range tests {
105+
t.Run(tt.name, func(t *testing.T) {
106+
got, err := handleCurationGoError(tt.err)
107+
assert.Equal(t, tt.expectedError, err)
108+
assert.Equal(t, tt.expectedError != nil, got)
109+
})
110+
}
111+
}

commands/audit/scarunner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ func getDirectDependenciesFromTree(dependencyTrees []*xrayCmdUtils.GraphNode) []
182182
}
183183

184184
func getCurationCacheByTech(tech techutils.Technology) (string, error) {
185-
if tech == techutils.Maven {
186-
return xrayutils.GetCurationMavenCacheFolder()
185+
if tech == techutils.Maven || tech == techutils.Go {
186+
return xrayutils.GetCurationCacheFolderByTech(tech)
187187
}
188188
return "", nil
189189
}

commands/curation/curationaudit.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const (
5454
TotalConcurrentRequests = 10
5555

5656
MinArtiPassThroughSupport = "7.82.0"
57+
MinArtiGolangSupport = "7.87.0"
5758
MinXrayPassTHroughSupport = "3.92.0"
5859
)
5960

@@ -62,20 +63,23 @@ var CurationOutputFormats = []string{string(outFormat.Table), string(outFormat.J
6263
var supportedTech = map[techutils.Technology]func(ca *CurationAuditCommand) (bool, error){
6364
techutils.Npm: func(ca *CurationAuditCommand) (bool, error) { return true, nil },
6465
techutils.Pip: func(ca *CurationAuditCommand) (bool, error) {
65-
return ca.checkSupportByVersionOrEnv(techutils.Pip, utils.CurationPipSupport)
66+
return ca.checkSupportByVersionOrEnv(techutils.Pip, utils.CurationSupportFlag, MinArtiPassThroughSupport)
6667
},
6768
techutils.Maven: func(ca *CurationAuditCommand) (bool, error) {
68-
return ca.checkSupportByVersionOrEnv(techutils.Maven, utils.CurationMavenSupport)
69+
return ca.checkSupportByVersionOrEnv(techutils.Maven, utils.CurationSupportFlag, MinArtiPassThroughSupport)
70+
},
71+
techutils.Go: func(ca *CurationAuditCommand) (bool, error) {
72+
return ca.checkSupportByVersionOrEnv(techutils.Go, utils.CurationSupportFlag, MinArtiGolangSupport)
6973
},
7074
}
7175

72-
func (ca *CurationAuditCommand) checkSupportByVersionOrEnv(tech techutils.Technology, envName string) (bool, error) {
76+
func (ca *CurationAuditCommand) checkSupportByVersionOrEnv(tech techutils.Technology, envName string, minArtiVersion string) (bool, error) {
7377
if flag, err := clientutils.GetBoolEnvValue(envName, false); flag {
7478
return true, nil
7579
} else if err != nil {
7680
log.Error(err)
7781
}
78-
rtVersion, serverDetails, err := ca.getRtVersionAndServiceDetails(tech)
82+
artiVersion, serverDetails, err := ca.getRtVersionAndServiceDetails(tech)
7983
if err != nil {
8084
return false, err
8185
}
@@ -86,7 +90,7 @@ func (ca *CurationAuditCommand) checkSupportByVersionOrEnv(tech techutils.Techno
8690
}
8791

8892
xrayVersionErr := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, MinXrayPassTHroughSupport)
89-
rtVersionErr := clientutils.ValidateMinimumVersion(clientutils.Artifactory, rtVersion, MinArtiPassThroughSupport)
93+
rtVersionErr := clientutils.ValidateMinimumVersion(clientutils.Artifactory, artiVersion, minArtiVersion)
9094
if xrayVersionErr != nil || rtVersionErr != nil {
9195
return false, errors.Join(xrayVersionErr, rtVersionErr)
9296
}
@@ -304,6 +308,7 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map
304308
return err
305309
}
306310
rootNode := depTreeResult.FullDepTrees[0]
311+
// we don't pass artiUrl and repo as we don't want to download the package, only to get the name and version.
307312
_, projectName, projectScope, projectVersion := getUrlNameAndVersionByTech(tech, rootNode, nil, "", "")
308313
if projectName == "" {
309314
workPath, err := os.Getwd()
@@ -312,9 +317,12 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map
312317
}
313318
projectName = filepath.Base(workPath)
314319
}
315-
320+
fullProjectName := projectName
321+
if projectVersion != "" {
322+
fullProjectName += ":" + projectVersion
323+
}
316324
if ca.Progress() != nil {
317-
ca.Progress().SetHeadlineMsg(fmt.Sprintf("Fetch curation status for %s graph with %v nodes project name: %s:%s", tech.ToFormal(), len(depTreeResult.FlatTree.Nodes)-1, projectName, projectVersion))
325+
ca.Progress().SetHeadlineMsg(fmt.Sprintf("Fetch curation status for %s graph with %v nodes project name: %s", tech.ToFormal(), len(depTreeResult.FlatTree.Nodes)-1, fullProjectName))
318326
}
319327
if projectScope != "" {
320328
projectName = projectScope + "/" + projectName
@@ -626,7 +634,8 @@ func getUrlNameAndVersionByTech(tech techutils.Technology, node *xrayUtils.Graph
626634
case techutils.Pip:
627635
downloadUrls, name, version = getPythonNameVersion(node.Id, downloadUrlsMap)
628636
return
629-
637+
case techutils.Go:
638+
return getGoNameScopeAndVersion(node.Id, artiUrl, repo)
630639
}
631640
return
632641
}
@@ -648,7 +657,21 @@ func getPythonNameVersion(id string, downloadUrlsMap map[string]string) (downloa
648657
return
649658
}
650659

651-
// input- id: gav://org.apache.tomcat.embed:tomcat-embed-jasper:8.0.33
660+
// input - id: go://github.com/kennygrant/sanitize:v1.2.4
661+
// input - repo: go
662+
// output: downloadUrl: <artiUrl>/api/go/go/github.com/kennygrant/sanitize/@v/v1.2.4.zip
663+
func getGoNameScopeAndVersion(id, artiUrl, repo string) (downloadUrls []string, name, scope, version string) {
664+
id = strings.TrimPrefix(id, techutils.Go.String()+"://")
665+
nameVersion := strings.Split(id, ":")
666+
name = nameVersion[0]
667+
if len(nameVersion) > 1 {
668+
version = nameVersion[1]
669+
}
670+
url := strings.TrimSuffix(artiUrl, "/") + "/api/go/" + repo + "/" + name + "/@v/" + version + ".zip"
671+
return []string{url}, name, "", version
672+
}
673+
674+
// input(with classifier) - id: gav://org.apache.tomcat.embed:tomcat-embed-jasper:8.0.33-jdk15
652675
// input - repo: libs-release
653676
// output - downloadUrl: <arti-url>/libs-release/org/apache/tomcat/embed/tomcat-embed-jasper/8.0.33/tomcat-embed-jasper-8.0.33-jdk15.jar
654677
func getMavenNameScopeAndVersion(id, artiUrl, repo string, node *xrayUtils.GraphNode) (downloadUrls []string, name, scope, version string) {

0 commit comments

Comments
 (0)