diff --git a/commands/audit/scarunner.go b/commands/audit/scarunner.go index 11ccb429..5e3c1d05 100644 --- a/commands/audit/scarunner.go +++ b/commands/audit/scarunner.go @@ -140,20 +140,22 @@ func getRequestedDescriptors(params *AuditParams) map[techutils.Technology][]str // Preform the SCA scan for the given scan information. func executeScaScanTask(auditParallelRunner *utils.SecurityParallelRunner, serverDetails *config.ServerDetails, auditParams *AuditParams, - scan *xrayutils.ScaScanResult, treeResult *technologies.DependencyTreeResult) parallel.TaskFunc { + scan *xrayutils.ScaScanResult, treeResult *techutils.TechnologyDependencyTrees) parallel.TaskFunc { return func(threadId int) (err error) { log.Info(clientutils.GetLogMsgPrefix(threadId, false)+"Running SCA scan for", scan.Target, "vulnerable dependencies in", scan.Target, "directory...") defer func() { auditParallelRunner.ScaScansWg.Done() }() // Scan the dependency tree. - scanResults, xrayErr := runScaWithTech(scan.Technology, auditParams, serverDetails, *treeResult.FlatTree, treeResult.FullDepTrees) + flatTree := treeResult.GetAsXrayScaScanParam() + fullDepTrees := treeResult.GetUnifiedTree() + scanResults, xrayErr := runScaWithTech(scan.Technology, auditParams, serverDetails, *flatTree, fullDepTrees) if xrayErr != nil { return fmt.Errorf("%s Xray dependency tree scan request on '%s' failed:\n%s", clientutils.GetLogMsgPrefix(threadId, false), scan.Technology, xrayErr.Error()) } - scan.IsMultipleRootProject = clientutils.Pointer(len(treeResult.FullDepTrees) > 1) + scan.IsMultipleRootProject = clientutils.Pointer(len(fullDepTrees) > 1) auditParallelRunner.ResultsMu.Lock() - addThirdPartyDependenciesToParams(auditParams, scan.Technology, treeResult.FlatTree, treeResult.FullDepTrees) + addThirdPartyDependenciesToParams(auditParams, scan.Technology, flatTree, fullDepTrees) scan.XrayResults = append(scan.XrayResults, scanResults...) err = dumpScanResponseToFileIfNeeded(scanResults, auditParams.scanResultsOutputDir, utils.ScaScan) auditParallelRunner.ResultsMu.Unlock() @@ -365,8 +367,23 @@ func SetResolutionRepoIfExists(params utils.AuditParams, tech techutils.Technolo // return // } +func getDependencyTreeDetectionParams(scan *utils.ScaScanResult, params *AuditParams, serverDetails *config.ServerDetails, curationCacheDir string) techutils.DetectDependencyTreeParams { + detectionParams := techutils.DetectDependencyTreeParams{Technology: scan.Technology, Descriptors: scan.Descriptors} + // Artifactory params + detectionParams.DependenciesRepository = params.DepsRepo() + // Curation optional params + detectionParams.IncludeCuration = params.IsCurationCmd() + detectionParams.ServerDetails = serverDetails + detectionParams.CurationCacheFolder = curationCacheDir + // Common params + detectionParams.UseWrapper = params.UseWrapper() + // Maven params + detectionParams.IsMavenDepTreeInstalled = params.IsMavenDepTreeInstalled() + return detectionParams +} + // This method will change the working directory to the scan's working directory. -func buildDependencyTree(scan *utils.ScaScanResult, params *AuditParams) (*technologies.DependencyTreeResult, error) { +func buildDependencyTree(scan *utils.ScaScanResult, params *AuditParams) (*techutils.TechnologyDependencyTrees, error) { if err := os.Chdir(scan.Target); err != nil { return nil, errorutils.CheckError(err) } @@ -374,12 +391,11 @@ func buildDependencyTree(scan *utils.ScaScanResult, params *AuditParams) (*techn if err != nil { return nil, err } - // technologies.GetDependencyTree(techutils.DetectDependencyTreeParams{Technology: scan.Technology, Descriptors: scan.Descriptors}) - treeResult, techErr := technologies.GetTechDependencyTree(params.AuditBasicParams, serverDetails, scan.Technology) + treeResult, techErr := technologies.GetDependencyTree(getDependencyTreeDetectionParams(scan, params, serverDetails))// technologies.GetTechDependencyTree(params.AuditBasicParams, serverDetails, scan.Technology) if techErr != nil { return nil, fmt.Errorf("failed while building '%s' dependency tree:\n%s", scan.Technology, techErr.Error()) } - if treeResult.FlatTree == nil || len(treeResult.FlatTree.Nodes) == 0 { + if len(treeResult.UniqueDependencies) == 0 { return nil, errorutils.CheckErrorf("no dependencies were found. Please try to build your project and re-run the audit command") } return &treeResult, nil diff --git a/technologies/java/mvn.go b/technologies/java/mvn.go new file mode 100644 index 00000000..550a64af --- /dev/null +++ b/technologies/java/mvn.go @@ -0,0 +1,31 @@ +package java + +import ( + "fmt" + + "github.com/owenrumney/go-sarif/v2/sarif" + + "github.com/jfrog/jfrog-cli-security/utils/techutils" +) + +const ( + mavenDepTreeJarFile = "maven-dep-tree.jar" + mavenDepTreeOutputFile = "mavendeptree.out" + // Changing this version also requires a change in MAVEN_DEP_TREE_VERSION within buildscripts/download_jars.sh + mavenDepTreeVersion = "1.1.1" + settingsXmlFile = "settings.xml" +) + +type MavenTechnologyHandler struct {} + +func (handler *MavenTechnologyHandler) GetTechDependencyTree(params techutils.DetectDependencyTreeParams) (techutils.TechnologyDependencyTrees, error) { + return techutils.TechnologyDependencyTrees{}, fmt.Errorf("Not implemented") +} + +func (handler *MavenTechnologyHandler) GetTechDependencyLocations(directDependencyName, directDependencyVersion string, descriptorPaths ...string) ([]*sarif.Location, error) { + return nil, fmt.Errorf("Not implemented") +} + +func (handler *MavenTechnologyHandler) ChangeTechDependencyVersion(directDependencyName, directDependencyVersion, fixVersion string, descriptorPaths ...string) error { + return fmt.Errorf("Not implemented") +} \ No newline at end of file diff --git a/technologies/technologies.go b/technologies/technologies.go index e9c66410..480701ce 100644 --- a/technologies/technologies.go +++ b/technologies/technologies.go @@ -6,6 +6,7 @@ import ( "time" "github.com/jfrog/build-info-go/utils/pythonutils" + "github.com/owenrumney/go-sarif/v2/sarif" clientUtils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" @@ -24,25 +25,78 @@ import ( "github.com/jfrog/jfrog-cli-security/commands/audit/sca/python" "github.com/jfrog/jfrog-cli-security/commands/audit/sca/yarn" + javaHandlers "github.com/jfrog/jfrog-cli-security/technologies/java" + "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-cli-security/utils/techutils" "github.com/jfrog/jfrog-cli-security/utils/xray" ) var TechnologyHandlers = map[techutils.Technology]techutils.TechnologyHandler{ + // Java technologies + techutils.Maven: &javaHandlers.MavenTechnologyHandler{}, +} + +func GetTechHandler(tech techutils.Technology) (techutils.TechnologyHandler, error) { + if handler, ok := TechnologyHandlers[tech]; ok { + return handler, nil + } + return nil, fmt.Errorf("%s is currently not supported", tech.ToFormal()) +} +func GetTechDependencyLocations(tech techutils.Technology, directDependencyName, directDependencyVersion string, descriptorPaths ...string) ([]*sarif.Location, error) { + handler, err := GetTechHandler(tech) + if err != nil { + return nil, err + } + return handler.GetTechDependencyLocations(directDependencyName, directDependencyVersion, descriptorPaths...) } -func GetDependencyTree(params techutils.DetectDependencyTreeParams) (*techutils.TechnologyDependencyTrees, error) { - if handler, ok := TechnologyHandlers[params.Technology]; ok { +func ChangeTechDependencyVersion(tech techutils.Technology, directDependencyName, directDependencyVersion, fixVersion string, descriptorPaths ...string) error { + handler, err := GetTechHandler(tech) + if err != nil { + return err + } + return handler.ChangeTechDependencyVersion(directDependencyName, directDependencyVersion, fixVersion, descriptorPaths...) +} + +func GetDependencyTree(params techutils.DetectDependencyTreeParams) (techutils.TechnologyDependencyTrees, error) { + if handler, err := GetTechHandler(params.Technology); err == nil { log.Info(fmt.Sprintf("Handler Calculating %s dependencies...", params.Technology.ToFormal())) if tree, err := handler.GetTechDependencyTree(params); err == nil { return tree, nil } - log.Warn(fmt.Sprintf("Handler failed to calculate %s dependencies, falling back to default handler", params.Technology.ToFormal())) + return techutils.TechnologyDependencyTrees{}, fmt.Errorf("Handler failed to calculate %s dependencies", params.Technology.ToFormal()) } + return runFallback(params) +} + +func runFallback(params techutils.DetectDependencyTreeParams) (techutils.TechnologyDependencyTrees, error) { log.Info(fmt.Sprintf("Calculating %s dependencies...", params.Technology.ToFormal())) - return nil, fmt.Errorf("%s is currently not supported", string(params.Technology)) + auditParams := toAuditParams(params) + oldTreeStruct, err := GetTechDependencyTree(auditParams, params.ServerDetails, params.Technology) + if err != nil { + return techutils.TechnologyDependencyTrees{}, err + } + return toResultNewStruct(oldTreeStruct), nil +} + +func toAuditParams(params techutils.DetectDependencyTreeParams) utils.AuditParams { + auditParams := &utils.AuditBasicParams{} + auditParams.SetServerDetails(params.ServerDetails) + return auditParams +} + +func toResultNewStruct(oldTreeStruct DependencyTreeResult) techutils.TechnologyDependencyTrees { + uniqueDeps := make([]string, 0, len(oldTreeStruct.FlatTree.Nodes)) + for _, node := range oldTreeStruct.FlatTree.Nodes { + uniqueDeps = append(uniqueDeps, node.Id) + } + tree := map[string]*xrayUtils.GraphNode{} + for _, node := range oldTreeStruct.FullDepTrees { + tree["root"] = node + } + return techutils.TechnologyDependencyTrees{DownloadUrls: oldTreeStruct.DownloadUrls, UniqueDependencies: uniqueDeps, DependencyTrees: tree} } type DependencyTreeResult struct { diff --git a/utils/techutils/techutils.go b/utils/techutils/techutils.go index 76ed5de5..a96924cb 100644 --- a/utils/techutils/techutils.go +++ b/utils/techutils/techutils.go @@ -8,13 +8,14 @@ import ( "regexp" "strings" + "github.com/owenrumney/go-sarif/v2/sarif" "golang.org/x/exp/maps" "golang.org/x/text/cases" "golang.org/x/text/language" - "github.com/owenrumney/go-sarif/v2/sarif" "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/common/project" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-client-go/artifactory/services/fspatterns" "github.com/jfrog/jfrog-client-go/utils/errorutils" @@ -73,8 +74,22 @@ var TechToProjectType = map[Technology]project.ProjectType{ type DetectDependencyTreeParams struct { Technology Technology `json:"technology"` + // If the tech need to create temp file for the output of the command it should output it to this path. + OutputDirPath string `json:"outputDirPath,omitempty"` // Files that the technology handlers use to detect the project's dependencies. Descriptors []string `json:"descriptors"` + // Artifactory related options + DependenciesRepository string `json:"dependenciesRepository,omitempty"` + // Curation related options + IncludeCuration bool `json:"includeCuration,omitempty"` + ServerDetails *config.ServerDetails `json:"artifactoryServerDetails,omitempty"` + CurationCacheFolder string `json:"curationCacheFolder,omitempty"` + + // Common Tech options + UseWrapper bool `json:"useWrapper,omitempty"` + + // Specific Maven options + IsMavenDepTreeInstalled bool `json:"isMavenDepTreeInstalled,omitempty"` } type TechnologyDependencyTrees struct { @@ -90,17 +105,24 @@ func (tdr TechnologyDependencyTrees) GetAsXrayScaScanParam() *xrayUtils.GraphNod } } +func (tdr TechnologyDependencyTrees) GetUnifiedTree() []*xrayUtils.GraphNode { + return []*xrayUtils.GraphNode{} +} + type TechnologyHandler interface { // Get a dependency tree for each descriptor file, the tree will have a root node id with the descriptor/project id, second level nodes are the direct dependencies... // If no descriptor files are provided, the handler will try to use cwd as the context to find the dependencies. - GetTechDependencyTree(params DetectDependencyTreeParams) (*TechnologyDependencyTrees, error) + GetTechDependencyTree(params DetectDependencyTreeParams) (TechnologyDependencyTrees, error) // Get the locations of the direct dependency in the given descriptor files. if no descriptor files are provided, the handler will try to find at cwd. - GetTechDependencyLocations(directDependencyName, directDependencyVersion string, descriptorPaths ...string) ([]*sarif.Location, error) + GetTechDependencyLocations(directDependencyName, directDependencyVersion string, descriptorPaths ...string) ([]*sarif.Location, error) // maybe ([]formats.ComponentRow, error) // Change a direct dependency version in the given descriptor files. if no descriptor files are provided, the handler will try to find at cwd. - FixTechDependencyVersion(directDependencyName, directDependencyVersion, fixVersion string, descriptorPaths ...string) error + ChangeTechDependencyVersion(directDependencyName, directDependencyVersion, fixVersion string, descriptorPaths ...string) error } type TechData struct { + techIdentifier string + + // The name of the package type used in this technology. packageType string // Suffixes of file/directory names that indicate if a project uses this technology. @@ -130,11 +152,13 @@ var technologiesData = map[Technology]TechData{ Maven: { indicators: []string{"pom.xml"}, packageDescriptors: []string{"pom.xml"}, + techIdentifier: "gav", execCommand: "mvn", }, Gradle: { indicators: []string{"build.gradle", "build.gradle.kts"}, packageDescriptors: []string{"build.gradle", "build.gradle.kts"}, + techIdentifier: "gav", }, Npm: { indicators: []string{"package.json", "package-lock.json", "npm-shrinkwrap.json"},