Skip to content

Commit

Permalink
Support pip for Curation (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
asafambar authored Apr 7, 2024
1 parent c1b4b6a commit 49f5168
Show file tree
Hide file tree
Showing 19 changed files with 797 additions and 150 deletions.
2 changes: 1 addition & 1 deletion cli/docs/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ var commandFlags = map[string][]string{
useWrapperAudit, DepType, RequirementsFile, Fail, ExtendedTable, WorkingDirs, ExclusionsAudit, Mvn, Gradle, Npm, Pnpm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis,
},
CurationAudit: {
CurationOutput, WorkingDirs, CurationThreads,
CurationOutput, WorkingDirs, CurationThreads, RequirementsFile,
},
// TODO: Deprecated commands (remove at next CLI major version)
AuditMvn: {
Expand Down
20 changes: 20 additions & 0 deletions commands/audit/sca/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (

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

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

func GetExcludePattern(params utils.AuditParams) string {
exclusions := params.Exclusions()
if len(exclusions) == 0 {
Expand Down Expand Up @@ -168,3 +171,20 @@ func setPathsForIssues(dependency *xrayUtils.GraphNode, issuesImpactPathsMap map
setPathsForIssues(depChild, issuesImpactPathsMap, pathFromRoot)
}
}

func SuspectCurationBlockedError(isCurationCmd bool, tech coreutils.Technology, cmdOutput string) (msgToUser string) {
if !isCurationCmd {
return
}
switch tech {
case coreutils.Maven:
if strings.Contains(cmdOutput, "status code: 403") || strings.Contains(cmdOutput, "status code: 500") {
msgToUser = fmt.Sprintf(curationErrorMsgToUserTemplate, coreutils.Maven)
}
case coreutils.Pip:
if strings.Contains(strings.ToLower(cmdOutput), "http error 403") {
msgToUser = fmt.Sprintf(curationErrorMsgToUserTemplate, coreutils.Pip)
}
}
return
}
61 changes: 61 additions & 0 deletions commands/audit/sca/common_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package sca

import (
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"reflect"
"testing"

Expand Down Expand Up @@ -272,3 +274,62 @@ func TestBuildImpactPaths(t *testing.T) {
expectedImpactPaths = [][]services.ImpactPathNode{{{ComponentId: "dep1"}, {ComponentId: "dep2"}, {ComponentId: "dep3"}}}
reflect.DeepEqual(expectedImpactPaths, scanResult[0].Licenses[0].Components["dep3"].ImpactPaths)
}

func TestSuspectCurationBlockedError(t *testing.T) {
mvnOutput1 := "status code: 403, reason phrase: Forbidden (403)"
mvnOutput2 := "status code: 500, reason phrase: Server Error (500)"
pipOutput := "because of HTTP error 403 Client Error: Forbidden for url"

tests := []struct {
name string
isCurationCmd bool
tech coreutils.Technology
output string
expect string
}{
{
name: "mvn 403 error",
isCurationCmd: true,
tech: coreutils.Maven,
output: mvnOutput1,
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, coreutils.Maven),
},
{
name: "mvn 500 error",
isCurationCmd: true,
tech: coreutils.Maven,
output: mvnOutput2,
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, coreutils.Maven),
},
{
name: "pip 403 error",
isCurationCmd: true,
tech: coreutils.Maven,
output: pipOutput,
expect: fmt.Sprintf(curationErrorMsgToUserTemplate, coreutils.Pip),
},
{
name: "pip not pass through error",
isCurationCmd: true,
tech: coreutils.Pip,
output: "http error 401",
},
{
name: "maven not pass through error",
isCurationCmd: true,
tech: coreutils.Maven,
output: "http error 401",
},
{
name: "nota supported tech",
isCurationCmd: true,
tech: coreutils.CI,
output: pipOutput,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
SuspectCurationBlockedError(tt.isCurationCmd, tt.tech, tt.output)
})
}
}
16 changes: 3 additions & 13 deletions commands/audit/sca/java/mvn.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
_ "embed"
"errors"
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-security/commands/audit/sca"
"net/url"
"os"
"os/exec"
Expand Down Expand Up @@ -174,7 +176,7 @@ func (mdt *MavenDepTreeManager) RunMvnCmd(goals []string) (cmdOutput []byte, err
if len(cmdOutput) > 0 {
log.Info(stringOutput)
}
if msg := mdt.suspectCurationBlockedError(stringOutput); msg != "" {
if msg := sca.SuspectCurationBlockedError(mdt.isCurationCmd, coreutils.Maven, stringOutput); msg != "" {
err = fmt.Errorf("failed running command 'mvn %s\n\n%s", strings.Join(goals, " "), msg)
} else {
err = fmt.Errorf("failed running command 'mvn %s': %s", strings.Join(goals, " "), err.Error())
Expand Down Expand Up @@ -251,15 +253,3 @@ func (mdt *MavenDepTreeManager) CreateTempDirWithSettingsXmlIfNeeded() (tempDirP
}
return
}

// In case mvn tree fails on 403 or 500 it can be related to packages blocked by curation.
// For this use case to succeed, pass through should be enabled in the curated repos
func (mdt *MavenDepTreeManager) suspectCurationBlockedError(cmdOutput string) (msgToUser string) {
if !mdt.isCurationCmd {
return
}
if strings.Contains(cmdOutput, "status code: 403") || strings.Contains(cmdOutput, "status code: 500") {
msgToUser = "Failed to get dependencies tree for maven project, Please verify pass-through enabled on the curated repos"
}
return msgToUser
}
34 changes: 0 additions & 34 deletions commands/audit/sca/java/mvn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,37 +344,3 @@ func TestRemoveMavenConfig(t *testing.T) {
assert.NoError(t, err)
assert.FileExists(t, mavenConfigPath)
}

func TestMavenDepTreeManager_suspectCurationBlockedError(t *testing.T) {
errPrefix := "[ERROR] Failed to execute goal on project my-app: Could not resolve dependencies for project com.mycompany.app:my-app:jar:1.0-SNAPSHOT: Failed to " +
"collect dependencies at junit:junit:jar:3.8.1: Failed to read artifact descriptor for junit:junit:jar:3.8.1: " +
"The following artifacts could not be resolved: junit:junit:pom:3.8.1 (absent): Could not transfer artifact junit:junit:pom:3.8.1 " +
"from/to artifactory (http://test:8046/artifactory/api/curation/audit/maven-remote):"
tests := []struct {
name string
wantMsgToUser string
input string
}{
{
name: "failed on 403",
wantMsgToUser: "Please verify pass-through enabled on the curated repos",
input: errPrefix + "status code: 403, reason phrase: Forbidden (403)",
},
{
name: "failed on 500",
wantMsgToUser: "Please verify pass-through enabled on the curated repos",
input: errPrefix + " status code: 500, reason phrase: Internal Server Error (500)",
},
{
name: "not 403 or 500",
wantMsgToUser: "",
input: errPrefix + " status code: 400, reason phrase: Forbidden (400)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mdt := &MavenDepTreeManager{}
assert.Contains(t, tt.wantMsgToUser, mdt.suspectCurationBlockedError(tt.input))
})
}
}
Loading

0 comments on commit 49f5168

Please sign in to comment.