Skip to content

Commit d867456

Browse files
authored
Run contextual analysis and secret detection in Docker scans (#10)
1 parent e4bd282 commit d867456

21 files changed

+276
-138
lines changed

commands/audit/audit.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ package audit
33
import (
44
"errors"
55
"fmt"
6+
"os"
7+
68
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
79
"github.com/jfrog/jfrog-cli-security/scangraph"
8-
"github.com/jfrog/jfrog-cli-security/utils"
10+
11+
"github.com/jfrog/jfrog-cli-security/jas"
12+
13+
"github.com/jfrog/jfrog-cli-security/jas/applicability"
14+
"github.com/jfrog/jfrog-cli-security/jas/runner"
15+
"github.com/jfrog/jfrog-cli-security/jas/secrets"
916
clientutils "github.com/jfrog/jfrog-client-go/utils"
1017
"github.com/jfrog/jfrog-client-go/utils/log"
1118
"github.com/jfrog/jfrog-client-go/xray"
1219
"github.com/jfrog/jfrog-client-go/xray/services"
1320
xscservices "github.com/jfrog/jfrog-client-go/xsc/services"
1421
"golang.org/x/sync/errgroup"
15-
"os"
1622

1723
xrayutils "github.com/jfrog/jfrog-cli-security/utils"
1824
)
@@ -182,15 +188,15 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error)
182188
return
183189
}
184190
results.XrayVersion = auditParams.xrayVersion
185-
results.ExtendedScanResults.EntitledForJas, err = isEntitledForJas(xrayManager, auditParams.xrayVersion)
191+
results.ExtendedScanResults.EntitledForJas, err = jas.IsEntitledForJas(xrayManager, auditParams.xrayVersion)
186192
if err != nil {
187193
return
188194
}
189195

190196
errGroup := new(errgroup.Group)
191197
if results.ExtendedScanResults.EntitledForJas {
192198
// Download (if needed) the analyzer manager in a background routine.
193-
errGroup.Go(utils.DownloadAnalyzerManagerIfNeeded)
199+
errGroup.Go(xrayutils.DownloadAnalyzerManagerIfNeeded)
194200
}
195201

196202
results.MultiScanId = auditParams.XrayGraphScanParams().MultiScanId
@@ -205,16 +211,7 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error)
205211

206212
// Run scanners only if the user is entitled for Advanced Security
207213
if results.ExtendedScanResults.EntitledForJas {
208-
results.JasError = runJasScannersAndSetResults(results, auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.thirdPartyApplicabilityScan, auditParams.XrayGraphScanParams().MultiScanId)
209-
}
210-
return
211-
}
212-
213-
func isEntitledForJas(xrayManager *xray.XrayServicesManager, xrayVersion string) (entitled bool, err error) {
214-
if e := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, xrayutils.EntitlementsMinVersion); e != nil {
215-
log.Debug(e)
216-
return
214+
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)
217215
}
218-
entitled, err = xrayManager.IsEntitled(xrayutils.ApplicabilityFeatureId)
219216
return
220217
}

commands/audit/jasrunner.go

Lines changed: 0 additions & 63 deletions
This file was deleted.

commands/scan/dockerscan.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ package scan
33
import (
44
"bytes"
55
"fmt"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
611
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
712
xrayutils "github.com/jfrog/jfrog-cli-security/utils"
813
clientutils "github.com/jfrog/jfrog-client-go/utils"
914
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1015
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
1116
"github.com/jfrog/jfrog-client-go/utils/log"
12-
"os"
13-
"os/exec"
14-
"path/filepath"
15-
"strings"
1617
)
1718

