Skip to content

Commit

Permalink
Docker scan JAS support, not tests and unit tests need fix jfrog. Rem…
Browse files Browse the repository at this point in the history
…ove multiscan #4
  • Loading branch information
guyshe-jfrog committed Jan 31, 2024
1 parent 0fc5abe commit a66c4c1
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 33 deletions.
12 changes: 1 addition & 11 deletions 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-security/scangraph"
"github.com/jfrog/jfrog-cli-security/utils"
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 @@ -161,7 +160,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 @@ -186,12 +185,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 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
)

// TODO: ADD
var mockDirectDependencies = []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency"}
var mockMultiRootDirectDependencies = []string{"issueId_2_direct_dependency", "issueId_1_direct_dependency", "issueId_3_direct_dependency", "issueId_4_direct_dependency"}

Expand Down
17 changes: 10 additions & 7 deletions 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
13 changes: 7 additions & 6 deletions commands/audit/jas/secrets/secretsscanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
"github.com/stretchr/testify/assert"
)

// TODO: Add docker scan applicablity and seacrets
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 +27,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 +54,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 +85,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 +108,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 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-security/commands/audit/jas"
"github.com/jfrog/jfrog-cli-security/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
44 changes: 44 additions & 0 deletions commands/scan/jasrunner_cves.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package scan

import (
"errors"
"fmt"

"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/secrets"

"github.com/jfrog/jfrog-cli-security/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
}
scanner, err := jas.NewJasScanner(workingDirs, serverDetails)
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
}
43 changes: 40 additions & 3 deletions commands/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
"regexp"
"strings"

"golang.org/x/exp/slices"

"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 @@ -46,7 +48,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 @@ -78,7 +80,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 @@ -249,6 +251,23 @@ func (scanCmd *ScanCommand) Run() (err error) {
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(xrutils.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 @@ -454,6 +473,24 @@ func appendErrorSlice(scanErrors []formats.SimpleJsonError, errorsToAdd [][]form
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
}

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
@@ -0,0 +1 @@
TODO
Loading

0 comments on commit a66c4c1

Please sign in to comment.