Skip to content

Commit

Permalink
Run contextual analysis and secret detection in Docker scans jfrog#10 j…
Browse files Browse the repository at this point in the history
  • Loading branch information
guyshe-jfrog committed Mar 4, 2024
1 parent d82ecc0 commit 3701097
Show file tree
Hide file tree
Showing 19 changed files with 150 additions and 78 deletions.
22 changes: 7 additions & 15 deletions commands/audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package audit

import (
"errors"
"github.com/jfrog/jfrog-cli-security/scangraph"
"os"

"github.com/jfrog/jfrog-cli-security/scangraph"

"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-security/utils"
"github.com/jfrog/jfrog-cli-security/jas/applicability"
"github.com/jfrog/jfrog-cli-security/jas/runner"
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"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -160,15 +161,15 @@ 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 = xrayutils.IsEntitledForJas(xrayManager, auditParams.xrayVersion)
if err != nil {
return
}

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)
}

if auditParams.xrayGraphScanParams.XscGitInfoContext != nil {
Expand All @@ -188,16 +189,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)
}
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, auditParams.DirectDependencies(), serverDetails, auditParams.workingDirs, auditParams.Progress(), auditParams.thirdPartyApplicabilityScan, applicability.ApplicabilityScannerType)
}
entitled, err = xrayManager.IsEntitled(xrayutils.ApplicabilityFeatureId)
return
}
62 changes: 56 additions & 6 deletions commands/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ import (
"regexp"
"strings"

"golang.org/x/exp/maps"
"golang.org/x/exp/slices"

"github.com/jfrog/jfrog-cli-security/jas/applicability"
"github.com/jfrog/jfrog-cli-security/jas/runner"
"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"
Expand Down Expand Up @@ -47,7 +52,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
Expand Down Expand Up @@ -79,7 +84,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
}
Expand Down Expand Up @@ -177,6 +182,16 @@ func (scanCmd *ScanCommand) Run() (err error) {
return err
}

scanResults := xrutils.NewAuditResults()
scanResults.XrayVersion = xrayVersion

