Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run contextual analysis and secret detection in Docker scans #1035 #1052

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions xray/commands/audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/jfrog/jfrog-cli-core/v2/utils/dependencies"
"github.com/jfrog/jfrog-cli-core/v2/xray/scangraph"
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 @@ -157,7 +156,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 = xrayutils.IsEntitledForJas(xrayManager, auditParams.xrayVersion)
if err != nil {
return
}
Expand All @@ -182,12 +181,3 @@ func RunAudit(auditParams *AuditParams) (results *xrayutils.Results, err error)
}
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
}
entitled, err = xrayManager.IsEntitled(xrayutils.ApplicabilityFeatureId)
return
}
43 changes: 39 additions & 4 deletions xray/commands/audit/jas/applicability/applicabilitymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import (
)

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

type ApplicabilityScanManager struct {
Expand All @@ -29,6 +30,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:
Expand All @@ -55,6 +57,37 @@ func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencie
return
}

// 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.
// Running the analyzer manager executable.
// Parsing the analyzer manager results.
// Return values:
// map[string]string: A map containing the applicability result of each XRAY CVE.
// bool: true if the user is entitled to the applicability scan, false otherwise.
// error: An error object (if any).
func RunApplicabilityWithScanCves(xrayResults []services.ScanResponse, cveList []string,
scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner) (results []*sarif.Run, err error) {
applicabilityScanManager := newApplicabilityScanManagerCves(xrayResults, cveList, scanner)
if err = applicabilityScanManager.scanner.Run(applicabilityScanManager); err != nil {
err = utils.ParseAnalyzerManagerError(utils.Applicability, err)
return
}
results = applicabilityScanManager.applicabilityScanResults
return
}

func newApplicabilityScanManagerCves(xrayScanResults []services.ScanResponse, cveList []string, scanner *jas.JasScanner) (manager *ApplicabilityScanManager) {
return &ApplicabilityScanManager{
applicabilityScanResults: []*sarif.Run{},
directDependenciesCves: cveList,
xrayResults: xrayScanResults,
scanner: scanner,
thirdPartyScan: false,
commandType: applicabilityDockerScanScanType,
}
}

func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner, thirdPartyScan bool) (manager *ApplicabilityScanManager) {
directDependenciesCves, indirectDependenciesCves := extractDependenciesCvesFromScan(xrayScanResults, directDependencies)
return &ApplicabilityScanManager{
Expand All @@ -64,6 +97,7 @@ func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, direct
xrayResults: xrayScanResults,
scanner: scanner,
thirdPartyScan: thirdPartyScan,
commandType: applicabilityScanType,
}
}

Expand Down Expand Up @@ -152,6 +186,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 +204,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
17 changes: 10 additions & 7 deletions xray/commands/audit/jas/secrets/secretsscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ import (
)

const (
secretsScanCommand = "sec"
secretsScannerType = "secrets-scan"
secretsDocsUrlSuffix = "secrets"
secretsScanCommand = "sec"
SecretsScannerType = "secrets-scan" // #nosec
SecretsScannerDockerScanType = "secrets-docker-scan" // #nosec
secretsDocsUrlSuffix = "secrets"
)

type SecretScanManager struct {
secretsScannerResults []*sarif.Run
scanner *jas.JasScanner
scanType string
}

// The getSecretsScanResults function runs the secrets scan flow, which includes the following steps:
Expand All @@ -29,8 +31,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 string) (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)
Expand All @@ -43,10 +45,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 string) (manager *SecretScanManager) {
return &SecretScanManager{
secretsScannerResults: []*sarif.Run{},
scanner: scanner,
scanType: scanType,
}
}

Expand Down Expand Up @@ -89,7 +92,7 @@ func (s *SecretScanManager) createConfigFile(module jfrogappsconfig.Module) erro
{
Roots: roots,
Output: s.scanner.ResultsFileName,
Type: secretsScannerType,
Type: s.scanType,
SkippedDirs: jas.GetExcludePatterns(module, module.Scanners.Secrets),
},
},
Expand Down
12 changes: 6 additions & 6 deletions xray/commands/audit/jas/secrets/secretsscanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -53,15 +53,15 @@ 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())
}

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
Expand All @@ -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
Expand All @@ -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")
Expand Down
3 changes: 2 additions & 1 deletion xray/commands/audit/jasrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package audit

