Skip to content

Commit

Permalink
Add summary format (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored May 22, 2024
1 parent 5738c28 commit 3e9fe2d
Show file tree
Hide file tree
Showing 20 changed files with 1,137 additions and 98 deletions.
15 changes: 8 additions & 7 deletions commands/audit/scarunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/jfrog/build-info-go/utils/pythonutils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
"os"
"time"

"github.com/jfrog/build-info-go/utils/pythonutils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"

"github.com/jfrog/gofrog/datastructures"
"github.com/jfrog/jfrog-cli-core/v2/common/project"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
Expand Down Expand Up @@ -58,9 +59,9 @@ func runScaScan(params *AuditParams, results *xrayutils.Results) (err error) {
}()
for _, scan := range scans {
// Run the scan
log.Info("Running SCA scan for", scan.Technology, "vulnerable dependencies in", scan.WorkingDirectory, "directory...")
log.Info("Running SCA scan for", scan.Technology, "vulnerable dependencies in", scan.Target, "directory...")
if wdScanErr := executeScaScan(serverDetails, params, scan); wdScanErr != nil {
err = errors.Join(err, fmt.Errorf("audit command in '%s' failed:\n%s", scan.WorkingDirectory, wdScanErr.Error()))
err = errors.Join(err, fmt.Errorf("audit command in '%s' failed:\n%s", scan.Target, wdScanErr.Error()))
continue
}
// Add the scan to the results
Expand All @@ -87,11 +88,11 @@ func getScaScansToPreform(params *AuditParams) (scansToPreform []*xrayutils.ScaS
}
if len(workingDirs) == 0 {
// Requested technology (from params) descriptors/indicators was not found, scan only requested directory for this technology.
scansToPreform = append(scansToPreform, &xrayutils.ScaScanResult{WorkingDirectory: requestedDirectory, Technology: tech})
scansToPreform = append(scansToPreform, &xrayutils.ScaScanResult{Target: requestedDirectory, Technology: tech})
}
for workingDir, descriptors := range workingDirs {
// Add scan for each detected working directory.
scansToPreform = append(scansToPreform, &xrayutils.ScaScanResult{WorkingDirectory: workingDir, Technology: tech, Descriptors: descriptors})
scansToPreform = append(scansToPreform, &xrayutils.ScaScanResult{Target: workingDir, Technology: tech, Descriptors: descriptors})
}
}
}
Expand All @@ -110,7 +111,7 @@ func getRequestedDescriptors(params *AuditParams) map[coreutils.Technology][]str
// This method will change the working directory to the scan's working directory.
func executeScaScan(serverDetails *config.ServerDetails, params *AuditParams, scan *xrayutils.ScaScanResult) (err error) {
// Get the dependency tree for the technology in the working directory.
if err = os.Chdir(scan.WorkingDirectory); err != nil {
if err = os.Chdir(scan.Target); err != nil {
return errorutils.CheckError(err)
}
treeResult, techErr := GetTechDependencyTree(params.AuditBasicParams, scan.Technology)
Expand Down
56 changes: 28 additions & 28 deletions commands/audit/scarunner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,23 @@ func TestGetScaScansToPreform(t *testing.T) {
},
expected: []*xrayutils.ScaScanResult{
{
Technology: coreutils.Maven,
WorkingDirectory: filepath.Join(dir, "dir", "maven"),
Technology: coreutils.Maven,
Target: filepath.Join(dir, "dir", "maven"),
Descriptors: []string{
filepath.Join(dir, "dir", "maven", "pom.xml"),
filepath.Join(dir, "dir", "maven", "maven-sub", "pom.xml"),
filepath.Join(dir, "dir", "maven", "maven-sub2", "pom.xml"),
},
},
{
Technology: coreutils.Npm,
WorkingDirectory: filepath.Join(dir, "dir", "npm"),
Descriptors: []string{filepath.Join(dir, "dir", "npm", "package.json")},
Technology: coreutils.Npm,
Target: filepath.Join(dir, "dir", "npm"),
Descriptors: []string{filepath.Join(dir, "dir", "npm", "package.json")},
},
{
Technology: coreutils.Go,
WorkingDirectory: filepath.Join(dir, "dir", "go"),
Descriptors: []string{filepath.Join(dir, "dir", "go", "go.mod")},
Technology: coreutils.Go,
Target: filepath.Join(dir, "dir", "go"),
Descriptors: []string{filepath.Join(dir, "dir", "go", "go.mod")},
},
},
},
Expand All @@ -167,43 +167,43 @@ func TestGetScaScansToPreform(t *testing.T) {
},
expected: []*xrayutils.ScaScanResult{
{
Technology: coreutils.Maven,
WorkingDirectory: filepath.Join(dir, "dir", "maven"),
Technology: coreutils.Maven,
Target: filepath.Join(dir, "dir", "maven"),
Descriptors: []string{
filepath.Join(dir, "dir", "maven", "pom.xml"),
filepath.Join(dir, "dir", "maven", "maven-sub", "pom.xml"),
filepath.Join(dir, "dir", "maven", "maven-sub2", "pom.xml"),
},
},
{
Technology: coreutils.Npm,
WorkingDirectory: filepath.Join(dir, "dir", "npm"),
Descriptors: []string{filepath.Join(dir, "dir", "npm", "package.json")},
Technology: coreutils.Npm,
Target: filepath.Join(dir, "dir", "npm"),
Descriptors: []string{filepath.Join(dir, "dir", "npm", "package.json")},
},
{
Technology: coreutils.Go,
WorkingDirectory: filepath.Join(dir, "dir", "go"),
Descriptors: []string{filepath.Join(dir, "dir", "go", "go.mod")},
Technology: coreutils.Go,
Target: filepath.Join(dir, "dir", "go"),
Descriptors: []string{filepath.Join(dir, "dir", "go", "go.mod")},
},
{
Technology: coreutils.Yarn,
WorkingDirectory: filepath.Join(dir, "yarn"),
Descriptors: []string{filepath.Join(dir, "yarn", "package.json")},
Technology: coreutils.Yarn,
Target: filepath.Join(dir, "yarn"),
Descriptors: []string{filepath.Join(dir, "yarn", "package.json")},
},
{
Technology: coreutils.Pip,
WorkingDirectory: filepath.Join(dir, "yarn", "Pip"),
Descriptors: []string{filepath.Join(dir, "yarn", "Pip", "requirements.txt")},
Technology: coreutils.Pip,
Target: filepath.Join(dir, "yarn", "Pip"),
Descriptors: []string{filepath.Join(dir, "yarn", "Pip", "requirements.txt")},
},
{
Technology: coreutils.Pipenv,
WorkingDirectory: filepath.Join(dir, "yarn", "Pipenv"),
Descriptors: []string{filepath.Join(dir, "yarn", "Pipenv", "Pipfile")},
Technology: coreutils.Pipenv,
Target: filepath.Join(dir, "yarn", "Pipenv"),
Descriptors: []string{filepath.Join(dir, "yarn", "Pipenv", "Pipfile")},
},
{
Technology: coreutils.Nuget,
WorkingDirectory: filepath.Join(dir, "Nuget"),
Descriptors: []string{filepath.Join(dir, "Nuget", "project.sln"), filepath.Join(dir, "Nuget", "Nuget-sub", "project.csproj")},
Technology: coreutils.Nuget,
Target: filepath.Join(dir, "Nuget"),
Descriptors: []string{filepath.Join(dir, "Nuget", "project.sln"), filepath.Join(dir, "Nuget", "Nuget-sub", "project.csproj")},
},
},
},
Expand Down
8 changes: 6 additions & 2 deletions commands/scan/buildscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scan

