From d86745699b3e8f635e0f6c566f8f0bc3e2ddaa10 Mon Sep 17 00:00:00 2001 From: Guy Sheffer <144031599+guyshe-jfrog@users.noreply.github.com> Date: Thu, 30 May 2024 16:50:20 +0300 Subject: [PATCH] Run contextual analysis and secret detection in Docker scans (#10) --- commands/audit/audit.go | 25 +++--- commands/audit/jasrunner.go | 63 -------------- commands/scan/dockerscan.go | 10 ++- commands/scan/scan.go | 83 ++++++++++++++++--- formats/conversion.go | 1 + formats/table.go | 3 +- .../applicability/applicabilitymanager.go | 23 +++-- .../applicabilitymanager_test.go | 20 ++--- {commands/audit/jas => jas}/common.go | 16 +++- {commands/audit/jas => jas}/common_test.go | 0 {commands/audit/jas => jas}/commons_test.go | 0 {commands/audit/jas => jas}/iac/iacscanner.go | 2 +- .../audit/jas => jas}/iac/iacscanner_test.go | 2 +- jas/runner/jasrunner.go | 69 +++++++++++++++ .../audit => jas/runner}/jasrunner_test.go | 12 +-- .../audit/jas => jas}/sast/sastscanner.go | 2 +- .../jas => jas}/sast/sastscanner_test.go | 2 +- .../jas => jas}/secrets/secretsscanner.go | 22 +++-- .../secrets/secretsscanner_test.go | 14 ++-- .../testdata/.jfrog/jfrog-apps-config.yml | 0 scans_test.go | 45 ++++++++++ 21 files changed, 276 insertions(+), 138 deletions(-) delete mode 100644 commands/audit/jasrunner.go rename {commands/audit/jas => jas}/applicability/applicabilitymanager.go (89%) rename {commands/audit/jas => jas}/applicability/applicabilitymanager_test.go (93%) rename {commands/audit/jas => jas}/common.go (95%) rename {commands/audit/jas => jas}/common_test.go (100%) rename {commands/audit/jas => jas}/commons_test.go (100%) rename {commands/audit/jas => jas}/iac/iacscanner.go (98%) rename {commands/audit/jas => jas}/iac/iacscanner_test.go (97%) create mode 100644 jas/runner/jasrunner.go rename {commands/audit => jas/runner}/jasrunner_test.go (56%) rename {commands/audit/jas => jas}/sast/sastscanner.go (98%) rename {commands/audit/jas => jas}/sast/sastscanner_test.go (99%) rename {commands/audit/jas => jas}/secrets/secretsscanner.go (82%) rename {commands/audit/jas => jas}/secrets/secretsscanner_test.go (89%) rename {commands/audit/jas => jas}/testdata/.jfrog/jfrog-apps-config.yml (100%) diff --git a/commands/audit/audit.go b/commands/audit/audit.go index a9e05bd6..28073963 100644 --- a/commands/audit/audit.go +++ b/commands/audit/audit.go @@ -3,16 +3,22 @@ package audit import ( "errors" "fmt" + "os" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-security/scangraph" - "github.com/jfrog/jfrog-cli-security/utils" + + "github.com/jfrog/jfrog-cli-security/jas" + + "github.com/jfrog/jfrog-cli-security/jas/applicability" + "github.com/jfrog/jfrog-cli-security/jas/runner" + "github.com/jfrog/jfrog-cli-security/jas/secrets" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray" "github.com/jfrog/jfrog-client-go/xray/services" xscservices "github.com/jfrog/jfrog-client-go/xsc/services" "golang.org/x/sync/errgroup" - "os" xrayutils "github.com/jfrog/jfrog-cli-security/utils" ) @@ -182,7 +188,7 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error) return } results.XrayVersion = auditParams.xrayVersion - results.ExtendedScanResults.EntitledForJas, err = isEntitledForJas(xrayManager, auditParams.xrayVersion) + results.ExtendedScanResults.EntitledForJas, err = jas.IsEntitledForJas(xrayManager, auditParams.xrayVersion) if err != nil { return } @@ -190,7 +196,7 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error) errGroup := new(errgroup.Group) if results.ExtendedScanResults.EntitledForJas { // Download (if needed) the analyzer manager in a background routine. - errGroup.Go(utils.DownloadAnalyzerManagerIfNeeded) + errGroup.Go(xrayutils.DownloadAnalyzerManagerIfNeeded) } results.MultiScanId = auditParams.XrayGraphScanParams().MultiScanId @@ -205,16 +211,7 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error) // Run scanners only if the user is entitled for Advanced Security if results.ExtendedScanResults.EntitledForJas { - results.JasError = runJasScannersAndSetResults(results, auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.thirdPartyApplicabilityScan, auditParams.XrayGraphScanParams().MultiScanId) - } - return -} - -func isEntitledForJas(xrayManager *xray.XrayServicesManager, xrayVersion string) (entitled bool, err error) { - if e := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, xrayutils.EntitlementsMinVersion); e != nil { - log.Debug(e) - return + results.JasError = runner.RunJasScannersAndSetResults(results.ExtendedScanResults, results.GetScaScannedTechnologies(), results.GetScaScansXrayResults(), auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.thirdPartyApplicabilityScan, auditParams.XrayGraphScanParams().MultiScanId, applicability.ApplicabilityScannerType, secrets.SecretsScannerType) } - entitled, err = xrayManager.IsEntitled(xrayutils.ApplicabilityFeatureId) return } diff --git a/commands/audit/jasrunner.go b/commands/audit/jasrunner.go deleted file mode 100644 index 03398730..00000000 --- a/commands/audit/jasrunner.go +++ /dev/null @@ -1,63 +0,0 @@ -package audit - -import ( - "errors" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas/applicability" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas/iac" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas/sast" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas/secrets" - "github.com/jfrog/jfrog-cli-security/utils" - "github.com/jfrog/jfrog-client-go/utils/io" - "github.com/jfrog/jfrog-client-go/utils/log" -) - -func runJasScannersAndSetResults(scanResults *utils.Results, directDependencies []string, - serverDetails *config.ServerDetails, workingDirs []string, progress io.ProgressMgr, thirdPartyApplicabilityScan bool, msi string) (err 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 - } - scanner, err := jas.NewJasScanner(workingDirs, serverDetails) - if err != nil { - return - } - defer func() { - cleanup := scanner.ScannerDirCleanupFunc - err = errors.Join(err, cleanup()) - }() - if progress != nil { - progress.SetHeadlineMsg("Running applicability scanning") - } - // Set environments variables for analytics in analyzers manager. - callback := jas.SetAnalyticsMetricsDataForAnalyzerManager(msi, scanResults.GetScaScannedTechnologies()) - defer callback() - scanResults.ExtendedScanResults.ApplicabilityScanResults, err = applicability.RunApplicabilityScan(scanResults.GetScaScansXrayResults(), directDependencies, scanResults.GetScaScannedTechnologies(), scanner, thirdPartyApplicabilityScan) - if err != nil { - return - } - // Don't execute other scanners when scanning third party dependencies. - if thirdPartyApplicabilityScan { - return - } - if progress != nil { - progress.SetHeadlineMsg("Running secrets scanning") - } - scanResults.ExtendedScanResults.SecretsScanResults, err = secrets.RunSecretsScan(scanner) - if err != nil { - return - } - if progress != nil { - progress.SetHeadlineMsg("Running IaC scanning") - } - scanResults.ExtendedScanResults.IacScanResults, err = iac.RunIacScan(scanner) - if err != nil { - return - } - if progress != nil { - progress.SetHeadlineMsg("Running SAST scanning") - } - scanResults.ExtendedScanResults.SastScanResults, err = sast.RunSastScan(scanner) - return -} diff --git a/commands/scan/dockerscan.go b/commands/scan/dockerscan.go index 8e954792..ba8d7b45 100644 --- a/commands/scan/dockerscan.go +++ b/commands/scan/dockerscan.go @@ -3,16 +3,17 @@ package scan import ( "bytes" "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "github.com/jfrog/jfrog-cli-core/v2/common/spec" xrayutils "github.com/jfrog/jfrog-cli-security/utils" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" - "os" - "os/exec" - "path/filepath" - "strings" ) const ( @@ -81,6 +82,7 @@ func (dsc *DockerScanCommand) Run() (err error) { Pattern(imageTarPath). Target(dsc.targetRepoPath). BuildSpec()).SetThreads(1) + dsc.ScanCommand.SetRunJasScans(true) err = dsc.setCredentialEnvsForIndexerApp() if err != nil { return errorutils.CheckError(err) diff --git a/commands/scan/scan.go b/commands/scan/scan.go index c707fd22..bdf37a38 100644 --- a/commands/scan/scan.go +++ b/commands/scan/scan.go @@ -10,12 +10,19 @@ import ( "regexp" "strings" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" + + "github.com/jfrog/jfrog-cli-security/jas" + "github.com/jfrog/jfrog-cli-security/jas/applicability" + "github.com/jfrog/jfrog-cli-security/jas/runner" + "github.com/jfrog/jfrog-cli-security/jas/secrets" "github.com/jfrog/jfrog-cli-security/scangraph" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" + "golang.org/x/sync/errgroup" "github.com/jfrog/gofrog/parallel" "github.com/jfrog/jfrog-cli-core/v2/common/format" - outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format" "github.com/jfrog/jfrog-cli-core/v2/common/spec" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" @@ -35,8 +42,9 @@ type FileContext func(string) parallel.TaskFunc type indexFileHandlerFunc func(file string) type ScanInfo struct { - Target string - Result *services.ScanResponse + Target string + Result *services.ScanResponse + ExtendedScanResults *utils.ExtendedScanResults } const ( @@ -52,7 +60,7 @@ type ScanCommand struct { // The location of the downloaded Xray indexer binary on the local file system. indexerPath string indexerTempDir string - outputFormat outputFormat.OutputFormat + outputFormat format.OutputFormat projectKey string minSeverityFilter string watches []string @@ -63,6 +71,7 @@ type ScanCommand struct { bypassArchiveLimits bool fixableOnly bool progress ioUtils.ProgressMgr + commandSupportsJAS bool } func (scanCmd *ScanCommand) SetMinSeverityFilter(minSeverityFilter string) *ScanCommand { @@ -75,6 +84,11 @@ func (scanCmd *ScanCommand) SetFixableOnly(fixable bool) *ScanCommand { return scanCmd } +func (scanCmd *ScanCommand) SetRunJasScans(run bool) *ScanCommand { + scanCmd.commandSupportsJAS = run + return scanCmd +} + func (scanCmd *ScanCommand) SetProgress(progress ioUtils.ProgressMgr) { scanCmd.progress = progress } @@ -84,7 +98,7 @@ func (scanCmd *ScanCommand) SetThreads(threads int) *ScanCommand { return scanCmd } -func (scanCmd *ScanCommand) SetOutputFormat(format outputFormat.OutputFormat) *ScanCommand { +func (scanCmd *ScanCommand) SetOutputFormat(format format.OutputFormat) *ScanCommand { scanCmd.outputFormat = format return scanCmd } @@ -182,6 +196,16 @@ func (scanCmd *ScanCommand) Run() (err error) { return err } + scanResults := xrutils.NewAuditResults() + scanResults.XrayVersion = xrayVersion + + scanResults.ExtendedScanResults.EntitledForJas, err = jas.IsEntitledForJas(xrayManager, xrayVersion) + errGroup := new(errgroup.Group) + if scanResults.ExtendedScanResults.EntitledForJas { + // Download (if needed) the analyzer manager in a background routine. + errGroup.Go(xrutils.DownloadAnalyzerManagerIfNeeded) + } + // Validate Xray minimum version for graph scan command err = clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, scangraph.GraphScanMinXrayVersion) if err != nil { @@ -226,16 +250,20 @@ func (scanCmd *ScanCommand) Run() (err error) { fileCollectingErrorsQueue := clientutils.NewErrorsQueue(1) // Start walking on the filesystem to "produce" files that match the given pattern // while the consumer uses the indexer to index those files. - scanCmd.prepareScanTasks(fileProducerConsumer, indexedFileProducerConsumer, resultsArr, fileProducerErrors, indexedFileProducerErrors, fileCollectingErrorsQueue, xrayVersion) + scanCmd.prepareScanTasks(fileProducerConsumer, indexedFileProducerConsumer, scanResults.ExtendedScanResults.EntitledForJas, resultsArr, fileProducerErrors, indexedFileProducerErrors, fileCollectingErrorsQueue, xrayVersion) scanCmd.performScanTasks(fileProducerConsumer, indexedFileProducerConsumer) // Handle results flatResults := []xrutils.ScaScanResult{} + for _, arr := range resultsArr { for _, res := range arr { flatResults = append(flatResults, xrutils.ScaScanResult{Target: res.Target, XrayResults: []services.ScanResponse{*res.Result}}) + scanResults.ExtendedScanResults.ApplicabilityScanResults = append(scanResults.ExtendedScanResults.ApplicabilityScanResults, res.ExtendedScanResults.ApplicabilityScanResults...) + scanResults.ExtendedScanResults.SecretsScanResults = append(scanResults.ExtendedScanResults.SecretsScanResults, res.ExtendedScanResults.SecretsScanResults...) } } + if scanCmd.progress != nil { if err = scanCmd.progress.Quit(); err != nil { return err @@ -251,10 +279,13 @@ func (scanCmd *ScanCommand) Run() (err error) { scanErrors = appendErrorSlice(scanErrors, fileProducerErrors) scanErrors = appendErrorSlice(scanErrors, indexedFileProducerErrors) - scanResults := xrutils.NewAuditResults() - scanResults.XrayVersion = xrayVersion scanResults.ScaResults = flatResults + // Wait for the Download of the AnalyzerManager to complete. + if err = errGroup.Wait(); err != nil { + err = errors.New("failed while trying to get Analyzer Manager: " + err.Error()) + } + if err = xrutils.NewResultsWriter(scanResults). SetOutputFormat(scanCmd.outputFormat). SetIncludeVulnerabilities(scanCmd.includeVulnerabilities). @@ -296,14 +327,14 @@ func (scanCmd *ScanCommand) CommandName() string { return "xr_scan" } -func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, fileCollectingErrorsQueue *clientutils.ErrorsQueue, xrayVersion string) { +func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, entitledForJas bool, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, fileCollectingErrorsQueue *clientutils.ErrorsQueue, xrayVersion string) { go func() { defer fileProducer.Done() // Iterate over file-spec groups and produce indexing tasks. // When encountering an error, log and move to next group. specFiles := scanCmd.spec.Files for i := range specFiles { - artifactHandlerFunc := scanCmd.createIndexerHandlerFunc(&specFiles[i], indexedFileProducer, resultsArr, fileErrors, indexedFileErrors, xrayVersion) + artifactHandlerFunc := scanCmd.createIndexerHandlerFunc(&specFiles[i], entitledForJas, indexedFileProducer, resultsArr, fileErrors, indexedFileErrors, xrayVersion) taskHandler := getAddTaskToProducerFunc(fileProducer, artifactHandlerFunc) err := collectFilesForIndexing(specFiles[i], taskHandler) @@ -315,7 +346,7 @@ func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer p }() } -func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, xrayVersion string) FileContext { +func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, entitledForJas bool, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, xrayVersion string) FileContext { return func(filePath string) parallel.TaskFunc { return func(threadId int) (err error) { logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, false) @@ -360,12 +391,26 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFil return err } scanResults, err := scangraph.RunScanGraphAndGetResults(scanGraphParams, xrayManager) + if err != nil { log.Error(fmt.Sprintf("scanning '%s' failed with error: %s", graph.Id, err.Error())) indexedFileErrors[threadId] = append(indexedFileErrors[threadId], formats.SimpleJsonError{FilePath: filePath, ErrorMessage: err.Error()}) return } - resultsArr[threadId] = append(resultsArr[threadId], &ScanInfo{Target: filePath, Result: scanResults}) + + extendedScanResults := utils.ExtendedScanResults{} + if entitledForJas && scanCmd.commandSupportsJAS { + // Run Jas scans + workingDirs := []string{filePath} + err = runner.RunJasScannersAndSetResults(&extendedScanResults, []coreutils.Technology{coreutils.Technology(scanResults.ScannedPackageType)}, []services.ScanResponse{*scanResults}, depsListFromVulnerabilities(*scanResults), scanCmd.serverDetails, workingDirs, nil, false, "", applicability.ApplicabilityDockerScanScanType, secrets.SecretsScannerDockerScanType) + + if err != nil { + log.Error(fmt.Sprintf("scanning '%s' failed with error: %s", graph.Id, err.Error())) + indexedFileErrors[threadId] = append(indexedFileErrors[threadId], formats.SimpleJsonError{FilePath: filePath, ErrorMessage: err.Error()}) + return + } + } + resultsArr[threadId] = append(resultsArr[threadId], &ScanInfo{Target: filePath, Result: scanResults, ExtendedScanResults: &extendedScanResults}) return } @@ -469,6 +514,20 @@ func appendErrorSlice(scanErrors []formats.SimpleJsonError, errorsToAdd [][]form return scanErrors } +func depsListFromVulnerabilities(scanResult ...services.ScanResponse) (depsList []string) { + for _, result := range scanResult { + for _, vulnerability := range result.Vulnerabilities { + dependencies := maps.Keys(vulnerability.Components) + for _, dependency := range dependencies { + if !slices.Contains(depsList, dependency) { + depsList = append(depsList, dependency) + } + } + } + } + return +} + func ConditionalUploadDefaultScanFunc(serverDetails *config.ServerDetails, fileSpec *spec.SpecFiles, threads int, scanOutputFormat format.OutputFormat) error { return NewScanCommand().SetServerDetails(serverDetails).SetSpec(fileSpec).SetThreads(threads).SetOutputFormat(scanOutputFormat).SetFail(true).SetPrintExtendedTable(false).Run() } diff --git a/formats/conversion.go b/formats/conversion.go index 1a360ec1..8a602869 100644 --- a/formats/conversion.go +++ b/formats/conversion.go @@ -28,6 +28,7 @@ func ConvertToVulnerabilityScanTableRow(rows []VulnerabilityOrViolationRow) (tab tableRows = append(tableRows, vulnerabilityScanTableRow{ severity: rows[i].Severity, severityNumValue: rows[i].SeverityNumValue, + applicable: rows[i].Applicable, impactedPackageName: rows[i].ImpactedDependencyName, impactedPackageVersion: rows[i].ImpactedDependencyVersion, ImpactedPackageType: rows[i].ImpactedDependencyType, diff --git a/formats/table.go b/formats/table.go index 8dd71d1d..fc9486d9 100644 --- a/formats/table.go +++ b/formats/table.go @@ -20,7 +20,8 @@ type vulnerabilityTableRow struct { } type vulnerabilityScanTableRow struct { - severity string `col-name:"Severity"` + severity string `col-name:"Severity"` + applicable string `col-name:"Contextual\nAnalysis" omitempty:"true"` // For sorting severityNumValue int directPackages []directPackagesTableRow `embed-table:"true"` diff --git a/commands/audit/jas/applicability/applicabilitymanager.go b/jas/applicability/applicabilitymanager.go similarity index 89% rename from commands/audit/jas/applicability/applicabilitymanager.go rename to jas/applicability/applicabilitymanager.go index 15f0f2fe..a94429d1 100644 --- a/commands/audit/jas/applicability/applicabilitymanager.go +++ b/jas/applicability/applicabilitymanager.go @@ -4,7 +4,7 @@ import ( "path/filepath" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" @@ -17,11 +17,15 @@ import ( ) const ( - applicabilityScanType = "analyze-applicability" - applicabilityScanCommand = "ca" - applicabilityDocsUrlSuffix = "contextual-analysis" + applicabilityScanCommand = "ca" + applicabilityDocsUrlSuffix = "contextual-analysis" + + ApplicabilityScannerType ApplicabilityScanType = "analyze-applicability" + ApplicabilityDockerScanScanType ApplicabilityScanType = "analyze-applicability-docker-scan" ) +type ApplicabilityScanType string + type ApplicabilityScanManager struct { applicabilityScanResults []*sarif.Run directDependenciesCves []string @@ -29,6 +33,7 @@ type ApplicabilityScanManager struct { xrayResults []services.ScanResponse scanner *jas.JasScanner thirdPartyScan bool + commandType string } // The getApplicabilityScanResults function runs the applicability scan flow, which includes the following steps: @@ -41,8 +46,8 @@ type ApplicabilityScanManager struct { // bool: true if the user is entitled to the applicability scan, false otherwise. // error: An error object (if any). func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencies []string, - scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner, thirdPartyContextualAnalysis bool) (results []*sarif.Run, err error) { - applicabilityScanManager := newApplicabilityScanManager(xrayResults, directDependencies, scanner, thirdPartyContextualAnalysis) + scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner, thirdPartyContextualAnalysis bool, scanType ApplicabilityScanType) (results []*sarif.Run, err error) { + applicabilityScanManager := newApplicabilityScanManager(xrayResults, directDependencies, scanner, thirdPartyContextualAnalysis, scanType) if !applicabilityScanManager.cvesExists() { log.Debug("We couldn't find any vulnerable dependencies. Skipping....") return @@ -55,7 +60,7 @@ func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencie return } -func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner, thirdPartyScan bool) (manager *ApplicabilityScanManager) { +func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner, thirdPartyScan bool, scanType ApplicabilityScanType) (manager *ApplicabilityScanManager) { directDependenciesCves, indirectDependenciesCves := extractDependenciesCvesFromScan(xrayScanResults, directDependencies) return &ApplicabilityScanManager{ applicabilityScanResults: []*sarif.Run{}, @@ -64,6 +69,7 @@ func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, direct xrayResults: xrayScanResults, scanner: scanner, thirdPartyScan: thirdPartyScan, + commandType: string(scanType), } } @@ -148,6 +154,7 @@ type scanConfiguration struct { CveWhitelist []string `yaml:"cve-whitelist"` IndirectCveWhitelist []string `yaml:"indirect-cve-whitelist"` SkippedDirs []string `yaml:"skipped-folders"` + ScanType string `yaml:"scantype"` } func (asm *ApplicabilityScanManager) createConfigFile(module jfrogappsconfig.Module) error { @@ -165,7 +172,7 @@ func (asm *ApplicabilityScanManager) createConfigFile(module jfrogappsconfig.Mod { Roots: roots, Output: asm.scanner.ResultsFileName, - Type: applicabilityScanType, + Type: asm.commandType, GrepDisable: false, CveWhitelist: asm.directDependenciesCves, IndirectCveWhitelist: asm.indirectDependenciesCves, diff --git a/commands/audit/jas/applicability/applicabilitymanager_test.go b/jas/applicability/applicabilitymanager_test.go similarity index 93% rename from commands/audit/jas/applicability/applicabilitymanager_test.go rename to jas/applicability/applicabilitymanager_test.go index 86b8a49a..10a91a76 100644 --- a/commands/audit/jas/applicability/applicabilitymanager_test.go +++ b/jas/applicability/applicabilitymanager_test.go @@ -7,7 +7,7 @@ import ( jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-client-go/xray/services" "github.com/stretchr/testify/assert" ) @@ -19,7 +19,7 @@ func TestNewApplicabilityScanManager_InputIsValid(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false, ApplicabilityScannerType) // Assert if assert.NotNil(t, applicabilityManager) { @@ -33,7 +33,7 @@ func TestNewApplicabilityScanManager_DependencyTreeDoesntExist(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, nil, scanner, false) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, nil, scanner, false, ApplicabilityScannerType) // Assert if assert.NotNil(t, applicabilityManager) { @@ -70,10 +70,10 @@ func TestNewApplicabilityScanManager_NoDirectDependenciesInScan(t *testing.T) { // Act scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, false, ApplicabilityScannerType) assertApplicabilityScanner(t, applicabilityManager) // ThirdPartyContextual shouldn't change anything here as this is not npm. - applicabilityManager = newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, true) + applicabilityManager = newApplicabilityScanManager(noDirectDependenciesResults, mockDirectDependencies, scanner, true, ApplicabilityScannerType) assertApplicabilityScanner(t, applicabilityManager) } @@ -91,7 +91,7 @@ func TestNewApplicabilityScanManager_MultipleDependencyTrees(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockMultiRootDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockMultiRootDirectDependencies, scanner, false, ApplicabilityScannerType) // Assert if assert.NotNil(t, applicabilityManager) { @@ -117,7 +117,7 @@ func TestNewApplicabilityScanManager_ViolationsDontExistInResults(t *testing.T) defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(noViolationScanResponse, mockDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(noViolationScanResponse, mockDirectDependencies, scanner, false, ApplicabilityScannerType) // Assert if assert.NotNil(t, applicabilityManager) { @@ -143,7 +143,7 @@ func TestNewApplicabilityScanManager_VulnerabilitiesDontExist(t *testing.T) { defer cleanUp() // Act - applicabilityManager := newApplicabilityScanManager(noVulnerabilitiesScanResponse, mockDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(noVulnerabilitiesScanResponse, mockDirectDependencies, scanner, false, ApplicabilityScannerType) // Assert if assert.NotNil(t, applicabilityManager) { @@ -241,7 +241,7 @@ func TestCreateConfigFile_VerifyFileWasCreated(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, scanner, false) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, scanner, false, ApplicabilityScannerType) currWd, err := coreutils.GetWorkingDirectory() assert.NoError(t, err) @@ -294,7 +294,7 @@ func TestParseResults_NewApplicabilityStatuses(t *testing.T) { // Arrange scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false) + applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false, ApplicabilityScannerType) // Act for _, tc := range testCases { diff --git a/commands/audit/jas/common.go b/jas/common.go similarity index 95% rename from commands/audit/jas/common.go rename to jas/common.go index bf66b271..89d5af72 100644 --- a/commands/audit/jas/common.go +++ b/jas/common.go @@ -3,20 +3,23 @@ package jas import ( "errors" "fmt" - clientutils "github.com/jfrog/jfrog-client-go/utils" "os" "path/filepath" "strings" "testing" "unicode" + clientutils "github.com/jfrog/jfrog-client-go/utils" + jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-security/utils" + goclientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/jfrog/jfrog-client-go/xray" "github.com/jfrog/jfrog-client-go/xray/services" "github.com/owenrumney/go-sarif/v2/sarif" "github.com/stretchr/testify/assert" @@ -238,7 +241,7 @@ func InitJasTest(t *testing.T, workingDirs ...string) (*JasScanner, func()) { } func GetTestDataPath() string { - return filepath.Join("..", "..", "..", "..", "tests", "testdata", "other") + return filepath.Join("..", "..", "tests", "testdata", "other") } func ShouldSkipScanner(module jfrogappsconfig.Module, scanType utils.JasScanType) bool { @@ -315,3 +318,12 @@ func SetAnalyticsMetricsDataForAnalyzerManager(msi string, technologies []coreut } } } + +func IsEntitledForJas(xrayManager *xray.XrayServicesManager, xrayVersion string) (entitled bool, err error) { + if e := goclientutils.ValidateMinimumVersion(goclientutils.Xray, xrayVersion, utils.EntitlementsMinVersion); e != nil { + log.Debug(e) + return + } + entitled, err = xrayManager.IsEntitled(utils.ApplicabilityFeatureId) + return +} diff --git a/commands/audit/jas/common_test.go b/jas/common_test.go similarity index 100% rename from commands/audit/jas/common_test.go rename to jas/common_test.go diff --git a/commands/audit/jas/commons_test.go b/jas/commons_test.go similarity index 100% rename from commands/audit/jas/commons_test.go rename to jas/commons_test.go diff --git a/commands/audit/jas/iac/iacscanner.go b/jas/iac/iacscanner.go similarity index 98% rename from commands/audit/jas/iac/iacscanner.go rename to jas/iac/iacscanner.go index 312dcf4f..99b7eb01 100644 --- a/commands/audit/jas/iac/iacscanner.go +++ b/jas/iac/iacscanner.go @@ -4,7 +4,7 @@ import ( "path/filepath" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-client-go/utils/log" diff --git a/commands/audit/jas/iac/iacscanner_test.go b/jas/iac/iacscanner_test.go similarity index 97% rename from commands/audit/jas/iac/iacscanner_test.go rename to jas/iac/iacscanner_test.go index 1a5403f4..5d7bab41 100644 --- a/commands/audit/jas/iac/iacscanner_test.go +++ b/jas/iac/iacscanner_test.go @@ -6,7 +6,7 @@ import ( "testing" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/stretchr/testify/assert" diff --git a/jas/runner/jasrunner.go b/jas/runner/jasrunner.go new file mode 100644 index 00000000..c6a8bd7d --- /dev/null +++ b/jas/runner/jasrunner.go @@ -0,0 +1,69 @@ +package runner + +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-security/jas" + "github.com/jfrog/jfrog-cli-security/jas/applicability" + "github.com/jfrog/jfrog-cli-security/jas/iac" + "github.com/jfrog/jfrog-cli-security/jas/sast" + "github.com/jfrog/jfrog-cli-security/jas/secrets" + "github.com/jfrog/jfrog-cli-security/utils" + "github.com/jfrog/jfrog-client-go/utils/io" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/jfrog/jfrog-client-go/xray/services" +) + +func RunJasScannersAndSetResults(extendedScanResults *utils.ExtendedScanResults, technologiesList []coreutils.Technology, xrayScanResults []services.ScanResponse, directDependencies []string, + serverDetails *config.ServerDetails, workingDirs []string, progress io.ProgressMgr, thirdPartyApplicabilityScan bool, msi string, scanType applicability.ApplicabilityScanType, secretsScanType secrets.SecretsScanType) (err 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 + } + scanner, err := jas.NewJasScanner(workingDirs, serverDetails) + if err != nil { + return + } + defer func() { + cleanup := scanner.ScannerDirCleanupFunc + err = errors.Join(err, cleanup()) + }() + if progress != nil { + progress.SetHeadlineMsg("Running applicability scanning") + } + // Set environments variables for analytics in analyzers manager. + callback := jas.SetAnalyticsMetricsDataForAnalyzerManager(msi, technologiesList) + defer callback() + + extendedScanResults.ApplicabilityScanResults, err = applicability.RunApplicabilityScan(xrayScanResults, directDependencies, technologiesList, scanner, thirdPartyApplicabilityScan, scanType) + if err != nil { + return + } + // Don't execute other scanners when scanning third party dependencies. + if thirdPartyApplicabilityScan { + return + } + if progress != nil { + progress.SetHeadlineMsg("Running secrets scanning") + } + extendedScanResults.SecretsScanResults, err = secrets.RunSecretsScan(scanner, secretsScanType) + if err != nil { + return + } + if scanType == applicability.ApplicabilityScannerType || secretsScanType == secrets.SecretsScannerType { + if progress != nil { + progress.SetHeadlineMsg("Running IaC scanning") + } + extendedScanResults.IacScanResults, err = iac.RunIacScan(scanner) + if err != nil { + return + } + if progress != nil { + progress.SetHeadlineMsg("Running SAST scanning") + } + extendedScanResults.SastScanResults, err = sast.RunSastScan(scanner) + } + return +} diff --git a/commands/audit/jasrunner_test.go b/jas/runner/jasrunner_test.go similarity index 56% rename from commands/audit/jasrunner_test.go rename to jas/runner/jasrunner_test.go index d9587dab..3f7eda13 100644 --- a/commands/audit/jasrunner_test.go +++ b/jas/runner/jasrunner_test.go @@ -1,11 +1,13 @@ -package audit +package runner import ( "os" "testing" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" + "github.com/jfrog/jfrog-cli-security/jas/applicability" + "github.com/jfrog/jfrog-cli-security/jas/secrets" "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/stretchr/testify/assert" @@ -22,14 +24,14 @@ func TestGetExtendedScanResults_AnalyzerManagerDoesntExist(t *testing.T) { assert.NoError(t, os.Unsetenv(coreutils.HomeDir)) }() scanResults := &utils.Results{ScaResults: []utils.ScaScanResult{{Technology: coreutils.Yarn, XrayResults: jas.FakeBasicXrayResults}}, ExtendedScanResults: &utils.ExtendedScanResults{}} - err = runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, &jas.FakeServerDetails, nil, nil, false, "") + err = RunJasScannersAndSetResults(scanResults.ExtendedScanResults, scanResults.GetScaScannedTechnologies(), scanResults.GetScaScansXrayResults(), []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, &jas.FakeServerDetails, nil, nil, false, "", applicability.ApplicabilityScannerType, secrets.SecretsScannerType) // Expect error: assert.Error(t, err) } func TestGetExtendedScanResults_ServerNotValid(t *testing.T) { scanResults := &utils.Results{ScaResults: []utils.ScaScanResult{{Technology: coreutils.Pip, XrayResults: jas.FakeBasicXrayResults}}, ExtendedScanResults: &utils.ExtendedScanResults{}} - err := runJasScannersAndSetResults(scanResults, []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, nil, nil, nil, false, "") + err := RunJasScannersAndSetResults(scanResults.ExtendedScanResults, scanResults.GetScaScannedTechnologies(), scanResults.GetScaScansXrayResults(), []string{"issueId_1_direct_dependency", "issueId_2_direct_dependency"}, nil, nil, nil, false, "", applicability.ApplicabilityScannerType, secrets.SecretsScannerType) assert.NoError(t, err) } @@ -37,7 +39,7 @@ func TestGetExtendedScanResults_AnalyzerManagerReturnsError(t *testing.T) { assert.NoError(t, utils.DownloadAnalyzerManagerIfNeeded()) scanResults := &utils.Results{ScaResults: []utils.ScaScanResult{{Technology: coreutils.Yarn, XrayResults: jas.FakeBasicXrayResults}}, ExtendedScanResults: &utils.ExtendedScanResults{}} - err := runJasScannersAndSetResults(scanResults, []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"}, &jas.FakeServerDetails, nil, nil, false, "") + err := RunJasScannersAndSetResults(scanResults.ExtendedScanResults, scanResults.GetScaScannedTechnologies(), scanResults.GetScaScansXrayResults(), []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"}, &jas.FakeServerDetails, nil, nil, false, "", applicability.ApplicabilityScannerType, secrets.SecretsScannerType) // Expect error: assert.ErrorContains(t, err, "failed to run Applicability scan") diff --git a/commands/audit/jas/sast/sastscanner.go b/jas/sast/sastscanner.go similarity index 98% rename from commands/audit/jas/sast/sastscanner.go rename to jas/sast/sastscanner.go index f81d869a..4f2925fa 100644 --- a/commands/audit/jas/sast/sastscanner.go +++ b/jas/sast/sastscanner.go @@ -6,7 +6,7 @@ import ( "path/filepath" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/owenrumney/go-sarif/v2/sarif" diff --git a/commands/audit/jas/sast/sastscanner_test.go b/jas/sast/sastscanner_test.go similarity index 99% rename from commands/audit/jas/sast/sastscanner_test.go rename to jas/sast/sastscanner_test.go index 5dcd2110..3a67ab15 100644 --- a/commands/audit/jas/sast/sastscanner_test.go +++ b/jas/sast/sastscanner_test.go @@ -4,7 +4,7 @@ import ( "path/filepath" "testing" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-security/utils" "github.com/owenrumney/go-sarif/v2/sarif" diff --git a/commands/audit/jas/secrets/secretsscanner.go b/jas/secrets/secretsscanner.go similarity index 82% rename from commands/audit/jas/secrets/secretsscanner.go rename to jas/secrets/secretsscanner.go index ca9d2ce7..5b0bfd7a 100644 --- a/commands/audit/jas/secrets/secretsscanner.go +++ b/jas/secrets/secretsscanner.go @@ -5,21 +5,26 @@ import ( "strings" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/owenrumney/go-sarif/v2/sarif" ) const ( - secretsScanCommand = "sec" - secretsScannerType = "secrets-scan" - secretsDocsUrlSuffix = "secrets" + secretsScanCommand = "sec" + secretsDocsUrlSuffix = "secrets" + + SecretsScannerType SecretsScanType = "secrets-scan" // #nosec + SecretsScannerDockerScanType SecretsScanType = "secrets-docker-scan" // #nosec ) +type SecretsScanType string + type SecretScanManager struct { secretsScannerResults []*sarif.Run scanner *jas.JasScanner + scanType SecretsScanType } // The getSecretsScanResults function runs the secrets scan flow, which includes the following steps: @@ -29,8 +34,8 @@ type SecretScanManager struct { // Return values: // []utils.IacOrSecretResult: a list of the secrets that were found. // error: An error object (if any). -func RunSecretsScan(scanner *jas.JasScanner) (results []*sarif.Run, err error) { - secretScanManager := newSecretsScanManager(scanner) +func RunSecretsScan(scanner *jas.JasScanner, scanType SecretsScanType) (results []*sarif.Run, err error) { + secretScanManager := newSecretsScanManager(scanner, scanType) log.Info("Running secrets scanning...") if err = secretScanManager.scanner.Run(secretScanManager); err != nil { err = utils.ParseAnalyzerManagerError(utils.Secrets, err) @@ -43,10 +48,11 @@ func RunSecretsScan(scanner *jas.JasScanner) (results []*sarif.Run, err error) { return } -func newSecretsScanManager(scanner *jas.JasScanner) (manager *SecretScanManager) { +func newSecretsScanManager(scanner *jas.JasScanner, scanType SecretsScanType) (manager *SecretScanManager) { return &SecretScanManager{ secretsScannerResults: []*sarif.Run{}, scanner: scanner, + scanType: scanType, } } @@ -89,7 +95,7 @@ func (s *SecretScanManager) createConfigFile(module jfrogappsconfig.Module) erro { Roots: roots, Output: s.scanner.ResultsFileName, - Type: secretsScannerType, + Type: string(s.scanType), SkippedDirs: jas.GetExcludePatterns(module, module.Scanners.Secrets), }, }, diff --git a/commands/audit/jas/secrets/secretsscanner_test.go b/jas/secrets/secretsscanner_test.go similarity index 89% rename from commands/audit/jas/secrets/secretsscanner_test.go rename to jas/secrets/secretsscanner_test.go index 82f10c99..bee3e87a 100644 --- a/commands/audit/jas/secrets/secretsscanner_test.go +++ b/jas/secrets/secretsscanner_test.go @@ -6,7 +6,7 @@ import ( "testing" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" - "github.com/jfrog/jfrog-cli-security/commands/audit/jas" + "github.com/jfrog/jfrog-cli-security/jas" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/stretchr/testify/assert" @@ -15,7 +15,7 @@ import ( func TestNewSecretsScanManager(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - secretScanManager := newSecretsScanManager(scanner) + secretScanManager := newSecretsScanManager(scanner, SecretsScannerType) assert.NotEmpty(t, secretScanManager) assert.NotEmpty(t, secretScanManager.scanner.ConfigFileName) @@ -26,7 +26,7 @@ func TestNewSecretsScanManager(t *testing.T) { func TestSecretsScan_CreateConfigFile_VerifyFileWasCreated(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - secretScanManager := newSecretsScanManager(scanner) + secretScanManager := newSecretsScanManager(scanner, SecretsScannerType) currWd, err := coreutils.GetWorkingDirectory() assert.NoError(t, err) @@ -53,7 +53,7 @@ func TestRunAnalyzerManager_ReturnsGeneralError(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - secretScanManager := newSecretsScanManager(scanner) + secretScanManager := newSecretsScanManager(scanner, SecretsScannerType) assert.Error(t, secretScanManager.runAnalyzerManager()) } @@ -61,7 +61,7 @@ func TestParseResults_EmptyResults(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() // Arrange - secretScanManager := newSecretsScanManager(scanner) + secretScanManager := newSecretsScanManager(scanner, SecretsScannerType) secretScanManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "secrets-scan", "no-secrets.sarif") // Act @@ -84,7 +84,7 @@ func TestParseResults_ResultsContainSecrets(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - secretScanManager := newSecretsScanManager(scanner) + secretScanManager := newSecretsScanManager(scanner, SecretsScannerType) secretScanManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "secrets-scan", "contain-secrets.sarif") // Act @@ -107,7 +107,7 @@ func TestGetSecretsScanResults_AnalyzerManagerReturnsError(t *testing.T) { scanner, cleanUp := jas.InitJasTest(t) defer cleanUp() - secretsResults, err := RunSecretsScan(scanner) + secretsResults, err := RunSecretsScan(scanner, SecretsScannerType) assert.Error(t, err) assert.ErrorContains(t, err, "failed to run Secrets scan") diff --git a/commands/audit/jas/testdata/.jfrog/jfrog-apps-config.yml b/jas/testdata/.jfrog/jfrog-apps-config.yml similarity index 100% rename from commands/audit/jas/testdata/.jfrog/jfrog-apps-config.yml rename to jas/testdata/.jfrog/jfrog-apps-config.yml diff --git a/scans_test.go b/scans_test.go index 6d9b6bae..6bf1a318 100644 --- a/scans_test.go +++ b/scans_test.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "github.com/jfrog/jfrog-cli-security/formats" "net/http" "net/http/httptest" "os" @@ -233,6 +234,50 @@ func createTestWatch(t *testing.T) (string, func()) { } } +// JAS docker scan tests + +func TestAdvancedSecurityDockerScan(t *testing.T) { + cleanup := initNativeDockerWithXrayTest(t) + defer cleanup() + runAdvancedSecurityDockerScan(t, "jfrog/demo-security:latest") +} + +func runAdvancedSecurityDockerScan(t *testing.T, imageName string) { + // Pull image from docker repo + imageTag := path.Join(*securityTests.ContainerRegistry, securityTests.DockerVirtualRepo, imageName) + dockerPullCommand := container.NewPullCommand(containerUtils.DockerClient) + dockerPullCommand.SetCmdParams([]string{"pull", imageTag}).SetImageTag(imageTag).SetRepo(securityTests.DockerVirtualRepo).SetServerDetails(securityTests.XrDetails).SetBuildConfiguration(new(build.BuildConfiguration)) + if assert.NoError(t, dockerPullCommand.Run()) { + defer commonTests.DeleteTestImage(t, imageTag, containerUtils.DockerClient) + args := []string{"docker", "scan", imageTag, "--server-id=default", "--format=simple-json", "--fail=false", "--min-severity=low", "--fixable-only"} + + // Run docker scan on image + output := securityTests.PlatformCli.WithoutCredentials().RunCliCmdWithOutput(t, args...) + if assert.NotEmpty(t, output) { + verifyAdvancedSecurityScanResults(t, output) + } + } +} + +func verifyAdvancedSecurityScanResults(t *testing.T, content string) { + var results formats.SimpleJsonResults + err := json.Unmarshal([]byte(content), &results) + assert.NoError(t, err) + // Verify that the scan succeeded, and that at least one "Applicable" status was received. + applicableStatusExists := false + for _, vulnerability := range results.Vulnerabilities { + if vulnerability.Applicable == string(utils.Applicable) { + applicableStatusExists = true + break + } + } + assert.True(t, applicableStatusExists) + + // Verify that secretes detection succeeded. + assert.NotEqual(t, 0, len(results.Secrets)) + +} + // Curation tests func TestCurationAudit(t *testing.T) {