Skip to content

Commit

Permalink
Improve audit data handling (#936)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored Sep 10, 2023
1 parent 1ae3674 commit 6e94c82
Show file tree
Hide file tree
Showing 24 changed files with 1,031 additions and 903 deletions.
1 change: 1 addition & 0 deletions xray/commands/audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ func RunAudit(auditParams *AuditParams) (results *Results, err error) {
if err = clientutils.ValidateMinimumVersion(clientutils.Xray, auditParams.xrayVersion, scangraph.GraphScanMinXrayVersion); err != nil {
return
}
results.ExtendedScanResults.XrayVersion = auditParams.xrayVersion
results.ExtendedScanResults.EntitledForJas, err = isEntitledForJas(xrayManager, auditParams.xrayVersion)
if err != nil {
return
Expand Down
53 changes: 8 additions & 45 deletions xray/commands/audit/jas/applicability/applicabilitymanager.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package applicability

import (
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas"
"path/filepath"
"strings"

"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas"

"github.com/jfrog/gofrog/datastructures"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-core/v2/xray/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"github.com/jfrog/jfrog-client-go/xray/services"
"github.com/owenrumney/go-sarif/v2/sarif"
Expand All @@ -22,7 +21,7 @@ const (
)

type ApplicabilityScanManager struct {
applicabilityScanResults map[string]utils.ApplicabilityStatus
applicabilityScanResults []*sarif.Run
directDependenciesCves []string
xrayResults []services.ScanResponse
scanner *jas.JasScanner
Expand All @@ -38,7 +37,7 @@ 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) (results map[string]utils.ApplicabilityStatus, err error) {
scannedTechnologies []coreutils.Technology, scanner *jas.JasScanner) (results []*sarif.Run, err error) {
applicabilityScanManager := newApplicabilityScanManager(xrayResults, directDependencies, scanner)
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 direct dependencies. Skipping....")
Expand All @@ -55,7 +54,7 @@ func RunApplicabilityScan(xrayResults []services.ScanResponse, directDependencie
func newApplicabilityScanManager(xrayScanResults []services.ScanResponse, directDependencies []string, scanner *jas.JasScanner) (manager *ApplicabilityScanManager) {
directDependenciesCves := extractDirectDependenciesCvesFromScan(xrayScanResults, directDependencies)
return &ApplicabilityScanManager{
applicabilityScanResults: map[string]utils.ApplicabilityStatus{},
applicabilityScanResults: []*sarif.Run{},
directDependenciesCves: directDependenciesCves,
xrayResults: xrayScanResults,
scanner: scanner,
Expand Down Expand Up @@ -111,13 +110,11 @@ func (asm *ApplicabilityScanManager) Run(wd string) (err error) {
if err = asm.runAnalyzerManager(); err != nil {
return
}
var workingDirResults map[string]utils.ApplicabilityStatus
if workingDirResults, err = asm.getScanResults(); err != nil {
workingDirResults, err := jas.ReadJasScanRunsFromFile(asm.scanner.ResultsFileName, wd)
if err != nil {
return
}
for cve, result := range workingDirResults {
asm.applicabilityScanResults[cve] = result
}
asm.applicabilityScanResults = append(asm.applicabilityScanResults, workingDirResults...)
return
}

Expand Down Expand Up @@ -163,37 +160,3 @@ func (asm *ApplicabilityScanManager) createConfigFile(workingDir string) error {
func (asm *ApplicabilityScanManager) runAnalyzerManager() error {
return asm.scanner.AnalyzerManager.Exec(asm.scanner.ConfigFileName, applicabilityScanCommand, filepath.Dir(asm.scanner.AnalyzerManager.AnalyzerManagerFullPath), asm.scanner.ServerDetails)
}

func (asm *ApplicabilityScanManager) getScanResults() (applicabilityResults map[string]utils.ApplicabilityStatus, err error) {
applicabilityResults = make(map[string]utils.ApplicabilityStatus, len(asm.directDependenciesCves))
for _, cve := range asm.directDependenciesCves {
applicabilityResults[cve] = utils.ApplicabilityUndetermined
}

report, err := sarif.Open(asm.scanner.ResultsFileName)
if errorutils.CheckError(err) != nil || len(report.Runs) == 0 {
return
}
// Applicability results contains one run only
for _, sarifResult := range report.Runs[0].Results {
cve := getCveFromRuleId(*sarifResult.RuleID)
if _, exists := applicabilityResults[cve]; !exists {
err = errorutils.CheckErrorf("received unexpected CVE: '%s' from RuleID: '%s' that does not exists on the requested CVEs list", cve, *sarifResult.RuleID)
return
}
applicabilityResults[cve] = resultKindToApplicabilityStatus(sarifResult.Kind)
}
return
}

// Gets a result of one CVE from the scanner, and returns true if the CVE is applicable, false otherwise
func resultKindToApplicabilityStatus(kind *string) utils.ApplicabilityStatus {
if !(kind != nil && *kind == "pass") {
return utils.Applicable
}
return utils.NotApplicable
}

func getCveFromRuleId(sarifRuleId string) string {
return strings.TrimPrefix(sarifRuleId, "applic_")
}
35 changes: 16 additions & 19 deletions xray/commands/audit/jas/applicability/applicabilitymanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package applicability
import (
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/jas"
"github.com/jfrog/jfrog-cli-core/v2/xray/utils"
"github.com/jfrog/jfrog-client-go/xray/services"
"github.com/stretchr/testify/assert"
"os"
Expand Down Expand Up @@ -276,13 +275,12 @@ func TestParseResults_EmptyResults_AllCvesShouldGetUnknown(t *testing.T) {
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "empty-results.sarif")

// Act
results, err := applicabilityManager.getScanResults()
var err error
applicabilityManager.applicabilityScanResults, err = jas.ReadJasScanRunsFromFile(applicabilityManager.scanner.ResultsFileName, scanner.WorkingDirs[0])

// Assert
assert.NoError(t, err)
assert.Equal(t, 5, len(results))
for _, cveResult := range results {
assert.Equal(t, utils.ApplicabilityUndetermined, cveResult)
if assert.NoError(t, err) {
assert.Len(t, applicabilityManager.applicabilityScanResults, 1)
assert.Empty(t, applicabilityManager.applicabilityScanResults[0].Results)
}
}

Expand All @@ -294,13 +292,13 @@ func TestParseResults_ApplicableCveExist(t *testing.T) {
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "applicable-cve-results.sarif")

// Act
results, err := applicabilityManager.getScanResults()
var err error
applicabilityManager.applicabilityScanResults, err = jas.ReadJasScanRunsFromFile(applicabilityManager.scanner.ResultsFileName, scanner.WorkingDirs[0])

// Assert
assert.NoError(t, err)
assert.Equal(t, 5, len(results))
assert.Equal(t, utils.Applicable, results["testCve1"])
assert.Equal(t, utils.NotApplicable, results["testCve3"])
if assert.NoError(t, err) && assert.NotNil(t, applicabilityManager.applicabilityScanResults) {
assert.Len(t, applicabilityManager.applicabilityScanResults, 1)
assert.NotEmpty(t, applicabilityManager.applicabilityScanResults[0].Results)
}
}

func TestParseResults_AllCvesNotApplicable(t *testing.T) {
Expand All @@ -311,12 +309,11 @@ func TestParseResults_AllCvesNotApplicable(t *testing.T) {
applicabilityManager.scanner.ResultsFileName = filepath.Join(jas.GetTestDataPath(), "applicability-scan", "no-applicable-cves-results.sarif")

// Act
results, err := applicabilityManager.getScanResults()
var err error
applicabilityManager.applicabilityScanResults, err = jas.ReadJasScanRunsFromFile(applicabilityManager.scanner.ResultsFileName, scanner.WorkingDirs[0])

// Assert
assert.NoError(t, err)
assert.Equal(t, 5, len(results))
for _, cveResult := range results {
assert.Equal(t, utils.NotApplicable, cveResult)
if assert.NoError(t, err) && assert.NotNil(t, applicabilityManager.applicabilityScanResults) {
assert.Len(t, applicabilityManager.applicabilityScanResults, 1)
assert.NotEmpty(t, applicabilityManager.applicabilityScanResults[0].Results)
}
}
91 changes: 51 additions & 40 deletions xray/commands/audit/jas/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package jas

import (
"errors"
"os"
"path/filepath"
"strings"
"testing"

rtutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
Expand All @@ -12,14 +17,19 @@ import (
"github.com/owenrumney/go-sarif/v2/sarif"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
"strings"
"testing"
)

var (
SkippedDirs = []string{"**/*test*/**", "**/*venv*/**", "**/*node_modules*/**", "**/*target*/**"}

mapSeverityToScore = map[string]string{
"": "0.0",
"unknown": "0.0",
"low": "3.9",
"medium": "6.9",
"high": "8.9",
"critical": "10",
}
)

type JasScanner struct {
Expand Down Expand Up @@ -88,46 +98,54 @@ func deleteJasProcessFiles(configFile string, resultFile string) error {
return errorutils.CheckError(err)
}

func GetSourceCodeScanResults(resultsFileName, workingDir string, scanType utils.JasScanType) (results []utils.SourceCodeScanResult, err error) {
// Read Sarif format results generated from the Jas scanner
report, err := sarif.Open(resultsFileName)
if errorutils.CheckError(err) != nil {
return nil, err
}
var sarifResults []*sarif.Result
if len(report.Runs) > 0 {
// Jas scanners returns results in a single run entry
sarifResults = report.Runs[0].Results
func ReadJasScanRunsFromFile(fileName, wd string) (sarifRuns []*sarif.Run, err error) {
if sarifRuns, err = utils.ReadScanRunsFromFile(fileName); err != nil {
return
}
resultPointers := convertSarifResultsToSourceCodeScanResults(sarifResults, workingDir, scanType)
for _, res := range resultPointers {
results = append(results, *res)
for _, sarifRun := range sarifRuns {
// Jas reports has only one invocation
// Set the actual working directory to the invocation, not the analyzerManager directory
// Also used to calculate relative paths if needed with it
sarifRun.Invocations[0].WorkingDirectory.WithUri(wd)
// Process runs values
sarifRun.Results = excludeSuppressResults(sarifRun.Results)
addScoreToRunRules(sarifRun)
}
return results, nil
return
}

func convertSarifResultsToSourceCodeScanResults(sarifResults []*sarif.Result, workingDir string, scanType utils.JasScanType) []*utils.SourceCodeScanResult {
var sourceCodeScanResults []*utils.SourceCodeScanResult
func excludeSuppressResults(sarifResults []*sarif.Result) []*sarif.Result {
results := []*sarif.Result{}
for _, sarifResult := range sarifResults {
// Describes a request to “suppress” a result (to exclude it from result lists)
if len(sarifResult.Suppressions) > 0 {
// Describes a request to “suppress” a result (to exclude it from result lists)
continue
}
// Convert
currentResult := utils.GetResultIfExists(sarifResult, workingDir, sourceCodeScanResults)
if currentResult == nil {
currentResult = utils.ConvertSarifResultToSourceCodeScanResult(sarifResult, workingDir)
// Set specific Jas scan attributes
if scanType == utils.Secrets {
currentResult.Text = hideSecret(utils.GetResultLocationSnippet(sarifResult.Locations[0]))
results = append(results, sarifResult)
}
return results
}

func addScoreToRunRules(sarifRun *sarif.Run) {
for _, sarifResult := range sarifRun.Results {
if rule, err := sarifRun.GetRuleById(*sarifResult.RuleID); err == nil {
// Add to the rule security-severity score based on results severity
score := convertToScore(utils.GetResultSeverity(sarifResult))
if score != utils.MissingCveScore {
if rule.Properties == nil {
rule.WithProperties(sarif.NewPropertyBag().Properties)
}
rule.Properties["security-severity"] = score
}
sourceCodeScanResults = append(sourceCodeScanResults, currentResult)
}
if scanType == utils.Sast {
currentResult.CodeFlow = append(currentResult.CodeFlow, utils.GetResultCodeFlows(sarifResult, workingDir)...)
}
}
return sourceCodeScanResults
}

func convertToScore(severity string) string {
if level, ok := mapSeverityToScore[strings.ToLower(severity)]; ok {
return level
}
return ""
}

func CreateScannersConfigFile(fileName string, fileContent interface{}) error {
Expand All @@ -139,13 +157,6 @@ func CreateScannersConfigFile(fileName string, fileContent interface{}) error {
return errorutils.CheckError(err)
}

func hideSecret(secret string) string {
if len(secret) <= 3 {
return "***"
}
return secret[:3] + strings.Repeat("*", 12)
}

var FakeServerDetails = config.ServerDetails{
Url: "platformUrl",
Password: "password",
Expand Down
23 changes: 0 additions & 23 deletions xray/commands/audit/jas/common_test.go

This file was deleted.

13 changes: 7 additions & 6 deletions xray/commands/audit/jas/iac/iacscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

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

const (
Expand All @@ -14,7 +15,7 @@ const (
)

type IacScanManager struct {
iacScannerResults []utils.SourceCodeScanResult
iacScannerResults []*sarif.Run
scanner *jas.JasScanner
}

Expand All @@ -26,23 +27,23 @@ type IacScanManager struct {
// []utils.SourceCodeScanResult: a list of the iac violations that were found.
// bool: true if the user is entitled to iac scan, false otherwise.
// error: An error object (if any).
func RunIacScan(scanner *jas.JasScanner) (results []utils.SourceCodeScanResult, err error) {
func RunIacScan(scanner *jas.JasScanner) (results []*sarif.Run, err error) {
iacScanManager := newIacScanManager(scanner)
log.Info("Running IaC scanning...")
if err = iacScanManager.scanner.Run(iacScanManager); err != nil {
err = utils.ParseAnalyzerManagerError(utils.IaC, err)
return
}
if len(iacScanManager.iacScannerResults) > 0 {
log.Info("Found", len(iacScanManager.iacScannerResults), "IaC vulnerabilities")
log.Info("Found", utils.GetResultsLocationCount(iacScanManager.iacScannerResults...), "IaC vulnerabilities")
}
results = iacScanManager.iacScannerResults
return
}

func newIacScanManager(scanner *jas.JasScanner) (manager *IacScanManager) {
return &IacScanManager{
iacScannerResults: []utils.SourceCodeScanResult{},
iacScannerResults: []*sarif.Run{},
scanner: scanner,
}
}
Expand All @@ -55,8 +56,8 @@ func (iac *IacScanManager) Run(wd string) (err error) {
if err = iac.runAnalyzerManager(); err != nil {
return
}
var workingDirResults []utils.SourceCodeScanResult
if workingDirResults, err = jas.GetSourceCodeScanResults(scanner.ResultsFileName, wd, utils.IaC); err != nil {
workingDirResults, err := jas.ReadJasScanRunsFromFile(scanner.ResultsFileName, wd)
if err != nil {
return
}
iac.iacScannerResults = append(iac.iacScannerResults, workingDirResults...)
Expand Down
Loading

0 comments on commit 6e94c82

Please sign in to comment.