scanResults.ExtendedScanResults.EntitledForJas, err = xrutils.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 {
Expand Down Expand Up @@ -246,9 +261,22 @@ func (scanCmd *ScanCommand) Run() (err error) {
scanErrors = appendErrorSlice(scanErrors, fileProducerErrors)
scanErrors = appendErrorSlice(scanErrors, indexedFileProducerErrors)

scanResults := xrutils.NewAuditResults()
scanResults.XrayVersion = xrayVersion
scanResults.ScaResults = []xrutils.ScaScanResult{{XrayResults: 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 scanResults.ExtendedScanResults.EntitledForJas {
depsList := depsListFromVulnerabilities(flatResults)

for _, scanResult := range flatResults {
scanResults.ScaResults = append(scanResults.ScaResults, xrutils.ScaScanResult{XrayResults: []services.ScanResponse{scanResult}, Technology: coreutils.Technology(scanResult.ScannedPackageType)})
}

workingDirs := []string{scanCmd.spec.Files[0].Pattern}

scanResults.JasError = runner.RunJasScannersAndSetResults(scanResults, depsList, scanCmd.serverDetails, workingDirs, nil, false, applicability.ApplicabilityDockerScanScanType)
}

if err = xrutils.NewResultsWriter(scanResults).
SetOutputFormat(scanCmd.outputFormat).
Expand Down Expand Up @@ -464,6 +492,28 @@ func appendErrorSlice(scanErrors []formats.SimpleJsonError, errorsToAdd [][]form
return scanErrors
}

func depsListFromVulnerabilities(flatResults []services.ScanResponse) []string {
var depsList []string
var technologiesList []coreutils.Technology
for _, result := range flatResults {
for _, vulnerability := range result.Vulnerabilities {
dependencies := maps.Keys(vulnerability.Components)
for _, dependency := range dependencies {
if !slices.Contains(depsList, dependency) {
depsList = append(depsList, dependency)
}
}

if !slices.Contains(technologiesList, coreutils.Technology(vulnerability.Technology)) && (vulnerability.Technology != "") {
technologiesList = append(technologiesList, coreutils.Technology(vulnerability.Technology))
}

}

}
return depsList
}

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()
}
1 change: 1 addition & 0 deletions formats/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion formats/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -17,18 +17,22 @@ import (
)

const (
applicabilityScanType = "analyze-applicability"
applicabilityScanCommand = "ca"
applicabilityDocsUrlSuffix = "contextual-analysis"
ApplicabilityScannerType = "analyze-applicability"
applicabilityScanCommand = "ca"
applicabilityDocsUrlSuffix = "contextual-analysis"
ApplicabilityDockerScanScanType = "analyze-applicability-docker-scan"
)

type ApplicabilityScanType string

type ApplicabilityScanManager struct {
applicabilityScanResults []*sarif.Run
directDependenciesCves []string
indirectDependenciesCves []string
xrayResults []services.ScanResponse
scanner *jas.JasScanner
thirdPartyScan bool
commandType string
}

// The getApplicabilityScanResults function runs the applicability scan flow, which includes the following steps:
Expand All @@ -41,8 +45,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.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 dependencies. Skipping....")
return
Expand All @@ -55,7 +59,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{},
Expand All @@ -64,6 +68,7 @@ func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, direct
xrayResults: xrayScanResults,
scanner: scanner,
thirdPartyScan: thirdPartyScan,
commandType: string(scanType),
}
}

Expand Down Expand Up @@ -152,6 +157,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 {
Expand All @@ -169,7 +175,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
if assert.NotNil(t, applicabilityManager) {
Expand All @@ -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, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
if assert.NotNil(t, applicabilityManager) {
Expand Down Expand Up @@ -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, ApplicabilityScanType(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, ApplicabilityScanType(ApplicabilityScannerType))
assertApplicabilityScanner(t, applicabilityManager)
}

Expand All @@ -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, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
if assert.NotNil(t, applicabilityManager) {
Expand All @@ -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, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
if assert.NotNil(t, applicabilityManager) {
Expand All @@ -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, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
if assert.NotNil(t, applicabilityManager) {
Expand All @@ -157,7 +157,7 @@ func TestApplicabilityScanManager_ShouldRun_TechnologiesNotEligibleForScan(t *te
scanner, cleanUp := jas.InitJasTest(t)
defer cleanUp()

results, err := RunApplicabilityScan(jas.FakeBasicXrayResults, mockDirectDependencies, []coreutils.Technology{coreutils.Nuget, coreutils.Go}, scanner, false)
results, err := RunApplicabilityScan(jas.FakeBasicXrayResults, mockDirectDependencies, []coreutils.Technology{coreutils.Nuget, coreutils.Go}, scanner, false, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
assert.Nil(t, results)
Expand All @@ -169,7 +169,7 @@ func TestApplicabilityScanManager_ShouldRun_ScanResultsAreEmpty(t *testing.T) {
scanner, cleanUp := jas.InitJasTest(t)
defer cleanUp()

applicabilityManager := newApplicabilityScanManager(nil, mockDirectDependencies, scanner, false)
applicabilityManager := newApplicabilityScanManager(nil, mockDirectDependencies, scanner, false, ApplicabilityScanType(ApplicabilityScannerType))

// Assert
eligible := applicabilityManager.shouldRunApplicabilityScan([]coreutils.Technology{coreutils.Nuget})
Expand Down Expand Up @@ -264,7 +264,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, ApplicabilityScanType(ApplicabilityScannerType))

currWd, err := coreutils.GetWorkingDirectory()
assert.NoError(t, err)
Expand All @@ -288,7 +288,7 @@ func TestParseResults_EmptyResults_AllCvesShouldGetUnknown(t *testing.T) {
scanner, cleanUp := jas.InitJasTest(t)
defer cleanUp()

applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false)
applicabilityManager := newApplicabilityScanManager(jas.FakeBasicXrayResults, mockDirectDependencies, scanner, false, ApplicabilityScanType(ApplicabilityScannerType))
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "empty-results.sarif")

// Act
Expand All @@ -305,7 +305,7 @@ func TestParseResults_ApplicableCveExist(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, ApplicabilityScanType(ApplicabilityScannerType))
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "applicable-cve-results.sarif")

// Act
Expand All @@ -322,7 +322,7 @@ func TestParseResults_AllCvesNotApplicable(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, ApplicabilityScanType(ApplicabilityScannerType))
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "no-applicable-cves-results.sarif")

// Act
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading

0 comments on commit 3701097

Please sign in to comment.