diff --git a/xray/commands/audit/jas/applicability/applicabilitymanager.go b/xray/commands/audit/jas/applicability/applicabilitymanager.go index c258e8b74..b038e4371 100644 --- a/xray/commands/audit/jas/applicability/applicabilitymanager.go +++ b/xray/commands/audit/jas/applicability/applicabilitymanager.go @@ -144,7 +144,7 @@ type scanConfiguration struct { func (asm *ApplicabilityScanManager) createConfigFile(workingDir string) error { skipDirs := jas.SkippedDirs if asm.thirdPartyScan { - log.Debug("Including node modules folder in applicability scan") + log.Info("Including node modules folder in applicability scan") skipDirs = removeElementFromSlice(skipDirs, jas.NodeModulesPattern) } configFileContent := applicabilityScanConfig{ @@ -170,5 +170,8 @@ func (asm *ApplicabilityScanManager) runAnalyzerManager() error { func removeElementFromSlice(skipDirs []string, element string) []string { deleteIndex := slices.Index(skipDirs, element) + if deleteIndex == -1 { + return skipDirs + } return slices.Delete(skipDirs, deleteIndex, deleteIndex+1) } diff --git a/xray/commands/audit/sca/npm/npm.go b/xray/commands/audit/sca/npm/npm.go index 7a646e349..269c6993e 100644 --- a/xray/commands/audit/sca/npm/npm.go +++ b/xray/commands/audit/sca/npm/npm.go @@ -5,14 +5,14 @@ import ( buildinfo "github.com/jfrog/build-info-go/entities" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca" + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/log" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "golang.org/x/exp/slices" ) const ( - npmPackageTypeIdentifier = "npm://" - ignoreScriptsFlag = "--ignore-scripts" + ignoreScriptsFlag = "--ignore-scripts" ) func BuildDependencyTree(npmArgs []string) (dependencyTrees []*xrayUtils.GraphNode, uniqueDeps []string, err error) { @@ -58,9 +58,9 @@ func addIgnoreScriptsFlag(npmArgs []string) []string { func parseNpmDependenciesList(dependencies []buildinfo.Dependency, packageInfo *biutils.PackageInfo) (*xrayUtils.GraphNode, []string) { treeMap := make(map[string][]string) for _, dependency := range dependencies { - dependencyId := npmPackageTypeIdentifier + dependency.Id + dependencyId := utils.NpmPackageTypeIdentifier + dependency.Id for _, requestedByNode := range dependency.RequestedBy { - parent := npmPackageTypeIdentifier + requestedByNode[0] + parent := utils.NpmPackageTypeIdentifier + requestedByNode[0] if children, ok := treeMap[parent]; ok { treeMap[parent] = appendUniqueChild(children, dependencyId) } else { @@ -68,7 +68,7 @@ func parseNpmDependenciesList(dependencies []buildinfo.Dependency, packageInfo * } } } - return sca.BuildXrayDependencyTree(treeMap, npmPackageTypeIdentifier+packageInfo.BuildInfoModuleId()) + return sca.BuildXrayDependencyTree(treeMap, utils.NpmPackageTypeIdentifier+packageInfo.BuildInfoModuleId()) } func appendUniqueChild(children []string, candidateDependency string) []string { diff --git a/xray/commands/audit/sca/npm/npm_test.go b/xray/commands/audit/sca/npm/npm_test.go index aaa9ea47c..ff4d0aa01 100644 --- a/xray/commands/audit/sca/npm/npm_test.go +++ b/xray/commands/audit/sca/npm/npm_test.go @@ -3,6 +3,7 @@ package npm import ( "encoding/json" "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca" + "github.com/jfrog/jfrog-cli-core/v2/xray/utils" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "os" "testing" @@ -102,7 +103,7 @@ func TestParseNpmDependenciesList(t *testing.T) { } expectedUniqueDeps := []string{xrayDependenciesTree.Id} for _, dep := range dependencies { - expectedUniqueDeps = append(expectedUniqueDeps, npmPackageTypeIdentifier+dep.Id) + expectedUniqueDeps = append(expectedUniqueDeps, utils.NpmPackageTypeIdentifier+dep.Id) } assert.ElementsMatch(t, uniqueDeps, expectedUniqueDeps, "First is actual, Second is Expected") diff --git a/xray/commands/audit/scarunner.go b/xray/commands/audit/scarunner.go index 9e7932ae0..8ad04bc7f 100644 --- a/xray/commands/audit/scarunner.go +++ b/xray/commands/audit/scarunner.go @@ -96,16 +96,7 @@ func runScaScanOnWorkingDir(params *AuditParams, results *Results, workingDir, r continue } techResults = sca.BuildImpactPathsForScanResponse(techResults, fullDependencyTrees) - var directDependencies []string - if tech == coreutils.Pip || (params.thirdPartyApplicabilityScan && tech == coreutils.Npm) { - // When building pip dependency tree using pipdeptree, some of the direct dependencies are recognized as transitive and missed by the CA scanner. - // Our solution for this case is to send all dependencies to the CA scanner. - // When thirdPartyApplicabilityScan is true, use flatten graph to include all the dependencies in applicability scanning. - // Only npm is supported for this flag. - directDependencies = getDirectDependenciesFromTree([]*xrayCmdUtils.GraphNode{flattenTree}) - } else { - directDependencies = getDirectDependenciesFromTree(fullDependencyTrees) - } + directDependencies := getDirectDependencies(params, tech, flattenTree, fullDependencyTrees) params.AppendDirectDependencies(directDependencies) results.ExtendedScanResults.XrayResults = append(results.ExtendedScanResults.XrayResults, techResults...) @@ -117,6 +108,19 @@ func runScaScanOnWorkingDir(params *AuditParams, results *Results, workingDir, r return } +// When building pip dependency tree using pipdeptree, some of the direct dependencies are recognized as transitive and missed by the CA scanner. +// Our solution for this case is to send all dependencies to the CA scanner. +// When thirdPartyApplicabilityScan is true, use flatten graph to include all the dependencies in applicability scanning. +// Only npm is supported for this flag. +func getDirectDependencies(params *AuditParams, tech coreutils.Technology, flattenTree *xrayCmdUtils.GraphNode, fullDependencyTrees []*xrayCmdUtils.GraphNode) (directDependencies []string) { + if tech == coreutils.Pip || (params.thirdPartyApplicabilityScan && tech == coreutils.Npm) { + directDependencies = getDirectDependenciesFromTree([]*xrayCmdUtils.GraphNode{flattenTree}) + } else { + directDependencies = getDirectDependenciesFromTree(fullDependencyTrees) + } + return directDependencies +} + // This function retrieves the dependency trees of the scanned project and extracts a set that contains only the direct dependencies. func getDirectDependenciesFromTree(dependencyTrees []*xrayCmdUtils.GraphNode) []string { directDependencies := datastructures.MakeSet[string]() diff --git a/xray/utils/resultstable.go b/xray/utils/resultstable.go index 5541bc44d..937e8a244 100644 --- a/xray/utils/resultstable.go +++ b/xray/utils/resultstable.go @@ -959,21 +959,25 @@ func getApplicableCveValue(extendedResults *ExtendedScanResults, xrayCves []form return ApplicabilityUndetermined } -func getCveApplicability(cve formats.CveRow, applicabilityScanResults []*sarif.Run, components map[string]services.Component) *formats.Applicability { - applicability := &formats.Applicability{Status: string(ApplicabilityUndetermined)} +func getCveApplicability(cve formats.CveRow, applicabilityScanResults []*sarif.Run, components map[string]services.Component) (applicability *formats.Applicability) { + applicability = &formats.Applicability{} + if len(applicabilityScanResults) == 0 { + return + } + resultFound := false for _, applicabilityRun := range applicabilityScanResults { - foundResult, _ := applicabilityRun.GetResultByRuleId(CveToApplicabilityRuleId(cve.Id)) - if foundResult == nil { + result, _ := applicabilityRun.GetResultByRuleId(CveToApplicabilityRuleId(cve.Id)) + if result == nil { continue } - applicability = &formats.Applicability{} - foundRule, _ := applicabilityRun.GetRuleById(CveToApplicabilityRuleId(cve.Id)) - if foundRule != nil { - applicability.ScannerDescription = GetRuleFullDescription(foundRule) + resultFound = true + rule, _ := applicabilityRun.GetRuleById(CveToApplicabilityRuleId(cve.Id)) + if rule != nil { + applicability.ScannerDescription = GetRuleFullDescription(rule) } // Add new evidences from locations - for _, location := range foundResult.Locations { + for _, location := range result.Locations { fileName := GetRelativeLocationFileName(location, applicabilityRun.Invocations) if shouldDisqualifyEvidence(components, fileName) { continue @@ -987,16 +991,20 @@ func getCveApplicability(cve formats.CveRow, applicabilityScanResults []*sarif.R EndColumn: GetLocationEndColumn(location), Snippet: GetLocationSnippet(location), }, - Reason: GetResultMsgText(foundResult), + Reason: GetResultMsgText(result), }) } } - if len(applicability.Evidence) == 0 { + + switch { + case !resultFound: + applicability.Status = string(ApplicabilityUndetermined) + case len(applicability.Evidence) == 0: applicability.Status = string(NotApplicable) - } else { + default: applicability.Status = string(Applicable) } - return applicability + return } func printApplicableCveValue(applicableValue ApplicabilityStatus, isTable bool) string { @@ -1024,10 +1032,10 @@ func printApplicableCveValue(applicableValue ApplicabilityStatus, isTable bool) // Found use of a badCode inside the node_modules from a different package, report applicable. func shouldDisqualifyEvidence(components map[string]services.Component, evidenceFilePath string) (disqualify bool) { for key := range components { - dependencyName := extractNpmDependencyNameFromComponent(key) - if dependencyName == "" { + if !strings.HasPrefix(key, NpmPackageTypeIdentifier) { return } + dependencyName := extractDependencyNameFromComponent(key, NpmPackageTypeIdentifier) // Check both Unix & Windows paths. if strings.Contains(evidenceFilePath, nodeModules+"/"+dependencyName) || strings.Contains(evidenceFilePath, filepath.Join(nodeModules, dependencyName)) { return true @@ -1036,11 +1044,8 @@ func shouldDisqualifyEvidence(components map[string]services.Component, evidence return } -func extractNpmDependencyNameFromComponent(key string) (dependencyName string) { - if !strings.HasPrefix(key, NpmPackageTypeIdentifier) { - return - } - packageAndVersion := strings.TrimPrefix(key, NpmPackageTypeIdentifier) +func extractDependencyNameFromComponent(key string, techIdentifier string) (dependencyName string) { + packageAndVersion := strings.TrimPrefix(key, techIdentifier) split := strings.Split(packageAndVersion, ":") if len(split) < 2 { return