import (
"errors"

"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas/applicability"
Expand Down Expand Up @@ -41,7 +42,7 @@ func runJasScannersAndSetResults(scanResults *utils.Results, directDependencies
if progress != nil {
progress.SetHeadlineMsg("Running secrets scanning")
}
scanResults.ExtendedScanResults.SecretsScanResults, err = secrets.RunSecretsScan(scanner)
scanResults.ExtendedScanResults.SecretsScanResults, err = secrets.RunSecretsScan(scanner, secrets.SecretsScannerType)
if err != nil {
return
}
Expand Down
45 changes: 45 additions & 0 deletions xray/commands/scan/jasrunner_cves.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package scan

import (
"errors"
"fmt"

"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas/applicability"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas/secrets"

"github.com/jfrog/jfrog-cli-core/v2/xray/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
)

func runJasScannersAndSetResults(scanResults *utils.Results, cveList []string,
serverDetails *config.ServerDetails, workingDirs []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
}
multiScanId := "" // Also empty for audit
scanner, err := jas.NewJasScanner(workingDirs, serverDetails, multiScanId)
if err != nil {
return
}

defer func() {
cleanup := scanner.ScannerDirCleanupFunc
err = errors.Join(err, cleanup())
}()

scanResults.ExtendedScanResults.ApplicabilityScanResults, err = applicability.RunApplicabilityWithScanCves(scanResults.GetScaScansXrayResults(), cveList, scanResults.GetScaScannedTechnologies(), scanner)
if err != nil {
fmt.Println("there was an error:", err)
return
}

scanResults.ExtendedScanResults.SecretsScanResults, err = secrets.RunSecretsScan(scanner, secrets.SecretsScannerDockerScanType)
if err != nil {
return
}
return
}
39 changes: 39 additions & 0 deletions xray/commands/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
"regexp"
"strings"

"golang.org/x/exp/slices"

rtutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/xray/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"
Expand Down Expand Up @@ -248,6 +252,23 @@
scanResults.XrayVersion = xrayVersion
scanResults.ScaResults = []xrutils.ScaScanResult{{XrayResults: flatResults}}

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(rtutils.DownloadAnalyzerManagerIfNeeded)

Check failure on line 259 in xray/commands/scan/scan.go

View workflow job for this annotation

GitHub Actions / Go-Sec

undefined: rtutils.DownloadAnalyzerManagerIfNeeded

Check failure on line 259 in xray/commands/scan/scan.go

View workflow job for this annotation

GitHub Actions / Go-Sec

undefined: rtutils.DownloadAnalyzerManagerIfNeeded
}
// 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 {
cveList := cveListFromVulnerabilities(flatResults)
workingDirs := []string{scanCmd.spec.Files[0].Pattern}
scanResults.JasError = runJasScannersAndSetResults(scanResults, cveList, scanCmd.serverDetails, workingDirs)
}

if err = xrutils.NewResultsWriter(scanResults).
SetOutputFormat(scanCmd.outputFormat).
SetIncludeVulnerabilities(scanCmd.includeVulnerabilities).
Expand Down Expand Up @@ -452,3 +473,21 @@
}
return scanErrors
}

func cveListFromVulnerabilities(flatResults []services.ScanResponse) []string {
var cveList []string
var technologiesList []string
for _, result := range flatResults {
for _, vulnerability := range result.Vulnerabilities {
for _, cve := range vulnerability.Cves {
if !slices.Contains(cveList, cve.Id) && (cve.Id != "") {
cveList = append(cveList, cve.Id)
}
}
if !slices.Contains(technologiesList, vulnerability.Technology) && (vulnerability.Technology != "") {
technologiesList = append(technologiesList, vulnerability.Technology)
}
}
}
return cveList
}
1 change: 1 addition & 0 deletions xray/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 xray/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
16 changes: 16 additions & 0 deletions xray/utils/jasutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package utils

import (
clientutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
"github.com/jfrog/jfrog-client-go/xray"
)

func IsEntitledForJas(xrayManager *xray.XrayServicesManager, xrayVersion string) (entitled bool, err error) {
if e := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, EntitlementsMinVersion); e != nil {
log.Debug(e)
return
}
entitled, err = xrayManager.IsEntitled(ApplicabilityFeatureId)
return
}
Loading