diff --git a/commands/audit/audit.go b/commands/audit/audit.go index 19f2788f..93c51b40 100644 --- a/commands/audit/audit.go +++ b/commands/audit/audit.go @@ -3,6 +3,7 @@ package audit import ( "errors" "fmt" + "github.com/jfrog/jfrog-cli-security/jas/maliciouscode" "strings" "github.com/jfrog/gofrog/parallel" @@ -195,7 +196,7 @@ func (auditCmd *AuditCommand) Run() (err error) { func (auditCmd *AuditCommand) getResultWriter(cmdResults *results.SecurityCommandResults) *output.ResultsWriter { var messages []string if !cmdResults.EntitledForJas { - messages = []string{coreutils.PrintTitle("The ‘jf audit’ command also supports JFrog Advanced Security features, such as 'Contextual Analysis', 'Secret Detection', 'IaC Scan' and ‘SAST’.\nThis feature isn't enabled on your system. Read more - ") + coreutils.PrintLink(utils.JasInfoURL)} + messages = []string{coreutils.PrintTitle("The ‘jf audit’ command also supports JFrog Advanced Security features, such as 'Contextual Analysis', 'Secret Detection', 'Malicious Code Scan', 'IaC Scan' and ‘SAST’.\nThis feature isn't enabled on your system. Read more - ") + coreutils.PrintLink(utils.JasInfoURL)} } return output.NewResultsWriter(cmdResults). SetOutputFormat(auditCmd.OutputFormat()). @@ -344,6 +345,7 @@ func createJasScansTasks(auditParallelRunner *utils.SecurityParallelRunner, scan ConfigProfile: auditParams.configProfile, ScansToPerform: auditParams.ScansToPerform(), SecretsScanType: secrets.SecretsScannerType, + MaliciousScanType: maliciouscode.MaliciousScannerType, DirectDependencies: auditParams.DirectDependencies(), ThirdPartyApplicabilityScan: auditParams.thirdPartyApplicabilityScan, ApplicableScanType: applicability.ApplicabilityScannerType, diff --git a/commands/scan/scan.go b/commands/scan/scan.go index c7ea0227..010a4409 100644 --- a/commands/scan/scan.go +++ b/commands/scan/scan.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/jfrog/jfrog-cli-security/jas/maliciouscode" "os/exec" "path/filepath" "regexp" @@ -495,6 +496,7 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, cmdResults Module: module, ScansToPerform: utils.GetAllSupportedScans(), SecretsScanType: secrets.SecretsScannerDockerScanType, + MaliciousScanType: maliciouscode.MaliciousScannerDockerScanType, DirectDependencies: directDepsListFromVulnerabilities(*graphScanResults), ApplicableScanType: applicability.ApplicabilityDockerScanScanType, ScanResults: targetResults, diff --git a/go.mod b/go.mod index cbc6df4e..892ef664 100644 --- a/go.mod +++ b/go.mod @@ -111,7 +111,7 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect ) -// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go dev +replace github.com/jfrog/jfrog-client-go => github.com/barv-jfrog/jfrog-client-go v0.0.0-20250128142448-cc5443a7de6c replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250128111343-44586261061e @@ -120,3 +120,5 @@ replace github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-art // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev // replace github.com/jfrog/froggit-go => github.com/jfrog/froggit-go dev + +replace github.com/jfrog/jfrog-apps-config => github.com/barv-jfrog/jfrog-apps-config v0.0.0-20250128142442-6fd49006bb85 diff --git a/go.sum b/go.sum index 10d264a8..5b83ffc0 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/barv-jfrog/jfrog-apps-config v0.0.0-20250128142442-6fd49006bb85 h1:kNdHaJdeD1QBQ0czgrfKqrcND3T+6dInSI8s23lW4Cw= +github.com/barv-jfrog/jfrog-apps-config v0.0.0-20250128142442-6fd49006bb85/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= +github.com/barv-jfrog/jfrog-client-go v0.0.0-20250128142448-cc5443a7de6c h1:VX04S3y/jhMQqb6V7Am/Y0r8rgtJBJDyb2hgE7KfWUQ= +github.com/barv-jfrog/jfrog-client-go v0.0.0-20250128142448-cc5443a7de6c/go.mod h1:ohIfKpMBCQsE9kunrKQ1wvoExpqsPLaluRFO186B5EM= github.com/beevik/etree v1.4.0 h1:oz1UedHRepuY3p4N5OjE0nK1WLCqtzHf25bxplKOHLs= github.com/beevik/etree v1.4.0/go.mod h1:cyWiXwGoasx60gHvtnEh5x8+uIjUVnjWqBvEnhnqKDA= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= @@ -126,14 +130,10 @@ github.com/jfrog/froggit-go v1.16.2 h1:F//S83iXH14qsCwYzv0zB2JtjS2pJVEsUoEmYA+37 github.com/jfrog/froggit-go v1.16.2/go.mod h1:5VpdQfAcbuyFl9x/x8HGm7kVk719kEtW/8YJFvKcHPA= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= -github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY= -github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-artifactory v0.1.12-0.20250128042424-bb6a289e237c h1:9975QDk6vBZG7vjsKVCuQB56r4xxeoYHHjmTzxbd4H8= github.com/jfrog/jfrog-cli-artifactory v0.1.12-0.20250128042424-bb6a289e237c/go.mod h1:/sP5tyuFpH9WJp0+vztczuXcxIYHlMye0CDWCxhy4/M= github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250128111343-44586261061e h1:QieRnuB+YDGS8c5QIt5bcJblE8KnomnHn3fpP9bymI8= github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250128111343-44586261061e/go.mod h1:ALempEeph7JoZZS2ob7xK+0r9v82z8IBDzQB1hnaxuQ= -github.com/jfrog/jfrog-client-go v1.50.0 h1:t7v/zpLkPomHR6ZjVbPQ1WPQJd9IFKESK9Tt6phZz3k= -github.com/jfrog/jfrog-client-go v1.50.0/go.mod h1:xHxwKBjPSUBd/FyCWgusfHmSWKUZTkfOZkTmntC2F5Y= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= diff --git a/jas/maliciouscode/maliciouscodescanner.go b/jas/maliciouscode/maliciouscodescanner.go new file mode 100644 index 00000000..52dfca1a --- /dev/null +++ b/jas/maliciouscode/maliciouscodescanner.go @@ -0,0 +1,101 @@ +package maliciouscode + +import ( + clientutils "github.com/jfrog/jfrog-client-go/utils" + "path/filepath" + + jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" + "github.com/jfrog/jfrog-cli-security/jas" + "github.com/jfrog/jfrog-cli-security/utils" + "github.com/jfrog/jfrog-cli-security/utils/formats/sarifutils" + "github.com/jfrog/jfrog-cli-security/utils/jasutils" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/owenrumney/go-sarif/v2/sarif" +) + +const ( + maliciousScanCommand = "mal" + maliciousDocsUrlSuffix = "malicious" + + MaliciousScannerType MaliciousScanType = "malicious-scan" // #nosec + MaliciousScannerDockerScanType MaliciousScanType = "malicious-docker-scan" // #nosec +) + +type MaliciousScanType string + +type MaliciousScanManager struct { + scanner *jas.JasScanner + scanType MaliciousScanType + configFileName string + resultsFileName string +} + +// The getMaliciousScanResults function runs the malicious code scan flow, which includes the following steps: +// Creating an MaliciousSecretManager object. +// Running the analyzer manager executable. +// Parsing the analyzer manager results. +func RunMaliciousScan(scanner *jas.JasScanner, scanType MaliciousScanType, module jfrogappsconfig.Module, threadId int) (vulnerabilitiesResults []*sarif.Run, violationsResults []*sarif.Run, err error) { + var scannerTempDir string + if scannerTempDir, err = jas.CreateScannerTempDirectory(scanner, jasutils.MaliciousCode.String()); err != nil { + return + } + maliciousScanManager := newMaliciousScanManager(scanner, scanType, scannerTempDir) + log.Info(clientutils.GetLogMsgPrefix(threadId, false) + "Running Malicious code scan...") + if vulnerabilitiesResults, violationsResults, err = maliciousScanManager.scanner.Run(maliciousScanManager, module); err != nil { + return + } + log.Info(utils.GetScanFindingsLog(utils.MaliciousCodeScan, sarifutils.GetResultsLocationCount(vulnerabilitiesResults...), sarifutils.GetResultsLocationCount(violationsResults...), threadId)) + return +} + +func newMaliciousScanManager(scanner *jas.JasScanner, scanType MaliciousScanType, scannerTempDir string) (manager *MaliciousScanManager) { + return &MaliciousScanManager{ + scanner: scanner, + scanType: scanType, + configFileName: filepath.Join(scannerTempDir, "config.yaml"), + resultsFileName: filepath.Join(scannerTempDir, "results.sarif"), + } +} + +func (msm *MaliciousScanManager) Run(module jfrogappsconfig.Module) (vulnerabilitiesSarifRuns []*sarif.Run, violationsSarifRuns []*sarif.Run, err error) { + if err = msm.createConfigFile(module, msm.scanner.Exclusions...); err != nil { + return + } + if err = msm.runAnalyzerManager(); err != nil { + return + } + return jas.ReadJasScanRunsFromFile(msm.resultsFileName, module.SourceRoot, maliciousDocsUrlSuffix, msm.scanner.MinSeverity) +} + +type maliciousScanConfig struct { + Scans []maliciousScanConfiguration `yaml:"scans"` +} + +type maliciousScanConfiguration struct { + Roots []string `yaml:"roots"` + Output string `yaml:"output"` + Type string `yaml:"type"` + SkippedDirs []string `yaml:"skipped-folders"` +} + +func (m *MaliciousScanManager) createConfigFile(module jfrogappsconfig.Module, exclusions ...string) error { + roots, err := jas.GetSourceRoots(module, module.Scanners.MaliciousCode) + if err != nil { + return err + } + configFileContent := maliciousScanConfig{ + Scans: []maliciousScanConfiguration{ + { + Roots: roots, + Output: m.resultsFileName, + Type: string(m.scanType), + SkippedDirs: jas.GetExcludePatterns(module, module.Scanners.MaliciousCode, exclusions...), + }, + }, + } + return jas.CreateScannersConfigFile(m.configFileName, configFileContent, jasutils.MaliciousCode) +} + +func (m *MaliciousScanManager) runAnalyzerManager() error { + return m.scanner.AnalyzerManager.Exec(m.configFileName, maliciousScanCommand, filepath.Dir(m.scanner.AnalyzerManager.AnalyzerManagerFullPath), m.scanner.ServerDetails, m.scanner.EnvVars) +} diff --git a/jas/maliciouscode/maliciouscodescanner_test.go b/jas/maliciouscode/maliciouscodescanner_test.go new file mode 100644 index 00000000..e804cef9 --- /dev/null +++ b/jas/maliciouscode/maliciouscodescanner_test.go @@ -0,0 +1,118 @@ +package maliciouscode + +import ( + "os" + "path/filepath" + "testing" + + "github.com/jfrog/jfrog-cli-security/utils/jasutils" + "github.com/jfrog/jfrog-cli-security/utils/severityutils" + "github.com/stretchr/testify/require" + + jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" + "github.com/jfrog/jfrog-cli-security/jas" + + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + "github.com/stretchr/testify/assert" +) + +func TestNewMaliciousScanManager(t *testing.T) { + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + maliciousScanManager := newMaliciousScanManager(scanner, MaliciousScannerType, "temoDirPath") + + assert.NotEmpty(t, maliciousScanManager) + assert.NotEmpty(t, maliciousScanManager.configFileName) + assert.NotEmpty(t, maliciousScanManager.resultsFileName) + assert.Equal(t, &jas.FakeServerDetails, maliciousScanManager.scanner.ServerDetails) +} + +func TestMaliciousScan_CreateConfigFile_VerifyFileWasCreated(t *testing.T) { + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + + scannerTempDir, err := jas.CreateScannerTempDirectory(scanner, jasutils.MaliciousCode.String()) + require.NoError(t, err) + MaliciouscanManager := newMaliciousScanManager(scanner, MaliciousScannerType, scannerTempDir) + + currWd, err := coreutils.GetWorkingDirectory() + assert.NoError(t, err) + err = MaliciouscanManager.createConfigFile(jfrogappsconfig.Module{SourceRoot: currWd}) + assert.NoError(t, err) + + defer func() { + err = os.Remove(MaliciouscanManager.configFileName) + assert.NoError(t, err) + }() + + _, fileNotExistError := os.Stat(MaliciouscanManager.configFileName) + assert.NoError(t, fileNotExistError) + fileContent, err := os.ReadFile(MaliciouscanManager.configFileName) + assert.NoError(t, err) + assert.True(t, len(fileContent) > 0) +} + +func TestRunAnalyzerManager_ReturnsGeneralError(t *testing.T) { + defer func() { + os.Clearenv() + }() + + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + + MaliciouscanManager := newMaliciousScanManager(scanner, MaliciousScannerType, "temoDirPath") + assert.Error(t, MaliciouscanManager.runAnalyzerManager()) +} + +func TestParseResults_EmptyResults(t *testing.T) { + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + jfrogAppsConfigForTest, err := jas.CreateJFrogAppsConfig([]string{}) + assert.NoError(t, err) + // Arrange + MaliciouscanManager := newMaliciousScanManager(scanner, MaliciousScannerType, "temoDirPath") + MaliciouscanManager.resultsFileName = filepath.Join(jas.GetTestDataPath(), "Malicious-scan", "no-Malicious.sarif") + + // Act + vulnerabilitiesResults, _, err := jas.ReadJasScanRunsFromFile(MaliciouscanManager.resultsFileName, jfrogAppsConfigForTest.Modules[0].SourceRoot, maliciousDocsUrlSuffix, scanner.MinSeverity) + + // Assert + if assert.NoError(t, err) && assert.NotNil(t, vulnerabilitiesResults) { + assert.Len(t, vulnerabilitiesResults, 1) + assert.Empty(t, vulnerabilitiesResults[0].Results) + } + +} + +func TestParseResults_ResultsContainMalicious(t *testing.T) { + // Arrange + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + jfrogAppsConfigForTest, err := jas.CreateJFrogAppsConfig([]string{}) + assert.NoError(t, err) + + MaliciouscanManager := newMaliciousScanManager(scanner, MaliciousScannerType, "temoDirPath") + MaliciouscanManager.resultsFileName = filepath.Join(jas.GetTestDataPath(), "Malicious-scan", "contain-Malicious.sarif") + + // Act + vulnerabilitiesResults, _, err := jas.ReadJasScanRunsFromFile(MaliciouscanManager.resultsFileName, jfrogAppsConfigForTest.Modules[0].SourceRoot, maliciousDocsUrlSuffix, severityutils.Medium) + + // Assert + if assert.NoError(t, err) && assert.NotNil(t, vulnerabilitiesResults) { + assert.Len(t, vulnerabilitiesResults, 1) + assert.NotEmpty(t, vulnerabilitiesResults[0].Results) + } + assert.NoError(t, err) + +} + +func TestGetMaliciousScanResults_AnalyzerManagerReturnsError(t *testing.T) { + scanner, cleanUp := jas.InitJasTest(t) + defer cleanUp() + jfrogAppsConfigForTest, err := jas.CreateJFrogAppsConfig([]string{}) + assert.NoError(t, err) + vulnerabilitiesResults, _, err := RunMaliciousScan(scanner, MaliciousScannerType, jfrogAppsConfigForTest.Modules[0], 0) + assert.Error(t, err) + assert.ErrorContains(t, jas.ParseAnalyzerManagerError(jasutils.MaliciousCode, err), "failed to run MaliciousCode scan") + assert.Nil(t, vulnerabilitiesResults) +} diff --git a/jas/runner/jasrunner.go b/jas/runner/jasrunner.go index 534a2053..faa3e680 100644 --- a/jas/runner/jasrunner.go +++ b/jas/runner/jasrunner.go @@ -3,6 +3,7 @@ package runner import ( "errors" "fmt" + "github.com/jfrog/jfrog-cli-security/jas/maliciouscode" "github.com/jfrog/gofrog/parallel" jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go" @@ -33,6 +34,8 @@ type JasRunnerParams struct { ScansToPerform []utils.SubScanType + // Malicious code scan flags + MaliciousScanType maliciouscode.MaliciousScanType // Secret scan flags SecretsScanType secrets.SecretsScanType // Contextual Analysis scan flags @@ -51,7 +54,7 @@ func AddJasScannersTasks(params JasRunnerParams) (generalError error) { if params.Scanner.AnalyzerManager.AnalyzerManagerFullPath, generalError = jas.GetAnalyzerManagerExecutable(); generalError != nil { return fmt.Errorf("failed to set analyzer manager executable path: %s", generalError.Error()) } - // For docker scan we support only secrets and contextual scans. + // For docker scan we support only secrets, malicious code, and contextual scans. runAllScanners := false if params.ApplicableScanType == applicability.ApplicabilityScannerType || params.SecretsScanType == secrets.SecretsScannerType { runAllScanners = true @@ -66,6 +69,9 @@ func AddJasScannersTasks(params JasRunnerParams) (generalError error) { if generalError = addJasScanTaskForModuleIfNeeded(params, utils.SecretsScan, runSecretsScan(params.Runner, params.Scanner, params.ScanResults.JasResults, params.Module, params.SecretsScanType, params.TargetOutputDir)); generalError != nil { return } + if generalError = addJasScanTaskForModuleIfNeeded(params, utils.MaliciousCodeScan, runMaliciousScan(params.Runner, params.Scanner, params.ScanResults.JasResults, params.Module, params.MaliciousScanType, params.TargetOutputDir)); generalError != nil { + return + } if !runAllScanners { return } @@ -102,6 +108,8 @@ func addJasScanTaskForModuleIfNeeded(params JasRunnerParams, subScan utils.SubSc enabled = params.ConfigProfile.Modules[0].ScanConfig.IacScannerConfig.EnableIacScan case jasutils.Applicability: enabled = params.ConfigProfile.Modules[0].ScanConfig.EnableContextualAnalysisScan + case jasutils.MaliciousCode: + enabled = params.ConfigProfile.Modules[0].ScanConfig.MaliciousScannerConfig.EnableMaliciousScan } if enabled { generalError = addModuleJasScanTask(jasType, params.Runner, task, params.ScanResults, params.AllowPartialResults) @@ -145,6 +153,24 @@ func runSecretsScan(securityParallelRunner *utils.SecurityParallelRunner, scanne } } +func runMaliciousScan(securityParallelRunner *utils.SecurityParallelRunner, scanner *jas.JasScanner, extendedScanResults *results.JasScansResults, + module jfrogappsconfig.Module, maliciousScanType maliciouscode.MaliciousScanType, scansOutputDir string) parallel.TaskFunc { + return func(threadId int) (err error) { + defer func() { + securityParallelRunner.JasScannersWg.Done() + }() + vulnerabilitiesResults, violationsResults, err := maliciouscode.RunMaliciousScan(scanner, maliciousScanType, module, threadId) + securityParallelRunner.ResultsMu.Lock() + defer securityParallelRunner.ResultsMu.Unlock() + // We first add the scan results and only then check for errors, so we can store the exit code in order to report it in the end + extendedScanResults.AddJasScanResults(jasutils.MaliciousCode, vulnerabilitiesResults, violationsResults, jas.GetAnalyzerManagerExitCode(err)) + if err = jas.ParseAnalyzerManagerError(jasutils.MaliciousCode, err); err != nil { + return fmt.Errorf("%s%s", clientutils.GetLogMsgPrefix(threadId, false), err.Error()) + } + return dumpSarifRunToFileIfNeeded(scansOutputDir, jasutils.MaliciousCode, vulnerabilitiesResults, violationsResults) + } +} + func runIacScan(securityParallelRunner *utils.SecurityParallelRunner, scanner *jas.JasScanner, extendedScanResults *results.JasScansResults, module jfrogappsconfig.Module, scansOutputDir string) parallel.TaskFunc { return func(threadId int) (err error) { diff --git a/tests/testdata/other/malicious-scan/contain-malicious.sarif b/tests/testdata/other/malicious-scan/contain-malicious.sarif new file mode 100644 index 00000000..fb4350bd --- /dev/null +++ b/tests/testdata/other/malicious-scan/contain-malicious.sarif @@ -0,0 +1,235 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "JFrog Malicious Code scanner", + "rules": [ + { + "id": "entropy", + "shortDescription": { + "text": "Scanner for entropy" + } + } + ], + "version": "" + } + }, + "invocations": [ + { + "executionSuccessful": true, + "arguments": [ + "./jas_scanner", + "scan", + "mal_config_example.yaml" + ], + "workingDirectory": { + "uri": "malicious_scanner" + } + } + ], + "results": [ + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.nodejs/applicable_base64.js" + }, + "region": { + "endColumn": 118, + "endLine": 1, + "snippet": { + "text": "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" + }, + "startColumn": 18, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.nodejs/applicable_base64.js.approval.json" + }, + "region": { + "endColumn": 195, + "endLine": 1, + "snippet": { + "text": "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" + }, + "startColumn": 95, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.nodejs/applicable_hex.js" + }, + "region": { + "endColumn": 138, + "endLine": 1, + "snippet": { + "text": "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" + }, + "startColumn": 18, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.nodejs/applicable_hex.js.approval.json" + }, + "region": { + "endColumn": 215, + "endLine": 1, + "snippet": { + "text": "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" + }, + "startColumn": 95, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.python/applicable_base64.py" + }, + "region": { + "endColumn": 112, + "endLine": 1, + "snippet": { + "text": "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" + }, + "startColumn": 12, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.python/applicable_base64.py.approval.json" + }, + "region": { + "endColumn": 191, + "endLine": 1, + "snippet": { + "text": "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" + }, + "startColumn": 91, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.python/applicable_hex.py" + }, + "region": { + "endColumn": 132, + "endLine": 1, + "snippet": { + "text": "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" + }, + "startColumn": 12, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy" + }, + { + "message": { + "text": "Malicious files were found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://malicious_scanner/tests/req.malicious.python/applicable_hex.py.approval.json" + }, + "region": { + "endColumn": 211, + "endLine": 1, + "snippet": { + "text": "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" + }, + "startColumn": 91, + "startLine": 1 + } + } + } + ], + "ruleId": "entropy", + "suppressions": [ + { + "kind": "inSource" + } + ] + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/tests/testdata/other/malicious-scan/no-malicious.sarif b/tests/testdata/other/malicious-scan/no-malicious.sarif new file mode 100644 index 00000000..8b0ae50d --- /dev/null +++ b/tests/testdata/other/malicious-scan/no-malicious.sarif @@ -0,0 +1,29 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "JFrog Terraform scanner", + "rules": [], + "version": "" + } + }, + "invocations": [ + { + "executionSuccessful": true, + "arguments": [ + "mac_arm/secrets_scanner/secrets_scanner", + "scan", + "sec_config_example.yaml" + ], + "workingDirectory": { + "uri": "file:///am_versions_for_leap" + } + } + ], + "results": [] + } + ], + "version": "2.1.0", + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/utils/formats/conversion.go b/utils/formats/conversion.go index 20cfb208..5828f567 100644 --- a/utils/formats/conversion.go +++ b/utils/formats/conversion.go @@ -179,6 +179,20 @@ func ConvertToSecretsTableRow(rows []SourceCodeRow) (tableRows []secretsTableRow return } +func ConvertToMaliciousTableRow(rows []SourceCodeRow) (tableRows []maliciousTableRow) { + for i := range rows { + tableRows = append(tableRows, maliciousTableRow{ + severity: rows[i].Severity, + file: rows[i].File, + lineColumn: strconv.Itoa(rows[i].StartLine) + ":" + strconv.Itoa(rows[i].StartColumn), + evidence: rows[i].Snippet, + maliciousType: rows[i].Finding, + }) + + } + return +} + func ConvertToIacOrSastTableRow(rows []SourceCodeRow) (tableRows []iacOrSastTableRow) { for i := range rows { tableRows = append(tableRows, iacOrSastTableRow{ diff --git a/utils/formats/simplejsonapi.go b/utils/formats/simplejsonapi.go index 68d38f4e..058c2ff0 100644 --- a/utils/formats/simplejsonapi.go +++ b/utils/formats/simplejsonapi.go @@ -22,6 +22,8 @@ type SimpleJsonResults struct { SecretsViolations []SourceCodeRow `json:"secretsViolations"` IacsViolations []SourceCodeRow `json:"iacViolations"` SastViolations []SourceCodeRow `json:"sastViolations"` + MaliciousVulnerabilities []SourceCodeRow `json:"maliciousCode"` + MaliciousViolations []SourceCodeRow `json:"maliciousViolations"` Errors []SimpleJsonError `json:"errors"` Statuses ScanStatus `json:"scansStatus"` MultiScanId string `json:"multiScanId,omitempty"` @@ -34,6 +36,7 @@ type ScanStatus struct { IacStatusCode *int `json:"iacScanStatusCode,omitempty"` SecretsStatusCode *int `json:"secretsScanStatusCode,omitempty"` ApplicabilityStatusCode *int `json:"ContextualAnalysisScanStatusCode,omitempty"` + MaliciousStatusCode *int `json:"MaliciousStatusCode,omitempty"` } type ViolationContext struct { diff --git a/utils/formats/summary.go b/utils/formats/summary.go index 3adb4fee..f6df479e 100644 --- a/utils/formats/summary.go +++ b/utils/formats/summary.go @@ -9,6 +9,7 @@ const ( IacResult SummaryResultType = "IAC" SecretsResult SummaryResultType = "Secrets" SastResult SummaryResultType = "SAST" + MaliciousResult SummaryResultType = "MaliciousCode" ScaResult SummaryResultType = "SCA" ScaSecurityResult SummaryResultType = "Security" ScaLicenseResult SummaryResultType = "License" @@ -36,10 +37,11 @@ type ScanSummary struct { } type ScanResultSummary struct { - ScaResults *ScaScanResultSummary `json:"sca,omitempty"` - IacResults *ResultSummary `json:"iac,omitempty"` - SecretsResults *ResultSummary `json:"secrets,omitempty"` - SastResults *ResultSummary `json:"sast,omitempty"` + ScaResults *ScaScanResultSummary `json:"sca,omitempty"` + IacResults *ResultSummary `json:"iac,omitempty"` + SecretsResults *ResultSummary `json:"secrets,omitempty"` + SastResults *ResultSummary `json:"sast,omitempty"` + MaliciousResults *ResultSummary `json:"malicious_code,omitempty"` } type ScanViolationsSummary struct { @@ -184,6 +186,9 @@ func (srs *ScanResultSummary) GetTotal(filterTypes ...SummaryResultType) (total if srs.SastResults != nil && isFilterApply(SastResult, filterTypes) { total += srs.SastResults.GetTotal() } + if srs.MaliciousResults != nil && isFilterApply(MaliciousResult, filterTypes) { + total += srs.MaliciousResults.GetTotal() + } if srs.ScaResults == nil { return } @@ -229,6 +234,9 @@ func (ss *ScanResultSummary) GetSummaryBySeverity() (summary ResultSummary) { if ss.SastResults != nil { summary = MergeResultSummaries(summary, *ss.SastResults) } + if ss.MaliciousResults != nil { + summary = MergeResultSummaries(summary, *ss.MaliciousResults) + } return } @@ -306,6 +314,9 @@ func extractIssuesToSummary(issues *ScanResultSummary, destination *ScanResultSu if issues.SastResults != nil { destination.SastResults = mergeResultSummariesPointers(destination.SastResults, issues.SastResults) } + if issues.MaliciousResults != nil { + destination.MaliciousResults = mergeResultSummariesPointers(destination.MaliciousResults, issues.MaliciousResults) + } } func mergeResultSummariesPointers(summaries ...*ResultSummary) (merged *ResultSummary) { diff --git a/utils/formats/table.go b/utils/formats/table.go index c1662698..c1244592 100644 --- a/utils/formats/table.go +++ b/utils/formats/table.go @@ -21,6 +21,9 @@ type ResultsTables struct { // Secrets SecretsVulnerabilitiesTable []secretsTableRow SecretsViolationsTable []secretsTableRow + + MaliciousVulnerabilitiesTable []maliciousTableRow + MaliciousViolationsTable []maliciousTableRow } // Used for vulnerabilities and security violations @@ -155,6 +158,14 @@ type secretsTableRow struct { watch string `col-name:"Watch Name" omitempty:"true"` } +type maliciousTableRow struct { + severity string `col-name:"Severity"` + file string `col-name:"File"` + lineColumn string `col-name:"Line:Column"` + evidence string `col-name:"Evidence"` + maliciousType string `col-name:"Malicious Code Type"` +} + type iacOrSastTableRow struct { severity string `col-name:"Severity"` file string `col-name:"File"` diff --git a/utils/jasutils/jasutils.go b/utils/jasutils/jasutils.go index 3c2d11c8..425a722e 100644 --- a/utils/jasutils/jasutils.go +++ b/utils/jasutils/jasutils.go @@ -20,6 +20,7 @@ const ( Secrets JasScanType = "Secrets" IaC JasScanType = "IaC" Sast JasScanType = "Sast" + MaliciousCode JasScanType = "MaliciousCode" ) const ( @@ -39,7 +40,7 @@ func (jst JasScanType) String() string { } func GetJasScanTypes() []JasScanType { - return []JasScanType{Applicability, Secrets, IaC, Sast} + return []JasScanType{Applicability, Secrets, IaC, Sast, MaliciousCode} } func (tvs TokenValidationStatus) String() string { return string(tvs) } @@ -96,6 +97,8 @@ func SubScanTypeToJasScanType(subScanType utils.SubScanType) JasScanType { return Secrets case utils.ContextualAnalysisScan: return Applicability + case utils.MaliciousCodeScan: + return MaliciousCode } return "" } diff --git a/utils/results/conversion/convertor.go b/utils/results/conversion/convertor.go index 4e8f9b1b..8dcf56e5 100644 --- a/utils/results/conversion/convertor.go +++ b/utils/results/conversion/convertor.go @@ -56,6 +56,7 @@ type ResultsStreamFormatParser[T interface{}] interface { ParseLicenses(target results.ScanTarget, scaResponse results.ScanResult[services.ScanResponse]) error // Parse JAS content to the current scan target ParseSecrets(target results.ScanTarget, violations bool, secrets []results.ScanResult[[]*sarif.Run]) error + ParseMalicious(target results.ScanTarget, violations bool, maliciousFindings []results.ScanResult[[]*sarif.Run]) error ParseIacs(target results.ScanTarget, violations bool, iacs []results.ScanResult[[]*sarif.Run]) error ParseSast(target results.ScanTarget, violations bool, sast []results.ScanResult[[]*sarif.Run]) error // When done parsing the stream results, get the converted content @@ -190,6 +191,16 @@ func parseJasResults[T interface{}](params ResultConvertParams, parser ResultsSt }); err != nil { return } + // Parsing JAS Malicious code results + if err = parseJasScanResults(params, targetResults, cmdType, utils.MaliciousCodeScan, func(violations bool) error { + scanResults := targetResults.JasResults.JasVulnerabilities.MaliciousScanResults + if violations { + scanResults = targetResults.JasResults.JasViolations.MaliciousScanResults + } + return parser.ParseMalicious(targetResults.ScanTarget, violations, scanResults) + }); err != nil { + return + } // Parsing JAS SAST results return parseJasScanResults(params, targetResults, cmdType, utils.SastScan, func(violations bool) error { scanResults := targetResults.JasResults.JasVulnerabilities.SastScanResults diff --git a/utils/results/conversion/sarifparser/sarifparser.go b/utils/results/conversion/sarifparser/sarifparser.go index fdf90dd3..f470439b 100644 --- a/utils/results/conversion/sarifparser/sarifparser.go +++ b/utils/results/conversion/sarifparser/sarifparser.go @@ -62,10 +62,11 @@ type CmdResultsSarifConverter struct { type currentTargetRuns struct { currentTarget results.ScanTarget // Current run cache information, we combine vulnerabilities and violations in the same run - scaCurrentRun *sarif.Run - secretsCurrentRun *sarif.Run - iacCurrentRun *sarif.Run - sastCurrentRun *sarif.Run + scaCurrentRun *sarif.Run + secretsCurrentRun *sarif.Run + iacCurrentRun *sarif.Run + sastCurrentRun *sarif.Run + maliciousCurrentRun *sarif.Run } // Parse parameters for the SCA result @@ -155,6 +156,10 @@ func (sc *CmdResultsSarifConverter) flush() { if sc.currentTargetConvertedRuns.sastCurrentRun != nil { sc.current.Runs = append(sc.current.Runs, sc.currentTargetConvertedRuns.sastCurrentRun) } + // Flush malicious if needed + if sc.currentTargetConvertedRuns.maliciousCurrentRun != nil { + sc.current.Runs = append(sc.current.Runs, sc.currentTargetConvertedRuns.maliciousCurrentRun) + } sc.currentTargetConvertedRuns = nil } @@ -235,6 +240,14 @@ func (sc *CmdResultsSarifConverter) ParseSecrets(target results.ScanTarget, viol return } +func (sc *CmdResultsSarifConverter) ParseMalicious(target results.ScanTarget, violations bool, maliciousFindings []results.ScanResult[[]*sarif.Run]) (err error) { + if err = sc.validateBeforeParse(); err != nil || !sc.entitledForJas { + return + } + sc.currentTargetConvertedRuns.maliciousCurrentRun = combineJasRunsToCurrentRun(sc.currentTargetConvertedRuns.maliciousCurrentRun, patchRunsToPassIngestionRules(sc.baseJfrogUrl, sc.currentCmdType, utils.MaliciousCodeScan, sc.patchBinaryPaths, violations, target, results.ScanResultsToRuns(maliciousFindings)...)...) + return +} + func (sc *CmdResultsSarifConverter) ParseIacs(target results.ScanTarget, violations bool, iacs []results.ScanResult[[]*sarif.Run]) (err error) { if err = sc.validateBeforeParse(); err != nil || !sc.entitledForJas { return diff --git a/utils/results/conversion/simplejsonparser/simplejsonparser.go b/utils/results/conversion/simplejsonparser/simplejsonparser.go index ce017315..c6483dd4 100644 --- a/utils/results/conversion/simplejsonparser/simplejsonparser.go +++ b/utils/results/conversion/simplejsonparser/simplejsonparser.go @@ -157,6 +157,30 @@ func (sjc *CmdResultsSimpleJsonConverter) ParseSecrets(_ results.ScanTarget, isV return } +func (sjc *CmdResultsSimpleJsonConverter) ParseMalicious(_ results.ScanTarget, isViolationsResults bool, maliciousFindings []results.ScanResult[[]*sarif.Run]) (err error) { + if !sjc.entitledForJas { + return + } + if sjc.current == nil { + return results.ErrResetConvertor + } + for i := range maliciousFindings { + if shouldUpdateStatus(sjc.current.Statuses.MaliciousStatusCode, &maliciousFindings[i].StatusCode) { + sjc.current.Statuses.MaliciousStatusCode = &maliciousFindings[i].StatusCode + } + } + maliciousSimpleJson, err := PrepareSimpleJsonJasIssues(sjc.entitledForJas, sjc.pretty, results.ScanResultsToRuns(maliciousFindings)...) + if err != nil || len(maliciousSimpleJson) == 0 { + return + } + if isViolationsResults { + sjc.current.MaliciousViolations = append(sjc.current.MaliciousViolations, maliciousSimpleJson...) + } else { + sjc.current.MaliciousVulnerabilities = append(sjc.current.MaliciousVulnerabilities, maliciousSimpleJson...) + } + return +} + func (sjc *CmdResultsSimpleJsonConverter) ParseIacs(_ results.ScanTarget, isViolationsResults bool, iacs []results.ScanResult[[]*sarif.Run]) (err error) { if !sjc.entitledForJas { return diff --git a/utils/results/conversion/summaryparser/summaryparser.go b/utils/results/conversion/summaryparser/summaryparser.go index 04c8a88b..2a9f80f8 100644 --- a/utils/results/conversion/summaryparser/summaryparser.go +++ b/utils/results/conversion/summaryparser/summaryparser.go @@ -274,6 +274,25 @@ func (sc *CmdResultsSummaryConverter) ParseSecrets(_ results.ScanTarget, isViola return results.ApplyHandlerToJasIssues(results.ScanResultsToRuns(secrets), sc.entitledForJas, sc.getJasHandler(jasutils.Secrets, isViolationsResults)) } +func (sc *CmdResultsSummaryConverter) ParseMalicious(_ results.ScanTarget, isViolationsResults bool, maliciousFindings []results.ScanResult[[]*sarif.Run]) (err error) { + if !sc.entitledForJas || sc.currentScan.Vulnerabilities == nil { + // JAS results are only supported as vulnerabilities for now + return + } + if err = sc.validateBeforeParse(); err != nil { + return + } + if !isViolationsResults && sc.currentScan.Vulnerabilities.MaliciousResults == nil { + sc.currentScan.Vulnerabilities.MaliciousResults = &formats.ResultSummary{} + } + if isViolationsResults { + if sc.currentScan.Violations.MaliciousResults == nil { + sc.currentScan.Violations.MaliciousResults = &formats.ResultSummary{} + } + } + return results.ApplyHandlerToJasIssues(results.ScanResultsToRuns(maliciousFindings), sc.entitledForJas, sc.getJasHandler(jasutils.MaliciousCode, isViolationsResults)) +} + func (sc *CmdResultsSummaryConverter) ParseIacs(_ results.ScanTarget, isViolationsResults bool, iacs []results.ScanResult[[]*sarif.Run]) (err error) { if !sc.entitledForJas || sc.currentScan.Vulnerabilities == nil { // JAS results are only supported as vulnerabilities for now diff --git a/utils/results/conversion/tableparser/tableparser.go b/utils/results/conversion/tableparser/tableparser.go index cc6774d3..cc082d83 100644 --- a/utils/results/conversion/tableparser/tableparser.go +++ b/utils/results/conversion/tableparser/tableparser.go @@ -38,6 +38,8 @@ func (tc *CmdResultsTableConverter) Get() (formats.ResultsTables, error) { IacViolationsTable: formats.ConvertToIacOrSastTableRow(simpleJsonFormat.IacsViolations), SastVulnerabilitiesTable: formats.ConvertToIacOrSastTableRow(simpleJsonFormat.SastVulnerabilities), SastViolationsTable: formats.ConvertToIacOrSastTableRow(simpleJsonFormat.SastViolations), + MaliciousVulnerabilitiesTable: formats.ConvertToMaliciousTableRow(simpleJsonFormat.MaliciousVulnerabilities), + MaliciousViolationsTable: formats.ConvertToMaliciousTableRow(simpleJsonFormat.MaliciousViolations), }, nil } @@ -61,6 +63,10 @@ func (tc *CmdResultsTableConverter) ParseSecrets(target results.ScanTarget, isVi return tc.simpleJsonConvertor.ParseSecrets(target, isViolationsResults, secrets) } +func (tc *CmdResultsTableConverter) ParseMalicious(target results.ScanTarget, isViolationsResults bool, maliciousFindings []results.ScanResult[[]*sarif.Run]) (err error) { + return tc.simpleJsonConvertor.ParseMalicious(target, isViolationsResults, maliciousFindings) +} + func (tc *CmdResultsTableConverter) ParseIacs(target results.ScanTarget, isViolationsResults bool, iacs []results.ScanResult[[]*sarif.Run]) (err error) { return tc.simpleJsonConvertor.ParseIacs(target, isViolationsResults, iacs) } diff --git a/utils/results/output/resultwriter.go b/utils/results/output/resultwriter.go index df9311f5..52206114 100644 --- a/utils/results/output/resultwriter.go +++ b/utils/results/output/resultwriter.go @@ -195,6 +195,9 @@ func (rw *ResultsWriter) printTables() (err error) { if rw.shouldPrintSecretValidationExtraMessage() { log.Output("This table contains multiple secret types, such as tokens, generic password, ssh keys and more, token validation is only supported on tokens.") } + if err = rw.printJasTablesIfNeeded(tableContent, utils.MaliciousCodeScan, jasutils.MaliciousCode); err != nil { + return + } if err = rw.printJasTablesIfNeeded(tableContent, utils.IacScan, jasutils.IaC); err != nil { return } @@ -333,6 +336,14 @@ func PrintJasTable(tables formats.ResultsTables, entitledForJas bool, scanType j return coreutils.PrintTable(tables.SastVulnerabilitiesTable, "Static Application Security Testing (SAST)", "✨ No Static Application Security Testing vulnerabilities were found ✨", false) } + case jasutils.MaliciousCode: + if violations { + return coreutils.PrintTable(tables.MaliciousViolationsTable, "Malicious Code Violations", + "✨ No Malicious Code violations were found ✨", false) + } else { + return coreutils.PrintTable(tables.MaliciousVulnerabilitiesTable, "Malicious Code Detection", + "✨ No Malicious Code vulnerabilities were found ✨", false) + } } return nil } diff --git a/utils/results/results.go b/utils/results/results.go index 0c3f2a6f..9970ce81 100644 --- a/utils/results/results.go +++ b/utils/results/results.go @@ -98,9 +98,10 @@ type JasScansResults struct { } type JasScanResults struct { - SecretsScanResults []ScanResult[[]*sarif.Run] `json:"secrets,omitempty"` - IacScanResults []ScanResult[[]*sarif.Run] `json:"iac,omitempty"` - SastScanResults []ScanResult[[]*sarif.Run] `json:"sast,omitempty"` + SecretsScanResults []ScanResult[[]*sarif.Run] `json:"secrets,omitempty"` + IacScanResults []ScanResult[[]*sarif.Run] `json:"iac,omitempty"` + SastScanResults []ScanResult[[]*sarif.Run] `json:"sast,omitempty"` + MaliciousScanResults []ScanResult[[]*sarif.Run] `json:"malicious_code,omitempty"` } type ScanTarget struct { @@ -457,6 +458,9 @@ func (jsr *JasScansResults) AddJasScanResults(scanType jasutils.JasScanType, vul case jasutils.Sast: jsr.JasVulnerabilities.SastScanResults = append(jsr.JasVulnerabilities.SastScanResults, ScanResult[[]*sarif.Run]{Scan: vulnerabilitiesRuns, StatusCode: exitCode}) jsr.JasViolations.SastScanResults = append(jsr.JasViolations.SastScanResults, ScanResult[[]*sarif.Run]{Scan: violationsRuns, StatusCode: exitCode}) + case jasutils.MaliciousCode: + jsr.JasVulnerabilities.MaliciousScanResults = append(jsr.JasVulnerabilities.MaliciousScanResults, ScanResult[[]*sarif.Run]{Scan: vulnerabilitiesRuns, StatusCode: exitCode}) + jsr.JasViolations.MaliciousScanResults = append(jsr.JasViolations.MaliciousScanResults, ScanResult[[]*sarif.Run]{Scan: violationsRuns, StatusCode: exitCode}) } } @@ -490,6 +494,13 @@ func (jsr *JasScansResults) GetVulnerabilitiesResults(scanType jasutils.JasScanT } results = append(results, scan.Scan...) } + case jasutils.MaliciousCode: + for _, scan := range jsr.JasVulnerabilities.MaliciousScanResults { + if scan.IsScanFailed() { + continue + } + results = append(results, scan.Scan...) + } } return } @@ -517,6 +528,13 @@ func (jsr *JasScansResults) GetViolationsResults(scanType jasutils.JasScanType) } results = append(results, scan.Scan...) } + case jasutils.MaliciousCode: + for _, scan := range jsr.JasViolations.MaliciousScanResults { + if scan.IsScanFailed() { + continue + } + results = append(results, scan.Scan...) + } } return } diff --git a/utils/utils.go b/utils/utils.go index 4d6536fa..a05c5721 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -50,6 +50,7 @@ const ( IacScan SubScanType = "iac" SastScan SubScanType = "sast" SecretsScan SubScanType = "secrets" + MaliciousCodeScan SubScanType = "malicious_code" SecretTokenValidationScan SubScanType = "secrets_token_validation" ViolationTypeSecurity ViolationIssueType = "security" ViolationTypeLicense ViolationIssueType = "license" @@ -84,7 +85,7 @@ func (s CommandType) IsTargetBinary() bool { } func GetAllSupportedScans() []SubScanType { - return []SubScanType{ScaScan, ContextualAnalysisScan, IacScan, SastScan, SecretsScan, SecretTokenValidationScan} + return []SubScanType{ScaScan, ContextualAnalysisScan, IacScan, SastScan, SecretsScan, SecretTokenValidationScan, MaliciousCodeScan} } // IsScanRequested returns true if the scan is requested, otherwise false. If requestedScans is empty, all scans are considered requested. @@ -99,7 +100,8 @@ func IsJASRequested(cmdType CommandType, requestedScans ...SubScanType) bool { return IsScanRequested(cmdType, ContextualAnalysisScan, requestedScans...) || IsScanRequested(cmdType, SecretsScan, requestedScans...) || IsScanRequested(cmdType, IacScan, requestedScans...) || - IsScanRequested(cmdType, SastScan, requestedScans...) + IsScanRequested(cmdType, SastScan, requestedScans...) || + IsScanRequested(cmdType, MaliciousCodeScan, requestedScans...) } func GetScanFindingsLog(scanType SubScanType, vulnerabilitiesCount, violationsCount, threadId int) string {