import (
"errors"
"fmt"

"github.com/jfrog/jfrog-cli-core/v2/common/build"
outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format"
Expand Down Expand Up @@ -130,7 +131,7 @@ func (bsc *BuildScanCommand) runBuildScanAndPrintResults(xrayManager *xray.XrayS

scanResults := xrutils.NewAuditResults()
scanResults.XrayVersion = xrayVersion
scanResults.ScaResults = []xrutils.ScaScanResult{{XrayResults: scanResponse}}
scanResults.ScaResults = []xrutils.ScaScanResult{{Target: fmt.Sprintf("%s (%s)", params.BuildName, params.BuildNumber), XrayResults: scanResponse}}

resultsPrinter := xrutils.NewResultsWriter(scanResults).
SetOutputFormat(bsc.outputFormat).
Expand All @@ -143,7 +144,9 @@ func (bsc *BuildScanCommand) runBuildScanAndPrintResults(xrayManager *xray.XrayS

if bsc.outputFormat != outputFormat.Table {
// Print the violations and/or vulnerabilities as part of one JSON.
err = resultsPrinter.PrintScanResults()
if err = resultsPrinter.PrintScanResults(); err != nil {
return
}
} else {
// Print two different tables for violations and vulnerabilities (if needed)

Expand All @@ -160,6 +163,7 @@ func (bsc *BuildScanCommand) runBuildScanAndPrintResults(xrayManager *xray.XrayS
}
}
}
err = utils.RecordSecurityCommandOutput(utils.ScanCommandSummaryResult{Results: scanResults.GetSummary(), Section: utils.Build})
return
}

Expand Down
26 changes: 18 additions & 8 deletions commands/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ import (
type FileContext func(string) parallel.TaskFunc
type indexFileHandlerFunc func(file string)

type ScanInfo struct {
Target string
Result *services.ScanResponse
}

const (
BypassArchiveLimitsMinXrayVersion = "3.59.0"
indexingCommand = "graph"
Expand Down Expand Up @@ -213,7 +218,7 @@ func (scanCmd *ScanCommand) Run() (err error) {
}

// resultsArr is a two-dimensional array. Each array in it contains a list of ScanResponses that were requested and collected by a specific thread.
resultsArr := make([][]*services.ScanResponse, threads)
resultsArr := make([][]*ScanInfo, threads)
fileProducerConsumer := parallel.NewRunner(scanCmd.threads, 20000, false)
fileProducerErrors := make([][]formats.SimpleJsonError, threads)
indexedFileProducerConsumer := parallel.NewRunner(scanCmd.threads, 20000, false)
Expand All @@ -225,10 +230,10 @@ func (scanCmd *ScanCommand) Run() (err error) {
scanCmd.performScanTasks(fileProducerConsumer, indexedFileProducerConsumer)

// Handle results
flatResults := []services.ScanResponse{}
flatResults := []xrutils.ScaScanResult{}
for _, arr := range resultsArr {
for _, res := range arr {
flatResults = append(flatResults, *res)
flatResults = append(flatResults, xrutils.ScaScanResult{Target: res.Target, XrayResults: []services.ScanResponse{*res.Result}})
}
}
if scanCmd.progress != nil {
Expand All @@ -248,7 +253,7 @@ func (scanCmd *ScanCommand) Run() (err error) {

scanResults := xrutils.NewAuditResults()
scanResults.XrayVersion = xrayVersion
scanResults.ScaResults = []xrutils.ScaScanResult{{XrayResults: flatResults}}
scanResults.ScaResults = flatResults

if err = xrutils.NewResultsWriter(scanResults).
SetOutputFormat(scanCmd.outputFormat).
Expand All @@ -264,10 +269,15 @@ func (scanCmd *ScanCommand) Run() (err error) {
if err != nil {
return err
}

if err = utils.RecordSecurityCommandOutput(utils.ScanCommandSummaryResult{Results: scanResults.GetSummary(), Section: utils.Binary}); err != nil {
return err
}

// If includeVulnerabilities is false it means that context was provided, so we need to check for build violations.
// If user provided --fail=false, don't fail the build.
if scanCmd.fail && !scanCmd.includeVulnerabilities {
if xrutils.CheckIfFailBuild(flatResults) {
if xrutils.CheckIfFailBuild(scanResults.GetScaScansXrayResults()) {
return xrutils.NewFailBuildError()
}
}
Expand All @@ -286,7 +296,7 @@ func (scanCmd *ScanCommand) CommandName() string {
return "xr_scan"
}

func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, resultsArr [][]*services.ScanResponse, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, fileCollectingErrorsQueue *clientutils.ErrorsQueue, xrayVersion string) {
func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer parallel.Runner, 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.
Expand All @@ -305,7 +315,7 @@ func (scanCmd *ScanCommand) prepareScanTasks(fileProducer, indexedFileProducer p
}()
}

func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFileProducer parallel.Runner, resultsArr [][]*services.ScanResponse, fileErrors, indexedFileErrors [][]formats.SimpleJsonError, xrayVersion string) FileContext {
func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, 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)
Expand Down Expand Up @@ -355,7 +365,7 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, indexedFil
indexedFileErrors[threadId] = append(indexedFileErrors[threadId], formats.SimpleJsonError{FilePath: filePath, ErrorMessage: err.Error()})
return
}
resultsArr[threadId] = append(resultsArr[threadId], scanResults)
resultsArr[threadId] = append(resultsArr[threadId], &ScanInfo{Target: filePath, Result: scanResults})
return
}

Expand Down
111 changes: 111 additions & 0 deletions formats/summary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package formats

type SummaryResults struct {
Scans []ScanSummaryResult `json:"scans"`
}

func (sr SummaryResults) GetTotalIssueCount() (total int) {
for _, scan := range sr.Scans {
total += scan.GetTotalIssueCount()
}
return
}

type ScanSummaryResult struct {
Target string `json:"target,omitempty"`
ScaScanResults *ScaSummaryCount `json:"sca,omitempty"`
IacScanResults *SummaryCount `json:"iac,omitempty"`
SecretsScanResults *SummaryCount `json:"secrets,omitempty"`
SastScanResults *SummaryCount `json:"sast,omitempty"`
}

type SummarySubScanType string

const (
ScaScan SummarySubScanType = "SCA"
IacScan SummarySubScanType = "IAC"
SecretsScan SummarySubScanType = "Secrets"
SastScan SummarySubScanType = "SAST"
)

// Severity -> Count
type SummaryCount map[string]int

func (sc SummaryCount) GetTotal() int {
total := 0
for _, count := range sc {
total += count
}
return total
}

// Severity -> Applicable status -> Count
type ScaSummaryCount map[string]SummaryCount

func (sc ScaSummaryCount) GetTotal() (total int) {
for _, count := range sc {
total += count.GetTotal()
}
return
}

func (sc ScaSummaryCount) GetSeverityCountsWithoutStatus() (severityCounts SummaryCount) {
severityCounts = SummaryCount{}
for severity, statusCounts := range sc {
for _, count := range statusCounts {
severityCounts[severity] += count
}
}
return
}

func (s *ScanSummaryResult) HasIssues() bool {
return s.GetTotalIssueCount() > 0
}

func (s *ScanSummaryResult) GetTotalIssueCount() (total int) {
if s.ScaScanResults != nil {
total += s.ScaScanResults.GetTotal()
}
if s.IacScanResults != nil {
total += s.IacScanResults.GetTotal()
}
if s.SecretsScanResults != nil {
total += s.SecretsScanResults.GetTotal()
}
if s.SastScanResults != nil {
total += s.SastScanResults.GetTotal()
}
return
}

func (s *ScanSummaryResult) GetSubScansWithIssues() []SummarySubScanType {
subScans := []SummarySubScanType{}
if s.SecretsScanResults != nil && s.SecretsScanResults.GetTotal() > 0 {
subScans = append(subScans, SecretsScan)
}
if s.SastScanResults != nil && s.SastScanResults.GetTotal() > 0 {
subScans = append(subScans, SastScan)
}
if s.IacScanResults != nil && s.IacScanResults.GetTotal() > 0 {
subScans = append(subScans, IacScan)
}
if s.ScaScanResults != nil && s.ScaScanResults.GetTotal() > 0 {
subScans = append(subScans, ScaScan)
}
return subScans
}

func (s *ScanSummaryResult) GetSubScanTotalIssueCount(subScanType SummarySubScanType) (count int) {
switch subScanType {
case ScaScan:
count = s.ScaScanResults.GetTotal()
case IacScan:
count = s.IacScanResults.GetTotal()
case SecretsScan:
count = s.SecretsScanResults.GetTotal()
case SastScan:
count = s.SastScanResults.GetTotal()
}
return
}
Loading

0 comments on commit 3e9fe2d

Please sign in to comment.