From 4befe41129f87427f3fe856f4beb1b6f8874e9b4 Mon Sep 17 00:00:00 2001 From: Omer Zidkoni <50792403+omerzi@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:55:38 +0300 Subject: [PATCH] Add technologies contextual analysis support flag (#863) --- artifactory/utils/dependenciesutils.go | 2 +- utils/coreutils/techutils.go | 42 ++++-- utils/coreutils/techutils_test.go | 17 +++ xray/audit/jas/applicabilitymanager.go | 154 ++++++-------------- xray/audit/jas/applicabilitymanager_test.go | 145 ++++++------------ xray/audit/jas/jasmanager.go | 29 ++-- xray/audit/jas/jasmanager_test.go | 28 +++- xray/commands/audit/generic/auditmanager.go | 92 ++++++------ xray/commands/audit/generic/generic.go | 1 + xray/utils/analyzermanager.go | 19 +-- xray/utils/resultstable.go | 20 +-- 11 files changed, 235 insertions(+), 314 deletions(-) diff --git a/artifactory/utils/dependenciesutils.go b/artifactory/utils/dependenciesutils.go index e595dcce1..7cfe5c4bf 100644 --- a/artifactory/utils/dependenciesutils.go +++ b/artifactory/utils/dependenciesutils.go @@ -79,7 +79,7 @@ func DownloadAnalyzerManagerIfNeeded() error { } } // Download & unzip the analyzer manager files - log.Info("The 'Analyzer Manager' app is not cached locally. Downloading it now...") + log.Debug("The 'Analyzer Manager' app is not cached locally. Downloading it now...") if err = DownloadDependency(artDetails, remotePath, filepath.Join(analyzerManagerDir, xrayutils.AnalyzerManagerZipName), true); err != nil { return err } diff --git a/utils/coreutils/techutils.go b/utils/coreutils/techutils.go index 8ea6bcf81..dcc6fbb44 100644 --- a/utils/coreutils/techutils.go +++ b/utils/coreutils/techutils.go @@ -38,6 +38,8 @@ type TechData struct { exclude []string // Whether this technology is supported by the 'jf ci-setup' command. ciSetupSupport bool + // Whether Contextual Analysis supported in this technology. + applicabilityScannable bool // The file that handles the project's dependencies. packageDescriptor string // Formal name of the technology @@ -52,15 +54,17 @@ type TechData struct { var technologiesData = map[Technology]TechData{ Maven: { - indicators: []string{"pom.xml"}, - ciSetupSupport: true, - packageDescriptor: "pom.xml", - execCommand: "mvn", + indicators: []string{"pom.xml"}, + ciSetupSupport: true, + packageDescriptor: "pom.xml", + execCommand: "mvn", + applicabilityScannable: true, }, Gradle: { - indicators: []string{".gradle", ".gradle.kts"}, - ciSetupSupport: true, - packageDescriptor: "build.gradle, build.gradle.kts", + indicators: []string{".gradle", ".gradle.kts"}, + ciSetupSupport: true, + packageDescriptor: "build.gradle, build.gradle.kts", + applicabilityScannable: true, }, Npm: { indicators: []string{"package.json", "package-lock.json", "npm-shrinkwrap.json"}, @@ -70,12 +74,14 @@ var technologiesData = map[Technology]TechData{ formal: string(Npm), packageVersionOperator: "@", packageInstallationCommand: "install", + applicabilityScannable: true, }, Yarn: { indicators: []string{".yarnrc.yml", "yarn.lock", ".yarn"}, packageDescriptor: "package.json", packageVersionOperator: "@", packageInstallationCommand: "up", + applicabilityScannable: true, }, Go: { indicators: []string{"go.mod"}, @@ -84,9 +90,10 @@ var technologiesData = map[Technology]TechData{ packageInstallationCommand: "get", }, Pip: { - packageType: Pypi, - indicators: []string{"setup.py", "requirements.txt"}, - exclude: []string{"Pipfile", "Pipfile.lock", "pyproject.toml", "poetry.lock"}, + packageType: Pypi, + indicators: []string{"setup.py", "requirements.txt"}, + exclude: []string{"Pipfile", "Pipfile.lock", "pyproject.toml", "poetry.lock"}, + applicabilityScannable: true, }, Pipenv: { packageType: Pypi, @@ -94,12 +101,14 @@ var technologiesData = map[Technology]TechData{ packageDescriptor: "Pipfile", packageVersionOperator: "==", packageInstallationCommand: "install", + applicabilityScannable: true, }, Poetry: { packageType: Pypi, indicators: []string{"pyproject.toml", "poetry.lock"}, packageInstallationCommand: "add", packageVersionOperator: "==", + applicabilityScannable: true, }, Nuget: { indicators: []string{".sln", ".csproj"}, @@ -155,6 +164,10 @@ func (tech Technology) GetPackageInstallOperator() string { return technologiesData[tech].packageInstallationCommand } +func (tech Technology) ApplicabilityScannable() bool { + return technologiesData[tech].applicabilityScannable +} + // DetectTechnologies tries to detect all technologies types according to the files in the given path. // 'isCiSetup' will limit the search of possible techs to Maven, Gradle, and npm. // 'recursive' will determine if the search will be limited to files in the root path or not. @@ -240,3 +253,12 @@ func GetAllTechnologiesList() (technologies []Technology) { } return } + +func ContainsApplicabilityScannableTech(technologies []Technology) bool { + for _, technology := range technologies { + if technology.ApplicabilityScannable() { + return true + } + } + return false +} diff --git a/utils/coreutils/techutils_test.go b/utils/coreutils/techutils_test.go index ebb636f06..943813177 100644 --- a/utils/coreutils/techutils_test.go +++ b/utils/coreutils/techutils_test.go @@ -30,3 +30,20 @@ func TestDetectTechnologiesByFilePaths(t *testing.T) { }) } } + +func TestContainsApplicabilityScannableTech(t *testing.T) { + tests := []struct { + name string + technologies []Technology + want bool + }{ + {name: "contains supported and unsupported techs", technologies: []Technology{Nuget, Go, Npm}, want: true}, + {name: "contains supported techs only", technologies: []Technology{Maven, Yarn, Npm}, want: true}, + {name: "contains unsupported techs only", technologies: []Technology{Dotnet, Nuget, Go}, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, ContainsApplicabilityScannableTech(tt.technologies)) + }) + } +} diff --git a/xray/audit/jas/applicabilitymanager.go b/xray/audit/jas/applicabilitymanager.go index d6d96c79b..87627686d 100644 --- a/xray/audit/jas/applicabilitymanager.go +++ b/xray/audit/jas/applicabilitymanager.go @@ -3,6 +3,7 @@ package jas import ( "errors" "fmt" + "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/utils" @@ -12,6 +13,7 @@ import ( "github.com/jfrog/jfrog-client-go/xray/services" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "github.com/owenrumney/go-sarif/v2/sarif" + "golang.org/x/exp/maps" "gopkg.in/yaml.v2" "os" "path/filepath" @@ -25,11 +27,6 @@ const ( applicabilityScanCommand = "ca" ) -var ( - technologiesEligibleForApplicabilityScan = []coreutils.Technology{coreutils.Npm, coreutils.Pip, - coreutils.Poetry, coreutils.Pipenv, coreutils.Pypi, coreutils.Maven, coreutils.Gradle, coreutils.Yarn} -) - // The getApplicabilityScanResults function runs the applicability scan flow, which includes the following steps: // Creating an ApplicabilityScanManager object. // Checking if the scanned project is eligible for applicability scan. @@ -40,7 +37,7 @@ var ( // bool: true if the user is entitled to the applicability scan, false otherwise. // error: An error object (if any). func getApplicabilityScanResults(results []services.ScanResponse, dependencyTrees []*xrayUtils.GraphNode, - serverDetails *config.ServerDetails, analyzerManager utils.AnalyzerManagerInterface) (map[string]string, bool, error) { + serverDetails *config.ServerDetails, scannedTechnologies []coreutils.Technology, analyzerManager utils.AnalyzerManagerInterface) (map[string]string, bool, error) { applicabilityScanManager, cleanupFunc, err := newApplicabilityScanManager(results, dependencyTrees, serverDetails, analyzerManager) if err != nil { return nil, false, fmt.Errorf(applicabilityScanFailureMessage, err.Error()) @@ -50,8 +47,8 @@ func getApplicabilityScanResults(results []services.ScanResponse, dependencyTree err = errors.Join(err, cleanupFunc()) } }() - if !applicabilityScanManager.eligibleForApplicabilityScan() { - log.Debug("The conditions for running the applicability scan are not met. Skipping...") + if !applicabilityScanManager.shouldRunApplicabilityScan(scannedTechnologies) { + log.Debug("The technologies that have been scanned are currently not supported for contextual analysis scanning, or we couldn't find any vulnerable direct dependencies. Skipping....") return nil, false, nil } if err = applicabilityScanManager.run(); err != nil { @@ -63,33 +60,9 @@ func getApplicabilityScanResults(results []services.ScanResponse, dependencyTree return applicabilityScanManager.applicabilityScanResults, true, nil } -// Applicability scan is relevant only to specific programming languages (the languages in this list: -// technologiesEligibleForApplicabilityScan). therefore, the applicability scan will not be performed on projects that -// do not contain those technologies. -// resultsIncludeEligibleTechnologies() runs over the xray scan results, and check if at least one of them is one of -// the techs on technologiesEligibleForApplicabilityScan. otherwise, the applicability scan will not be executed. -func resultsIncludeEligibleTechnologies(xrayVulnerabilities []services.Vulnerability, xrayViolations []services.Violation) bool { - for _, vuln := range xrayVulnerabilities { - for _, technology := range technologiesEligibleForApplicabilityScan { - if vuln.Technology == technology.ToString() { - return true - } - } - } - for _, violation := range xrayViolations { - for _, technology := range technologiesEligibleForApplicabilityScan { - if violation.Technology == technology.ToString() { - return true - } - } - } - return false -} - type ApplicabilityScanManager struct { applicabilityScanResults map[string]string - xrayVulnerabilities []services.Vulnerability - xrayViolations []services.Violation + directDependenciesCves *datastructures.Set[string] xrayResults []services.ScanResponse configFileName string resultsFileName string @@ -99,7 +72,7 @@ type ApplicabilityScanManager struct { func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, dependencyTrees []*xrayUtils.GraphNode, serverDetails *config.ServerDetails, analyzerManager utils.AnalyzerManagerInterface) (manager *ApplicabilityScanManager, cleanup func() error, err error) { - directDependencies := getDirectDependenciesList(dependencyTrees) + directDependencies := getDirectDependenciesSet(dependencyTrees) tempDir, err := fileutils.CreateTempDir() if err != nil { return @@ -107,10 +80,10 @@ func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, depend cleanup = func() error { return fileutils.RemoveTempDir(tempDir) } + directDependenciesCves := extractDirectDependenciesCvesFromScan(xrayScanResults, directDependencies) return &ApplicabilityScanManager{ applicabilityScanResults: map[string]string{}, - xrayVulnerabilities: extractXrayDirectVulnerabilities(xrayScanResults, directDependencies), - xrayViolations: extractXrayDirectViolations(xrayScanResults, directDependencies), + directDependenciesCves: directDependenciesCves, configFileName: filepath.Join(tempDir, "config.yaml"), resultsFileName: filepath.Join(tempDir, "results.sarif"), xrayResults: xrayScanResults, @@ -119,68 +92,50 @@ func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, depend }, cleanup, nil } -func (a *ApplicabilityScanManager) eligibleForApplicabilityScan() bool { - return resultsIncludeEligibleTechnologies(getXrayVulnerabilities(a.xrayResults), getXrayViolations(a.xrayResults)) -} - -// This function gets a liat of xray scan responses that contains direct and indirect violations, and returns only direct -// violation of the scanned project, ignoring indirect violations -func extractXrayDirectViolations(xrayScanResults []services.ScanResponse, directDependencies []string) []services.Violation { - xrayViolationsDirectDependency := []services.Violation{} - for _, violation := range getXrayViolations(xrayScanResults) { - for _, dep := range directDependencies { - if _, ok := violation.Components[dep]; ok { - xrayViolationsDirectDependency = append(xrayViolationsDirectDependency, violation) +// This function gets a list of xray scan responses that contain direct and indirect vulnerabilities and returns only direct +// vulnerabilities of the scanned project, ignoring indirect vulnerabilities +func extractDirectDependenciesCvesFromScan(xrayScanResults []services.ScanResponse, directDependencies *datastructures.Set[string]) *datastructures.Set[string] { + directsCves := datastructures.MakeSet[string]() + for _, scanResult := range xrayScanResults { + for _, vulnerability := range scanResult.Vulnerabilities { + if isDirectComponents(maps.Keys(vulnerability.Components), directDependencies) { + for _, cve := range vulnerability.Cves { + directsCves.Add(cve.Id) + } + } + } + for _, violation := range scanResult.Violations { + if isDirectComponents(maps.Keys(violation.Components), directDependencies) { + for _, cve := range violation.Cves { + directsCves.Add(cve.Id) + } } } } - return xrayViolationsDirectDependency + + return directsCves } -// This function gets a liat of xray scan responses that contains direct and indirect vulnerabilities, and returns only direct -// vulnerabilities of the scanned project, ignoring indirect vulnerabilities -func extractXrayDirectVulnerabilities(xrayScanResults []services.ScanResponse, directDependencies []string) []services.Vulnerability { - xrayVulnerabilitiesDirectDependency := []services.Vulnerability{} - for _, vulnerability := range getXrayVulnerabilities(xrayScanResults) { - for _, dep := range directDependencies { - if _, ok := vulnerability.Components[dep]; ok { - xrayVulnerabilitiesDirectDependency = append(xrayVulnerabilitiesDirectDependency, vulnerability) - } +func isDirectComponents(components []string, directDependencies *datastructures.Set[string]) bool { + for _, component := range components { + if directDependencies.Exists(component) { + return true } } - return xrayVulnerabilitiesDirectDependency + return false } -// This function gets the dependencies tress of the scanned project, and extract a list containing only directed -// dependencies node ids. -func getDirectDependenciesList(dependencyTrees []*xrayUtils.GraphNode) []string { - directDependencies := []string{} +// This function retrieves the dependency trees of the scanned project and extracts a set that contains only the direct dependencies. +func getDirectDependenciesSet(dependencyTrees []*xrayUtils.GraphNode) *datastructures.Set[string] { + directDependencies := datastructures.MakeSet[string]() for _, tree := range dependencyTrees { for _, node := range tree.Nodes { - directDependencies = append(directDependencies, node.Id) + directDependencies.Add(node.Id) } } return directDependencies } -// Gets xray scan response and returns only the vulnerabilities part of it -func getXrayVulnerabilities(xrayScanResults []services.ScanResponse) []services.Vulnerability { - xrayVulnerabilities := []services.Vulnerability{} - for _, result := range xrayScanResults { - xrayVulnerabilities = append(xrayVulnerabilities, result.Vulnerabilities...) - } - return xrayVulnerabilities -} - -// Gets xray scan response and returns only the violations part of it -func getXrayViolations(xrayScanResults []services.ScanResponse) []services.Violation { - xrayViolations := []services.Violation{} - for _, result := range xrayScanResults { - xrayViolations = append(xrayViolations, result.Violations...) - } - return xrayViolations -} - func (a *ApplicabilityScanManager) run() (err error) { defer func() { if deleteJasProcessFiles(a.configFileName, a.resultsFileName) != nil { @@ -188,9 +143,6 @@ func (a *ApplicabilityScanManager) run() (err error) { err = errors.Join(err, deleteFilesError) } }() - if !a.directDependenciesExist() { - return nil - } log.Info("Running applicability scanning for the identified vulnerable dependencies...") if err = a.createConfigFile(); err != nil { return @@ -202,7 +154,11 @@ func (a *ApplicabilityScanManager) run() (err error) { } func (a *ApplicabilityScanManager) directDependenciesExist() bool { - return len(createCveList(a.xrayVulnerabilities, a.xrayViolations)) > 0 + return a.directDependenciesCves.Size() > 0 +} + +func (a *ApplicabilityScanManager) shouldRunApplicabilityScan(technologies []coreutils.Technology) bool { + return a.directDependenciesExist() && coreutils.ContainsApplicabilityScannableTech(technologies) } type applicabilityScanConfig struct { @@ -223,7 +179,6 @@ func (a *ApplicabilityScanManager) createConfigFile() error { if err != nil { return err } - cveWhiteList := utils.RemoveDuplicateValues(createCveList(a.xrayVulnerabilities, a.xrayViolations)) configFileContent := applicabilityScanConfig{ Scans: []scanConfiguration{ { @@ -231,7 +186,7 @@ func (a *ApplicabilityScanManager) createConfigFile() error { Output: a.resultsFileName, Type: applicabilityScanType, GrepDisable: false, - CveWhitelist: cveWhiteList, + CveWhitelist: a.directDependenciesCves.ToSlice(), SkippedDirs: skippedDirs, }, }, @@ -263,7 +218,7 @@ func (a *ApplicabilityScanManager) setScanResults() error { fullVulnerabilitiesList = report.Runs[0].Results } - xrayCves := utils.RemoveDuplicateValues(createCveList(a.xrayVulnerabilities, a.xrayViolations)) + xrayCves := a.directDependenciesCves.ToSlice() for _, xrayCve := range xrayCves { a.applicabilityScanResults[xrayCve] = utils.ApplicabilityUndeterminedStringValue } @@ -279,27 +234,6 @@ func (a *ApplicabilityScanManager) setScanResults() error { return nil } -// This function iterate the direct vulnerabilities and violations of the scanned projects, and creates a string list -// of the CVEs ids. This list will be sent as input to analyzer manager. -func createCveList(xrayVulnerabilities []services.Vulnerability, xrayViolations []services.Violation) []string { - cveWhiteList := []string{} - for _, vulnerability := range xrayVulnerabilities { - for _, cve := range vulnerability.Cves { - if cve.Id != "" { - cveWhiteList = append(cveWhiteList, cve.Id) - } - } - } - for _, violation := range xrayViolations { - for _, cve := range violation.Cves { - if cve.Id != "" { - cveWhiteList = append(cveWhiteList, cve.Id) - } - } - } - return cveWhiteList -} - // Gets a result of one CVE from the scanner, and returns true if the CVE is applicable, false otherwise func isVulnerabilityApplicable(result *sarif.Result) bool { return !(result.Kind != nil && *result.Kind == "pass") diff --git a/xray/audit/jas/applicabilitymanager_test.go b/xray/audit/jas/applicabilitymanager_test.go index d7c06cbd6..46b93b8db 100644 --- a/xray/audit/jas/applicabilitymanager_test.go +++ b/xray/audit/jas/applicabilitymanager_test.go @@ -3,6 +3,7 @@ package jas import ( "errors" "fmt" + "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/xray/services" @@ -22,8 +23,7 @@ func TestNewApplicabilityScanManager_InputIsValid(t *testing.T) { assert.NotEmpty(t, applicabilityManager) assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) - assert.Equal(t, 1, len(applicabilityManager.xrayVulnerabilities)) - assert.Equal(t, 1, len(applicabilityManager.xrayViolations)) + assert.Equal(t, applicabilityManager.directDependenciesCves.Size(), 5) } func TestNewApplicabilityScanManager_DependencyTreeDoesntExist(t *testing.T) { @@ -35,11 +35,10 @@ func TestNewApplicabilityScanManager_DependencyTreeDoesntExist(t *testing.T) { assert.NotEmpty(t, applicabilityManager) assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) - assert.Empty(t, applicabilityManager.xrayVulnerabilities) - assert.Empty(t, applicabilityManager.xrayViolations) + assert.Equal(t, applicabilityManager.directDependenciesCves.Size(), 0) } -func TestNewApplicabilityScanManager_NoDirectDependenciesInTree(t *testing.T) { +func TestNewApplicabilityScanManager_NoDirectDependenciesInScan(t *testing.T) { // Arrange var noDirectDependenciesResults = []services.ScanResponse{ { @@ -48,14 +47,12 @@ func TestNewApplicabilityScanManager_NoDirectDependenciesInTree(t *testing.T) { {IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve1"}, {Id: "testCve2"}, {Id: "testCve3"}}, Components: map[string]services.Component{ - "issueId_1_direct_dependency": {}, "issueId_1_non_direct_dependency": {}}}, }, Violations: []services.Violation{ {IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve4"}, {Id: "testCve5"}}, Components: map[string]services.Component{ - "issueId_2_direct_dependency": {}, "issueId_2_non_direct_dependency": {}}}, }, }, @@ -72,13 +69,12 @@ func TestNewApplicabilityScanManager_NoDirectDependenciesInTree(t *testing.T) { assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) // Non-direct dependencies should not be added - assert.Equal(t, 1, len(applicabilityManager.xrayVulnerabilities)) - assert.Equal(t, 1, len(applicabilityManager.xrayViolations)) + assert.Equal(t, 0, applicabilityManager.directDependenciesCves.Size()) } func TestNewApplicabilityScanManager_MultipleDependencyTrees(t *testing.T) { // Arrange - multipleDependencyTrees := []*xrayUtils.GraphNode{fakeBasicDependencyGraph[0], fakeBasicDependencyGraph[0]} + multipleDependencyTrees := []*xrayUtils.GraphNode{multipleFakeBasicDependencyGraph[0], multipleFakeBasicDependencyGraph[1]} // Act applicabilityManager, _, err := newApplicabilityScanManager(fakeBasicXrayResults, multipleDependencyTrees, &fakeServerDetails, &analyzerManagerMock{}) @@ -88,8 +84,7 @@ func TestNewApplicabilityScanManager_MultipleDependencyTrees(t *testing.T) { assert.NotEmpty(t, applicabilityManager) assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) - assert.Equal(t, 2, len(applicabilityManager.xrayVulnerabilities)) - assert.Equal(t, 2, len(applicabilityManager.xrayViolations)) + assert.Equal(t, 5, applicabilityManager.directDependenciesCves.Size()) } func TestNewApplicabilityScanManager_ViolationsDontExistInResults(t *testing.T) { @@ -113,8 +108,7 @@ func TestNewApplicabilityScanManager_ViolationsDontExistInResults(t *testing.T) assert.NotEmpty(t, applicabilityManager) assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) - assert.Equal(t, 1, len(applicabilityManager.xrayVulnerabilities)) - assert.Empty(t, applicabilityManager.xrayViolations) + assert.Equal(t, 3, applicabilityManager.directDependenciesCves.Size()) } func TestNewApplicabilityScanManager_VulnerabilitiesDontExist(t *testing.T) { @@ -138,40 +132,16 @@ func TestNewApplicabilityScanManager_VulnerabilitiesDontExist(t *testing.T) { assert.NotEmpty(t, applicabilityManager) assert.NotEmpty(t, applicabilityManager.configFileName) assert.NotEmpty(t, applicabilityManager.resultsFileName) - assert.Equal(t, 1, len(applicabilityManager.xrayViolations)) - assert.Empty(t, applicabilityManager.xrayVulnerabilities) -} - -func TestApplicabilityScanManager_ShouldRun_AllConditionsMet(t *testing.T) { - // Arrange - analyzerManagerExecuter = &analyzerManagerMock{} - applicabilityManager, _, err := newApplicabilityScanManager(fakeBasicXrayResults, fakeBasicDependencyGraph, &fakeServerDetails, &analyzerManagerMock{}) - - // Act - eligible := applicabilityManager.eligibleForApplicabilityScan() - - // Assert - assert.NoError(t, err) - assert.True(t, eligible) + assert.Equal(t, 2, applicabilityManager.directDependenciesCves.Size()) } func TestApplicabilityScanManager_ShouldRun_TechnologiesNotEligibleForScan(t *testing.T) { - defer func() { - fakeBasicXrayResults[0].Vulnerabilities[0].Technology = coreutils.Pipenv.ToString() - fakeBasicXrayResults[0].Violations[0].Technology = coreutils.Pipenv.ToString() - }() - - // Arrange analyzerManagerExecuter = &analyzerManagerMock{} - fakeBasicXrayResults[0].Vulnerabilities[0].Technology = coreutils.Nuget.ToString() - fakeBasicXrayResults[0].Violations[0].Technology = coreutils.Go.ToString() - applicabilityManager, _, err := newApplicabilityScanManager(fakeBasicXrayResults, fakeBasicDependencyGraph, - &fakeServerDetails, &analyzerManagerMock{}) - - // Act - eligible := applicabilityManager.eligibleForApplicabilityScan() + applicabilityManager, eligible, err := getApplicabilityScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, + &fakeServerDetails, []coreutils.Technology{coreutils.Nuget, coreutils.Go}, &analyzerManagerMock{}) // Assert + assert.Nil(t, applicabilityManager) assert.NoError(t, err) assert.False(t, eligible) } @@ -180,40 +150,15 @@ func TestApplicabilityScanManager_ShouldRun_ScanResultsAreEmpty(t *testing.T) { // Arrange analyzerManagerExecuter = &analyzerManagerMock{} applicabilityManager, _, err := newApplicabilityScanManager(nil, fakeBasicDependencyGraph, &fakeServerDetails, &analyzerManagerMock{}) - - // Act - eligible := applicabilityManager.eligibleForApplicabilityScan() - - // Assert assert.NoError(t, err) + // Assert + eligible := applicabilityManager.shouldRunApplicabilityScan([]coreutils.Technology{coreutils.Npm}) assert.False(t, eligible) } -func TestResultsIncludeEligibleTechnologies(t *testing.T) { - tests := []struct { - vulnerabilities []services.Vulnerability - violations []services.Violation - expectedResult bool - }{ - {vulnerabilities: []services.Vulnerability{{Technology: "npm"}}, violations: []services.Violation{{Technology: "go"}}, expectedResult: true}, - {vulnerabilities: []services.Vulnerability{{Technology: "go"}}, violations: []services.Violation{{Technology: "npm"}}, expectedResult: true}, - {vulnerabilities: []services.Vulnerability{{Technology: "npm"}}, violations: []services.Violation{{Technology: "npm"}}, expectedResult: true}, - {vulnerabilities: []services.Vulnerability{{Technology: "go"}}, violations: []services.Violation{{Technology: "go"}}, expectedResult: false}, - } - for _, test := range tests { - assert.Equal(t, test.expectedResult, resultsIncludeEligibleTechnologies(test.vulnerabilities, test.violations)) - } -} - func TestExtractXrayDirectViolations(t *testing.T) { var xrayResponseForDirectViolationsTest = []services.ScanResponse{ { - ScanId: "scanId_1", - Vulnerabilities: []services.Vulnerability{ - {IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), - Cves: []services.Cve{{Id: "testCve1"}, {Id: "testCve2"}, {Id: "testCve3"}}, - Components: map[string]services.Component{"issueId_1_direct_dependency": {}}}, - }, Violations: []services.Violation{ {IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve4"}, {Id: "testCve5"}}, @@ -223,26 +168,27 @@ func TestExtractXrayDirectViolations(t *testing.T) { } tests := []struct { directDependencies []string - expectedResult []services.Violation + cvesCount int }{ {directDependencies: []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"}, - expectedResult: []services.Violation{ - {IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), - Cves: []services.Cve{{Id: "testCve4"}, {Id: "testCve5"}}, - Components: map[string]services.Component{"issueId_2_direct_dependency": {}}}, - }, + cvesCount: 2, }, // Vulnerability dependency, should be ignored by function {directDependencies: []string{"issueId_1_direct_dependency"}, - expectedResult: []services.Violation{}, + cvesCount: 0, }, {directDependencies: []string{}, - expectedResult: []services.Violation{}, + cvesCount: 0, }, } for _, test := range tests { - assert.Equal(t, test.expectedResult, extractXrayDirectViolations(xrayResponseForDirectViolationsTest, test.directDependencies)) + directDependenciesSet := datastructures.MakeSet[string]() + for _, direct := range test.directDependencies { + directDependenciesSet.Add(direct) + } + cves := extractDirectDependenciesCvesFromScan(xrayResponseForDirectViolationsTest, directDependenciesSet) + assert.Equal(t, test.cvesCount, cves.Size()) } } @@ -251,38 +197,42 @@ func TestExtractXrayDirectVulnerabilities(t *testing.T) { { ScanId: "scanId_1", Vulnerabilities: []services.Vulnerability{ - {IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), + { + IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve1"}, {Id: "testCve2"}, {Id: "testCve3"}}, - Components: map[string]services.Component{"issueId_1_direct_dependency": {}}}, - }, - Violations: []services.Violation{ - {IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), + Components: map[string]services.Component{"issueId_1_direct_dependency": {}}, + }, + { + IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve4"}, {Id: "testCve5"}}, - Components: map[string]services.Component{"issueId_2_direct_dependency": {}}}, + Components: map[string]services.Component{"issueId_2_direct_dependency": {}}, + }, }, }, } tests := []struct { directDependencies []string - expectedResult []services.Vulnerability + cvesCount int }{ - {directDependencies: []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"}, - expectedResult: []services.Vulnerability{ - {IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), - Cves: []services.Cve{{Id: "testCve1"}, {Id: "testCve2"}, {Id: "testCve3"}}, - Components: map[string]services.Component{"issueId_1_direct_dependency": {}}}, - }, + { + directDependencies: []string{"issueId_1_direct_dependency"}, + cvesCount: 3, }, - {directDependencies: []string{"issueId_2_direct_dependency"}, // violation dependency, should be ignored by function - expectedResult: []services.Vulnerability{}, + { + directDependencies: []string{"issueId_2_direct_dependency"}, + cvesCount: 2, }, {directDependencies: []string{}, - expectedResult: []services.Vulnerability{}, + cvesCount: 0, }, } for _, test := range tests { - assert.Equal(t, test.expectedResult, extractXrayDirectVulnerabilities(xrayResponseForDirectVulnerabilitiesTest, test.directDependencies)) + directDependenciesSet := datastructures.MakeSet[string]() + for _, direct := range test.directDependencies { + directDependenciesSet.Add(direct) + } + assert.Equal(t, test.cvesCount, extractDirectDependenciesCvesFromScan(xrayResponseForDirectVulnerabilitiesTest, directDependenciesSet).Size()) } } @@ -318,7 +268,8 @@ func TestGetDirectDependenciesList(t *testing.T) { } for _, test := range tests { - assert.ElementsMatch(t, test.expectedResult, getDirectDependenciesList(test.dependenciesTrees)) + result := getDirectDependenciesSet(test.dependenciesTrees) + assert.ElementsMatch(t, test.expectedResult, result.ToSlice()) } } @@ -411,7 +362,7 @@ func TestGetExtendedScanResults_AnalyzerManagerReturnsError(t *testing.T) { analyzerManagerExecuter = &analyzerManagerMock{} // Act - extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, &fakeServerDetails) + extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, &fakeServerDetails, []coreutils.Technology{coreutils.Npm}) // Assert assert.Error(t, err) diff --git a/xray/audit/jas/jasmanager.go b/xray/audit/jas/jasmanager.go index d24ca8beb..accbd5cd5 100644 --- a/xray/audit/jas/jasmanager.go +++ b/xray/audit/jas/jasmanager.go @@ -1,8 +1,8 @@ package jas import ( - "errors" "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" @@ -12,35 +12,27 @@ import ( "os" ) -const serverDetailsErrorMessage = "cant get xray server details" - var ( analyzerManagerExecuter utils.AnalyzerManagerInterface = &utils.AnalyzerManager{} skippedDirs = []string{"**/*test*/**", "**/*venv*/**", "**/*node_modules*/**", "**/*target*/**"} ) func GetExtendedScanResults(xrayResults []services.ScanResponse, dependencyTrees []*xrayUtils.GraphNode, - serverDetails *config.ServerDetails) (*utils.ExtendedScanResults, error) { - if serverDetails == nil { - return nil, errors.New(serverDetailsErrorMessage) - } - if len(serverDetails.Url) == 0 { - log.Warn("To include 'Contextual Analysis' information as part of the audit output, please run the 'jf c add' command before running this command.") - return &utils.ExtendedScanResults{XrayResults: xrayResults}, nil + serverDetails *config.ServerDetails, scannedTechnologies []coreutils.Technology) (*utils.ExtendedScanResults, error) { + if serverDetails == nil || len(serverDetails.Url) == 0 { + log.Warn("To include 'Advanced Security' scan as part of the audit output, please run the 'jf c add' command before running this command.") + return nil, nil } analyzerManagerExist, err := analyzerManagerExecuter.ExistLocally() if err != nil { - return nil, err + return &utils.ExtendedScanResults{XrayResults: xrayResults}, err } if !analyzerManagerExist { log.Debug("Since the 'Analyzer Manager' doesn't exist locally, its execution is skipped.") return &utils.ExtendedScanResults{XrayResults: xrayResults}, nil } - if err = utils.CreateAnalyzerManagerLogDir(); err != nil { - return nil, err - } applicabilityScanResults, eligibleForApplicabilityScan, err := getApplicabilityScanResults(xrayResults, - dependencyTrees, serverDetails, analyzerManagerExecuter) + dependencyTrees, serverDetails, scannedTechnologies, analyzerManagerExecuter) if err != nil { return nil, err } @@ -53,13 +45,14 @@ func GetExtendedScanResults(xrayResults []services.ScanResponse, dependencyTrees return nil, err } return &utils.ExtendedScanResults{ + EntitledForJas: true, XrayResults: xrayResults, + ScannedTechnologies: scannedTechnologies, ApplicabilityScanResults: applicabilityScanResults, - SecretsScanResults: secretsScanResults, - IacScanResults: iacScanResults, - EntitledForJas: true, EligibleForApplicabilityScan: eligibleForApplicabilityScan, + SecretsScanResults: secretsScanResults, EligibleForSecretScan: eligibleForSecretsScan, + IacScanResults: iacScanResults, EligibleForIacScan: eligibleForIacScan, }, nil } diff --git a/xray/audit/jas/jasmanager_test.go b/xray/audit/jas/jasmanager_test.go index 9fd56367b..812434465 100644 --- a/xray/audit/jas/jasmanager_test.go +++ b/xray/audit/jas/jasmanager_test.go @@ -31,12 +31,12 @@ var fakeBasicXrayResults = []services.ScanResponse{ Vulnerabilities: []services.Vulnerability{ {IssueId: "issueId_1", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve1"}, {Id: "testCve2"}, {Id: "testCve3"}}, - Components: map[string]services.Component{"issueId_1_direct_dependency": {}}}, + Components: map[string]services.Component{"issueId_1_direct_dependency": {}, "issueId_3_direct_dependency": {}}}, }, Violations: []services.Violation{ {IssueId: "issueId_2", Technology: coreutils.Pipenv.ToString(), Cves: []services.Cve{{Id: "testCve4"}, {Id: "testCve5"}}, - Components: map[string]services.Component{"issueId_2_direct_dependency": {}}}, + Components: map[string]services.Component{"issueId_2_direct_dependency": {}, "issueId_4_direct_dependency": {}}}, }, }, } @@ -51,6 +51,23 @@ var fakeBasicDependencyGraph = []*xrayUtils.GraphNode{ }, } +var multipleFakeBasicDependencyGraph = []*xrayUtils.GraphNode{ + { + Id: "parent_node_id", + Nodes: []*xrayUtils.GraphNode{ + {Id: "issueId_1_direct_dependency", Nodes: []*xrayUtils.GraphNode{{Id: "issueId_1_non_direct_dependency"}}}, + {Id: "issueId_2_direct_dependency", Nodes: nil}, + }, + }, + { + Id: "parent_node_id", + Nodes: []*xrayUtils.GraphNode{ + {Id: "issueId_3_direct_dependency", Nodes: []*xrayUtils.GraphNode{{Id: "issueId_2_non_direct_dependency"}}}, + {Id: "issueId_4_direct_dependency", Nodes: nil}, + }, + }, +} + var fakeServerDetails = config.ServerDetails{ Url: "platformUrl", Password: "password", @@ -63,7 +80,7 @@ func TestGetExtendedScanResults_AnalyzerManagerDoesntExist(t *testing.T) { analyzerManagerExecuter = &analyzerManagerMock{} // Act - extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, &fakeServerDetails) + extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, &fakeServerDetails, []coreutils.Technology{coreutils.Yarn}) // Assert assert.NoError(t, err) @@ -74,10 +91,9 @@ func TestGetExtendedScanResults_AnalyzerManagerDoesntExist(t *testing.T) { func TestGetExtendedScanResults_ServerNotValid(t *testing.T) { // Act - extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, nil) + extendedResults, err := GetExtendedScanResults(fakeBasicXrayResults, fakeBasicDependencyGraph, nil, []coreutils.Technology{coreutils.Pip}) // Assert assert.Nil(t, extendedResults) - assert.Error(t, err) - assert.Equal(t, "cant get xray server details", err.Error()) + assert.NoError(t, err) } diff --git a/xray/commands/audit/generic/auditmanager.go b/xray/commands/audit/generic/auditmanager.go index 2fe44e1d5..f817c5351 100644 --- a/xray/commands/audit/generic/auditmanager.go +++ b/xray/commands/audit/generic/auditmanager.go @@ -3,19 +3,14 @@ package audit import ( "errors" "fmt" + "github.com/jfrog/build-info-go/utils/pythonutils" "github.com/jfrog/gofrog/version" rtutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" - "github.com/jfrog/jfrog-cli-core/v2/xray/audit/jas" - "golang.org/x/sync/errgroup" - "os" - "path/filepath" - "strings" - - "github.com/jfrog/build-info-go/utils/pythonutils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/audit" _go "github.com/jfrog/jfrog-cli-core/v2/xray/audit/go" + "github.com/jfrog/jfrog-cli-core/v2/xray/audit/jas" "github.com/jfrog/jfrog-cli-core/v2/xray/audit/java" "github.com/jfrog/jfrog-cli-core/v2/xray/audit/npm" "github.com/jfrog/jfrog-cli-core/v2/xray/audit/nuget" @@ -28,6 +23,9 @@ import ( "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray/services" xrayCmdUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" + "golang.org/x/sync/errgroup" + "os" + "path/filepath" ) type Params struct { @@ -127,25 +125,18 @@ func RunAudit(auditParams *Params) (results *Results, err error) { } // The audit scan doesn't require the analyzer manager, so it can run separately from the analyzer manager download routine. - scanResults, isMultipleRootProject, auditError := genericAudit(auditParams) + results = genericAudit(auditParams) // Wait for the Download of the AnalyzerManager to complete. if err = errGroup.Wait(); err != nil { return } - extendedScanResults := &clientUtils.ExtendedScanResults{XrayResults: scanResults} // Run scanners only if the user is entitled for Advanced Security if isEntitled { - extendedScanResults, err = jas.GetExtendedScanResults(scanResults, auditParams.FullDependenciesTree(), serverDetails) - if err != nil { - return - } - } - results = &Results{ - IsMultipleRootProject: isMultipleRootProject, - AuditError: auditError, - ExtendedScanResults: extendedScanResults, + xrayScanResults := results.ExtendedScanResults.XrayResults + scannedTechnologies := results.ScannedTechnologies + results.ExtendedScanResults, err = jas.GetExtendedScanResults(xrayScanResults, auditParams.FullDependenciesTree(), serverDetails, scannedTechnologies) } return } @@ -165,9 +156,9 @@ func isEntitledForJas(serverDetails *config.ServerDetails) (entitled bool, xrayV } // genericAudit audits all the projects found in the given workingDirs -func genericAudit(params *Params) (results []services.ScanResponse, isMultipleRoot bool, err error) { - if err = coreutils.ValidateMinimumVersion(coreutils.Xray, params.xrayVersion, commandsutils.GraphScanMinXrayVersion); err != nil { - return +func genericAudit(params *Params) *Results { + if err := coreutils.ValidateMinimumVersion(coreutils.Xray, params.xrayVersion, commandsutils.GraphScanMinXrayVersion); err != nil { + return &Results{AuditError: err} } log.Info("JFrog Xray version is:", params.xrayVersion) log.Info("Scanning for vulnerable dependencies...") @@ -178,61 +169,61 @@ func genericAudit(params *Params) (results []services.ScanResponse, isMultipleRo return auditMultipleWorkingDirs(params) } -func auditMultipleWorkingDirs(params *Params) (results []services.ScanResponse, isMultipleRoot bool, err error) { +func auditMultipleWorkingDirs(params *Params) *Results { projectDir, err := os.Getwd() - if errorutils.CheckError(err) != nil { - return + if err != nil { + return &Results{AuditError: errorutils.CheckError(err)} } defer func() { err = errors.Join(err, os.Chdir(projectDir)) }() - var errorList strings.Builder + results := &Results{ExtendedScanResults: &clientUtils.ExtendedScanResults{}} for _, wd := range params.workingDirs { absWd, e := filepath.Abs(wd) if e != nil { - errorList.WriteString(fmt.Sprintf("the audit command couldn't find the following path: %s\n%s\n", wd, e.Error())) + err = errors.Join(err, fmt.Errorf("the audit command couldn't find the following path: %s\n%s\n", wd, e.Error())) continue } log.Info("Scanning directory:", absWd, "...") e = os.Chdir(absWd) if e != nil { - errorList.WriteString(fmt.Sprintf("the audit command couldn't change the current working directory to the following path: %s\n%s\n", absWd, e.Error())) + err = errors.Join(err, fmt.Errorf("the audit command couldn't change the current working directory to the following path: %s\n%s\n", absWd, e.Error())) continue } - techResults, isMultipleRootProject, e := doAudit(params) - if e != nil { - errorList.WriteString(fmt.Sprintf("audit command in %s failed:\n%s\n", absWd, e.Error())) + auditResults := doAudit(params) + if auditResults.AuditError != nil { + err = errors.Join(err, fmt.Errorf("audit command in %s failed:\n%s\n", absWd, auditResults.AuditError.Error())) continue } - results = append(results, techResults...) - isMultipleRoot = isMultipleRootProject - } - - if errorList.Len() > 0 { - err = errorutils.CheckErrorf(errorList.String()) + results.ExtendedScanResults.XrayResults = + append(results.ExtendedScanResults.XrayResults, auditResults.ExtendedScanResults.XrayResults...) + if !results.IsMultipleRootProject { + results.IsMultipleRootProject = auditResults.IsMultipleRootProject + } + results.ScannedTechnologies = append(results.ScannedTechnologies, auditResults.ScannedTechnologies...) } - - return + return results } // Audits the project found in the current directory using Xray. -func doAudit(params *Params) (results []services.ScanResponse, isMultipleRoot bool, err error) { +func doAudit(params *Params) *Results { // If no technologies were given, try to detect all types of technologies used. // Otherwise, run audit for requested technologies only. + var err error technologies := params.Technologies() if len(technologies) == 0 { technologies = commandsutils.DetectedTechnologies() if len(technologies) == 0 { log.Info("Skipping vulnerable dependencies scanning...") - return + return &Results{AuditError: err} } } - var errorList strings.Builder serverDetails, err := params.ServerDetails() + results := &Results{ExtendedScanResults: &clientUtils.ExtendedScanResults{}} if err != nil { - return + return &Results{AuditError: err} } for _, tech := range coreutils.ToTechnologies(technologies) { if tech == coreutils.Dotnet { @@ -240,7 +231,7 @@ func doAudit(params *Params) (results []services.ScanResponse, isMultipleRoot bo } flattenTree, e := GetTechDependencyTree(params.GraphBasicParams, tech) if e != nil { - errorList.WriteString(fmt.Sprintf("audit failed while building %s dependency tree:\n%s\n", tech, e.Error())) + err = errors.Join(err, fmt.Errorf("audit failed while building %s dependency tree:\n%s\n", tech, e.Error())) continue } @@ -252,17 +243,18 @@ func doAudit(params *Params) (results []services.ScanResponse, isMultipleRoot bo SetSeverityLevel(params.minSeverityFilter) techResults, e := audit.Audit(flattenTree, params.Progress(), tech, scanGraphParams) if e != nil { - errorList.WriteString(fmt.Sprintf("'%s' audit request failed:\n%s\n", tech, e.Error())) + err = errors.Join(err, fmt.Errorf("'%s' audit request failed:\n%s\n", tech, e.Error())) continue } techResults = audit.BuildImpactPathsForScanResponse(techResults, params.FullDependenciesTree()) - results = append(results, techResults...) - isMultipleRoot = len(flattenTree) > 1 - } - if errorList.Len() > 0 { - err = errorutils.CheckErrorf(errorList.String()) + results.ExtendedScanResults.XrayResults = append(results.ExtendedScanResults.XrayResults, techResults...) + if !results.IsMultipleRootProject { + results.IsMultipleRootProject = len(flattenTree) > 1 + } + results.ScannedTechnologies = append(results.ScannedTechnologies, tech) } - return + results.AuditError = err + return results } func GetTechDependencyTree(params *clientUtils.GraphBasicParams, tech coreutils.Technology) (flatTree []*xrayCmdUtils.GraphNode, err error) { diff --git a/xray/commands/audit/generic/generic.go b/xray/commands/audit/generic/generic.go index 88192aec6..6d42e554d 100644 --- a/xray/commands/audit/generic/generic.go +++ b/xray/commands/audit/generic/generic.go @@ -23,6 +23,7 @@ type Results struct { IsMultipleRootProject bool AuditError error ExtendedScanResults *xrutils.ExtendedScanResults + ScannedTechnologies []coreutils.Technology } func NewGenericAuditCommand() *GenericAuditCommand { diff --git a/xray/utils/analyzermanager.go b/xray/utils/analyzermanager.go index a511e5016..faea3ca3b 100644 --- a/xray/utils/analyzermanager.go +++ b/xray/utils/analyzermanager.go @@ -18,8 +18,7 @@ import ( ) var ( - analyzerManagerLogFolder = "" - levelToSeverity = map[string]string{"error": "High", "warning": "Medium", "info": "Low"} + levelToSeverity = map[string]string{"error": "High", "warning": "Medium", "info": "Low"} ) const ( @@ -57,6 +56,7 @@ type IacOrSecretResult struct { type ExtendedScanResults struct { XrayResults []services.ScanResponse + ScannedTechnologies []coreutils.Technology ApplicabilityScanResults map[string]string SecretsScanResults []IacOrSecretResult IacScanResults []IacOrSecretResult @@ -110,15 +110,6 @@ func (am *AnalyzerManager) Exec(configFile string, scanCommand string) (err erro return errorutils.CheckError(err) } -func CreateAnalyzerManagerLogDir() error { - logDir, err := coreutils.CreateDirInJfrogHome(filepath.Join(coreutils.JfrogLogsDirName, analyzerManagerLogDirName)) - if err != nil { - return err - } - analyzerManagerLogFolder = logDir - return nil -} - func GetAnalyzerManagerDownloadPath() (string, error) { osAndArc, err := coreutils.GetOSAndArc() if err != nil { @@ -167,7 +158,11 @@ func SetAnalyzerManagerEnvVariables(serverDetails *config.ServerDetails) error { if err := os.Setenv(jfTokenEnvVariable, serverDetails.AccessToken); errorutils.CheckError(err) != nil { return err } - if err := os.Setenv(logDirEnvVariable, analyzerManagerLogFolder); errorutils.CheckError(err) != nil { + analyzerManagerLogFolder, err := coreutils.CreateDirInJfrogHome(filepath.Join(coreutils.JfrogLogsDirName, analyzerManagerLogDirName)) + if err != nil { + return err + } + if err = os.Setenv(logDirEnvVariable, analyzerManagerLogFolder); errorutils.CheckError(err) != nil { return err } return nil diff --git a/xray/utils/resultstable.go b/xray/utils/resultstable.go index 39af61f15..fb50ab8a4 100644 --- a/xray/utils/resultstable.go +++ b/xray/utils/resultstable.go @@ -531,21 +531,21 @@ var Severities = map[string]map[string]*Severity{ "Critical": { ApplicableStringValue: {emoji: "💀", title: "Critical", numValue: 12, style: color.New(color.BgLightRed, color.LightWhite)}, ApplicabilityUndeterminedStringValue: {emoji: "💀", title: "Critical", numValue: 11, style: color.New(color.BgLightRed, color.LightWhite)}, - NotApplicableStringValue: {emoji: "👌", title: "Critical", numValue: 10}, + NotApplicableStringValue: {emoji: "👌", title: "Critical", numValue: 4}, }, "High": { - ApplicableStringValue: {emoji: "🔥", title: "High", numValue: 9, style: color.New(color.Red)}, - ApplicabilityUndeterminedStringValue: {emoji: "🔥", title: "High", numValue: 8, style: color.New(color.Red)}, - NotApplicableStringValue: {emoji: "👌", title: "High", numValue: 7}, + ApplicableStringValue: {emoji: "🔥", title: "High", numValue: 10, style: color.New(color.Red)}, + ApplicabilityUndeterminedStringValue: {emoji: "🔥", title: "High", numValue: 9, style: color.New(color.Red)}, + NotApplicableStringValue: {emoji: "👌", title: "High", numValue: 3}, }, "Medium": { - ApplicableStringValue: {emoji: "🎃", title: "Medium", numValue: 6, style: color.New(color.Yellow)}, - ApplicabilityUndeterminedStringValue: {emoji: "🎃", title: "Medium", numValue: 5, style: color.New(color.Yellow)}, - NotApplicableStringValue: {emoji: "👌", title: "Medium", numValue: 4}, + ApplicableStringValue: {emoji: "🎃", title: "Medium", numValue: 8, style: color.New(color.Yellow)}, + ApplicabilityUndeterminedStringValue: {emoji: "🎃", title: "Medium", numValue: 7, style: color.New(color.Yellow)}, + NotApplicableStringValue: {emoji: "👌", title: "Medium", numValue: 2}, }, "Low": { - ApplicableStringValue: {emoji: "👻", title: "Low", numValue: 3}, - ApplicabilityUndeterminedStringValue: {emoji: "👻", title: "Low", numValue: 2}, + ApplicableStringValue: {emoji: "👻", title: "Low", numValue: 6}, + ApplicabilityUndeterminedStringValue: {emoji: "👻", title: "Low", numValue: 5}, NotApplicableStringValue: {emoji: "👌", title: "Low", numValue: 1}, }, } @@ -805,7 +805,7 @@ func getUniqueKey(vulnerableDependency, vulnerableVersion string, cves []service // Else if at least one cve is undetermined - final value is undetermined // Else (case when all cves aren't applicable) -> final value is not applicable func getApplicableCveValue(extendedResults *ExtendedScanResults, xrayCves []formats.CveRow) string { - if !extendedResults.EntitledForJas { + if !extendedResults.EntitledForJas || len(extendedResults.ApplicabilityScanResults) == 0 { return "" } if len(xrayCves) == 0 {