1819
const (
@@ -81,6 +82,7 @@ func (dsc *DockerScanCommand) Run() (err error) {
8182
Pattern(imageTarPath).
8283
Target(dsc.targetRepoPath).
8384
BuildSpec()).SetThreads(1)
85+
dsc.ScanCommand.SetRunJasScans(true)
8486
err = dsc.setCredentialEnvsForIndexerApp()
8587
if err != nil {
8688
return errorutils.CheckError(err)

commands/scan/scan.go

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ import (
1010
"regexp"
1111
"strings"
1212

13+
"golang.org/x/exp/maps"
14+
"golang.org/x/exp/slices"
15+
16+
"github.com/jfrog/jfrog-cli-security/jas"
17+
"github.com/jfrog/jfrog-cli-security/jas/applicability"
18+
"github.com/jfrog/jfrog-cli-security/jas/runner"
19+
"github.com/jfrog/jfrog-cli-security/jas/secrets"
1320
"github.com/jfrog/jfrog-cli-security/scangraph"
1421
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
22+
"golang.org/x/sync/errgroup"
1523

1624
"github.com/jfrog/gofrog/parallel"
1725
"github.com/jfrog/jfrog-cli-core/v2/common/format"
18-
outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format"
1926
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
2027
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
2128
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
@@ -35,8 +42,9 @@ type FileContext func(string) parallel.TaskFunc
3542
type indexFileHandlerFunc func(file string)
3643

3744
type ScanInfo struct {
38-
Target string
39-
Result *services.ScanResponse
45+
Target string
46+
Result *services.ScanResponse
47+
ExtendedScanResults *utils.ExtendedScanResults
4048
}
4149

4250
const (
@@ -52,7 +60,7 @@ type ScanCommand struct {
5260
// The location of the downloaded Xray indexer binary on the local file system.
5361
indexerPath string
5462
indexerTempDir string
55-
outputFormat outputFormat.OutputFormat
63+
outputFormat format.OutputFormat
5664
projectKey string
5765
minSeverityFilter string
5866
watches []string
@@ -63,6 +71,7 @@ type ScanCommand struct {
6371
bypassArchiveLimits bool
6472
fixableOnly bool
6573
progress ioUtils.ProgressMgr
74+
commandSupportsJAS bool
6675
}
6776

6877
func (scanCmd *ScanCommand) SetMinSeverityFilter(minSeverityFilter string) *ScanCommand {
@@ -75,6 +84,11 @@ func (scanCmd *ScanCommand) SetFixableOnly(fixable bool) *ScanCommand {
7584
return scanCmd
7685
}
7786

87+
func (scanCmd *ScanCommand) SetRunJasScans(run bool) *ScanCommand {
88+
scanCmd.commandSupportsJAS = run
89+
return scanCmd
90+
}
91+
7892
func (scanCmd *ScanCommand) SetProgress(progress ioUtils.ProgressMgr) {
7993
scanCmd.progress = progress
8094
}
@@ -84,7 +98,7 @@ func (scanCmd *ScanCommand) SetThreads(threads int) *ScanCommand {
8498
return scanCmd
8599
}
86100

87-
func (scanCmd *ScanCommand) SetOutputFormat(format outputFormat.OutputFormat) *ScanCommand {
101+
func (scanCmd *ScanCommand) SetOutputFormat(format format.OutputFormat) *ScanCommand {
88102
scanCmd.outputFormat = format
89103
return scanCmd
90104
}
@@ -182,6 +196,16 @@ func (scanCmd *ScanCommand) Run() (err error) {
182196
return err
183197
}
184198

199+
scanResults := xrutils.NewAuditResults()
200+
scanResults.XrayVersion = xrayVersion
201+
202+
scanResults.ExtendedScanResults.EntitledForJas, err = jas.IsEntitledForJas(xrayManager, xrayVersion)
203+
errGroup := new(errgroup.Group)
204+
if scanResults.ExtendedScanResults.EntitledForJas {
205+
// Download (if needed) the analyzer manager in a background routine.
206+
errGroup.Go(xrutils.DownloadAnalyzerManagerIfNeeded)
207+
}
208+
185209
// Validate Xray minimum version for graph scan command
186210
err = clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, scangraph.GraphScanMinXrayVersion)
187211
if err != nil {
@@ -226,16 +250,20 @@ func (scanCmd *ScanCommand) Run() (err error) {
226250
fileCollectingErrorsQueue := clientutils.NewErrorsQueue(1)
227251
// Start walking on the filesystem to "produce" files that match the given pattern
228252
// while the consumer uses the indexer to index those files.
229-
scanCmd.prepareScanTasks(fileProducerConsumer, indexedFileProducerConsumer, resultsArr, fileProducerErrors, indexedFileProducerErrors, fileCollectingErrorsQueue, xrayVersion)
253+
scanCmd.prepareScanTasks(fileProducerConsumer, indexedFileProducerConsumer, scanResults.ExtendedScanResults.EntitledForJas, resultsArr, fileProducerErrors, indexedFileProducerErrors, fileCollectingErrorsQueue, xrayVersion)
230254
scanCmd.performScanTasks(fileProducerConsumer, indexedFileProducerConsumer)
231255

232256
// Handle results
233257
flatResults := []xrutils.ScaScanResult{}
258+
234259
for _, arr := range resultsArr {
235260
for _, res := range arr {
236261
flatResults = append(flatResults, xrutils.ScaScanResult{Target: res.Target, XrayResults: []services.ScanResponse{*res.Result}})
262+
scanResults.ExtendedScanResults.ApplicabilityScanResults = append(scanResults.ExtendedScanResults.ApplicabilityScanResults, res.ExtendedScanResults.ApplicabilityScanResults...)
263+
scanResults.ExtendedScanResults.SecretsScanResults = append(scanResults.ExtendedScanResults.SecretsScanResults, res.ExtendedScanResults.SecretsScanResults...)
237264
}
238265
}
266+
239267
if scanCmd.progress != nil {
240268
if err = scanCmd.progress.Quit(); err != nil {
241269
return err
@@ -251,10 +279,13 @@ func (scanCmd *ScanCommand) Run() (err error) {
251279
scanErrors = appendErrorSlice(scanErrors, fileProducerErrors)
252280
scanErrors = appendErrorSlice(scanErrors, indexedFileProducerErrors)
253281

254-
scanResults := xrutils.NewAuditResults()
255-
scanResults.XrayVersion = xrayVersion
256282
scanResults.ScaResults = flatResults
257283

284+
// Wait for the Download of the AnalyzerManager to complete.
285+
if err = errGroup.Wait(); err != nil {
286+
err = errors.New("failed while trying to get Analyzer Manager: " + err.Error())
287+
}
288+
258289
if err = xrutils.NewResultsWriter(scanResults).
259290
SetOutputFormat(scanCmd.outputFormat).
260291
SetIncludeVulnerabilities(scanCmd.includeVulnerabilities).
@@ -296,14 +327,14 @@ func (scanCmd *ScanCommand) CommandName() string {
296327
return "xr_scan"
297328
}
298329

299-
func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, fileCollectingErrorsQueue *clientutils.ErrorsQueue, xrayVersion string) {
330+
func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, entitledForJas bool, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, fileCollectingErrorsQueue *clientutils.ErrorsQueue, xrayVersion string) {
300331
go func() {
301332
defer fileProducer.Done()
302333
// Iterate over file-spec groups and produce indexing tasks.
303334
// When encountering an error, log and move to next group.
304335
specFiles := scanCmd.spec.Files
305336
for i := range specFiles {
306-
artifactHandlerFunc := scanCmd.createIndexerHandlerFunc(&specFiles[i], indexedFileProducer, resultsArr, fileErrors, indexedFileErrors, xrayVersion)
337+
artifactHandlerFunc := scanCmd.createIndexerHandlerFunc(&specFiles[i], entitledForJas, indexedFileProducer, resultsArr, fileErrors, indexedFileErrors, xrayVersion)
307338
taskHandler := getAddTaskToProducerFunc(fileProducer, artifactHandlerFunc)
308339

309340
err := collectFilesForIndexing(specFiles[i], taskHandler)
@@ -315,7 +346,7 @@ func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer p
315346
}()
316347
}
317348

318-
func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, xrayVersion string) FileContext {
349+
func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, entitledForJas bool, indexedFileProducer parallel.Runner, resultsArr [][]*ScanInfo, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, xrayVersion string) FileContext {
319350
return func(filePath string) parallel.TaskFunc {
320351
return func(threadId int) (err error) {
321352
logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, false)
@@ -360,12 +391,26 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFil
360391
return err
361392
}
362393
scanResults, err := scangraph.RunScanGraphAndGetResults(scanGraphParams, xrayManager)
394+
363395
if err != nil {
364396
log.Error(fmt.Sprintf("scanning '%s' failed with error: %s", graph.Id, err.Error()))
365397
indexedFileErrors[threadId] = append(indexedFileErrors[threadId], formats.SimpleJsonError{FilePath: filePath, ErrorMessage: err.Error()})
366398
return
367399
}
368-
resultsArr[threadId] = append(resultsArr[threadId], &ScanInfo{Target: filePath, Result: scanResults})
400+
401+
extendedScanResults := utils.ExtendedScanResults{}
402+
if entitledForJas && scanCmd.commandSupportsJAS {
403+
// Run Jas scans
404+
workingDirs := []string{filePath}
405+
err = runner.RunJasScannersAndSetResults(&extendedScanResults, []coreutils.Technology{coreutils.Technology(scanResults.ScannedPackageType)}, []services.ScanResponse{*scanResults}, depsListFromVulnerabilities(*scanResults), scanCmd.serverDetails, workingDirs, nil, false, "", applicability.ApplicabilityDockerScanScanType, secrets.SecretsScannerDockerScanType)
406+
407+
if err != nil {
408+
log.Error(fmt.Sprintf("scanning '%s' failed with error: %s", graph.Id, err.Error()))
409+
indexedFileErrors[threadId] = append(indexedFileErrors[threadId], formats.SimpleJsonError{FilePath: filePath, ErrorMessage: err.Error()})
410+
return
411+
}
412+
}
413+
resultsArr[threadId] = append(resultsArr[threadId], &ScanInfo{Target: filePath, Result: scanResults, ExtendedScanResults: &extendedScanResults})
369414
return
370415
}
371416

@@ -469,6 +514,20 @@ func appendErrorSlice(scanErrors []formats.SimpleJsonError, errorsToAdd [][]form
469514
return scanErrors
470515
}
471516

517+
func depsListFromVulnerabilities(scanResult ...services.ScanResponse) (depsList []string) {
518+
for _, result := range scanResult {
519+
for _, vulnerability := range result.Vulnerabilities {
520+
dependencies := maps.Keys(vulnerability.Components)
521+
for _, dependency := range dependencies {
522+
if !slices.Contains(depsList, dependency) {
523+
depsList = append(depsList, dependency)
524+
}
525+
}
526+
}
527+
}
528+
return
529+
}
530+
472531
func ConditionalUploadDefaultScanFunc(serverDetails *config.ServerDetails, fileSpec *spec.SpecFiles, threads int, scanOutputFormat format.OutputFormat) error {
473532
return NewScanCommand().SetServerDetails(serverDetails).SetSpec(fileSpec).SetThreads(threads).SetOutputFormat(scanOutputFormat).SetFail(true).SetPrintExtendedTable(false).Run()
474533
}

formats/conversion.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func ConvertToVulnerabilityScanTableRow(rows []VulnerabilityOrViolationRow) (tab
2828
tableRows = append(tableRows, vulnerabilityScanTableRow{
2929
severity: rows[i].Severity,
3030
severityNumValue: rows[i].SeverityNumValue,
31+
applicable: rows[i].Applicable,
3132
impactedPackageName: rows[i].ImpactedDependencyName,
3233
impactedPackageVersion: rows[i].ImpactedDependencyVersion,
3334
ImpactedPackageType: rows[i].ImpactedDependencyType,

formats/table.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ type vulnerabilityTableRow struct {
2020
}
2121

2222
type vulnerabilityScanTableRow struct {
23-
severity string `col-name:"Severity"`
23+
severity string `col-name:"Severity"`
24+
applicable string `col-name:"Contextual\nAnalysis" omitempty:"true"`
2425
// For sorting
2526
severityNumValue int
2627
directPackages []directPackagesTableRow `embed-table:"true"`

0 commit comments

Comments
 (0)