From aef782bf3e905147ff3b05f7b8220a53ec9cdd3b Mon Sep 17 00:00:00 2001 From: "gal.dahan" <963gal963@gmail.com> Date: Thu, 22 Aug 2024 11:29:03 +0300 Subject: [PATCH 1/5] Add SARIF generation feature --- scanrepository/scanrepository.go | 6 +++++- utils/consts.go | 3 +++ utils/utils.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/scanrepository/scanrepository.go b/scanrepository/scanrepository.go index f3ad56389..e415c9900 100644 --- a/scanrepository/scanrepository.go +++ b/scanrepository/scanrepository.go @@ -164,7 +164,11 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er if err = utils.UploadSarifResultsToGithubSecurityTab(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { log.Warn(err) } - } + }else if repository.GitProvider.String() == vcsutils.GitLab.String() { + // Uploads SARIF result to gitlab Dashborad + if err = utils.UploadSarifResults(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { + log.Warn(err) + } // Prepare the vulnerabilities map for each working dir path currPathVulnerabilities, err := cfp.getVulnerabilitiesMap(scanResults, scanResults.IsMultipleProject()) diff --git a/utils/consts.go b/utils/consts.go index b9a705234..c11c38a04 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -100,6 +100,9 @@ const ( // Frogbot Git author details showed in commits frogbotAuthorName = "JFrog-Frogbot" frogbotAuthorEmail = "eco-system+frogbot@jfrog.com" + + // SARIF file related environment variables + SarifOutputPathEnv = "JF_SARIF_OUTPUT_PATH" ) type UnsupportedErrorType string diff --git a/utils/utils.go b/utils/utils.go index 56591d855..34f48bc96 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,6 +4,7 @@ import ( "context" "crypto" "encoding/hex" + "encoding/json" "errors" "fmt" "net/http" @@ -238,6 +239,34 @@ func UploadSarifResultsToGithubSecurityTab(scanResults *xrayutils.Results, repo return nil } +func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { + // Generate SARIF report from scan results + report, err := GenerateFrogbotSarifReport(scanResults, scanResults.IsMultipleProject(), repo.AllowedLicenses) + if err != nil { + return fmt.Errorf("failed to generate SARIF report: %w", err) + } + + // Get SARIF output path from environment variable + sarifOutputPath := getTrimmedEnv(SarifOutputPathEnv) + if sarifOutputPath != "" { + file, err := os.Create(sarifOutputPath) + if err != nil { + return fmt.Errorf("failed to create SARIF file: %w", err) + } + defer file.Close() + + encoder := json.NewEncoder(file) + if err := encoder.Encode(report); err != nil { + return fmt.Errorf("failed to write SARIF file: %w", err) + } + log.Debug("SARIF report has been saved to", sarifOutputPath) + } else { + log.Debug("SARIF output path not set. Skipping SARIF file creation.") + } + + return nil +} + func prepareRunsForGithubReport(runs []*sarif.Run) []*sarif.Run { for _, run := range runs { for _, rule := range run.Tool.Driver.Rules { From a616570febc982f5c0affdb6faaf3028b57cb36f Mon Sep 17 00:00:00 2001 From: "gal.dahan" <963gal963@gmail.com> Date: Thu, 22 Aug 2024 12:04:01 +0300 Subject: [PATCH 2/5] Add SARIF generation feature --- scanrepository/scanrepository.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scanrepository/scanrepository.go b/scanrepository/scanrepository.go index e415c9900..b2263c24c 100644 --- a/scanrepository/scanrepository.go +++ b/scanrepository/scanrepository.go @@ -164,12 +164,12 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er if err = utils.UploadSarifResultsToGithubSecurityTab(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { log.Warn(err) } - }else if repository.GitProvider.String() == vcsutils.GitLab.String() { + } else if repository.GitProvider.String() == vcsutils.GitLab.String() { // Uploads SARIF result to gitlab Dashborad if err = utils.UploadSarifResults(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { log.Warn(err) } - + } // Prepare the vulnerabilities map for each working dir path currPathVulnerabilities, err := cfp.getVulnerabilitiesMap(scanResults, scanResults.IsMultipleProject()) if err != nil { From 8cdd9a0cbc4e4be42e6880ddbf26a749adcd4827 Mon Sep 17 00:00:00 2001 From: "gal.dahan" <963gal963@gmail.com> Date: Thu, 29 Aug 2024 08:42:13 +0300 Subject: [PATCH 3/5] Add SARIF generation feature --- utils/utils.go | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index 34f48bc96..b4140caf0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -240,30 +240,46 @@ func UploadSarifResultsToGithubSecurityTab(scanResults *xrayutils.Results, repo } func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { - // Generate SARIF report from scan results - report, err := GenerateFrogbotSarifReport(scanResults, scanResults.IsMultipleProject(), repo.AllowedLicenses) + // Get SARIF output path from environment variable + log.Info("SarifOutputPathEnv in start %s", SarifOutputPathEnv) + sarifOutputPath := getTrimmedEnv(SarifOutputPathEnv) + if sarifOutputPath == "" { + sarifOutputPath = "/tmp/sarifOutputPath.sarif" // Fallback path if env variable is not set + } + log.Info("sarifOutputPath after getTrimmedEnv %s", SarifOutputPathEnv) + // Generate SARIF report + sarifReport, err := xrayutils.GenereateSarifReportFromResults(scanResults, scanResults.IsMultipleProject(), false, repo.AllowedLicenses) if err != nil { return fmt.Errorf("failed to generate SARIF report: %w", err) } - // Get SARIF output path from environment variable - sarifOutputPath := getTrimmedEnv(SarifOutputPathEnv) - if sarifOutputPath != "" { - file, err := os.Create(sarifOutputPath) - if err != nil { - return fmt.Errorf("failed to create SARIF file: %w", err) - } - defer file.Close() + // Serialize the SARIF report to JSON + reportBytes, err := json.MarshalIndent(sarifReport, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal SARIF report to JSON: %w", err) + } + //report, err := GenerateFrogbotSarifReport(scanResults, scanResults.IsMultipleProject(), repo.AllowedLicenses) + //if err != nil { + // return err + //} + //// Open or create the SARIF output file + file, err := os.Create(sarifOutputPath) + if err != nil { + return fmt.Errorf("failed to create SARIF file at %s: %w", sarifOutputPath, err) + } + defer file.Close() // Ensure the file is closed even if an error occurs - encoder := json.NewEncoder(file) - if err := encoder.Encode(report); err != nil { - return fmt.Errorf("failed to write SARIF file: %w", err) - } - log.Debug("SARIF report has been saved to", sarifOutputPath) - } else { - log.Debug("SARIF output path not set. Skipping SARIF file creation.") + // Write the SARIF report to the file + //_, err = file.Write([]byte(sarifReport)) + _, err = file.Write(reportBytes) + + if err != nil { + return fmt.Errorf("failed to write SARIF file: %w", err) } + // Log the action + log.Info("SARIF report has been written to %s", sarifOutputPath) + return nil } From beebfa1d5ae9f805609350835d7997946bf12eda Mon Sep 17 00:00:00 2001 From: "gal.dahan" <963gal963@gmail.com> Date: Thu, 29 Aug 2024 15:19:09 +0300 Subject: [PATCH 4/5] Add SARIF generation feature --- scanpullrequest/scanpullrequest.go | 20 ++++++++++++++++++++ utils/utils.go | 3 +-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/scanpullrequest/scanpullrequest.go b/scanpullrequest/scanpullrequest.go index 905f464d5..5d5646d8a 100644 --- a/scanpullrequest/scanpullrequest.go +++ b/scanpullrequest/scanpullrequest.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io/ioutil" "os" "github.com/jfrog/frogbot/v2/utils" @@ -178,6 +179,25 @@ func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils. return } + // Generate SARIF report + log.Info("Generating SARIF report...") + sarifReportStr, err := utils.GenerateFrogbotSarifReport(sourceResults, sourceResults.IsMultipleProject(), repoConfig.AllowedLicenses) + if err != nil { + log.Error("Error generating SARIF report: ", err) + return + } + + // Get SARIF output path from environment variable + sarifFilePath := "/tmp/sarifOutputPath.sarif" + // Write the SARIF report to a file + log.Info("Writing SARIF report to file: ", sarifFilePath) + err = ioutil.WriteFile(sarifFilePath, []byte(sarifReportStr), 0644) + if err != nil { + log.Error("Error writing SARIF report to file: ", err) + return + } + log.Info("SARIF report successfully written to file: ", sarifFilePath) + // Set JAS output flags sourceScanResults := sourceResults.ExtendedScanResults repoConfig.OutputWriter.SetJasOutputFlags(sourceScanResults.EntitledForJas, len(sourceScanResults.ApplicabilityScanResults) > 0) diff --git a/utils/utils.go b/utils/utils.go index b4140caf0..c4baee056 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -241,12 +241,11 @@ func UploadSarifResultsToGithubSecurityTab(scanResults *xrayutils.Results, repo func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { // Get SARIF output path from environment variable - log.Info("SarifOutputPathEnv in start %s", SarifOutputPathEnv) sarifOutputPath := getTrimmedEnv(SarifOutputPathEnv) + if sarifOutputPath == "" { sarifOutputPath = "/tmp/sarifOutputPath.sarif" // Fallback path if env variable is not set } - log.Info("sarifOutputPath after getTrimmedEnv %s", SarifOutputPathEnv) // Generate SARIF report sarifReport, err := xrayutils.GenereateSarifReportFromResults(scanResults, scanResults.IsMultipleProject(), false, repo.AllowedLicenses) if err != nil { From 37cbd0f5e5bb7e81aa96aad1154d77735bc92cef Mon Sep 17 00:00:00 2001 From: "gal.dahan" <963gal963@gmail.com> Date: Mon, 2 Sep 2024 15:05:24 +0300 Subject: [PATCH 5/5] Add SARIF generation feature --- commands.go | 4 +- .../jfrog-pipelines/pipelines-dotnet.yml | 4 ++ .../jfrog-pipelines/pipelines-go.yml | 4 ++ .../jfrog-pipelines/pipelines-gradle.yml | 4 ++ .../jfrog-pipelines/pipelines-maven.yml | 4 ++ .../jfrog-pipelines/pipelines-npm.yml | 4 ++ .../jfrog-pipelines/pipelines-pip.yml | 4 ++ .../jfrog-pipelines/pipelines-pipenv.yml | 4 ++ .../jfrog-pipelines/pipelines-poetry.yml | 4 ++ .../jfrog-pipelines/pipelines-yarn2.yml | 4 ++ scanpullrequest/scanallpullrequests.go | 8 +-- scanpullrequest/scanallpullrequests_test.go | 31 ++++++++-- scanpullrequest/scanpullrequest.go | 58 +++++++++++-------- scanpullrequest/scanpullrequest_test.go | 11 +++- scanrepository/scanmultiplerepositories.go | 5 +- .../scanmultiplerepositories_test.go | 22 ++++--- scanrepository/scanrepository.go | 19 +++--- scanrepository/scanrepository_test.go | 18 +++++- utils/params.go | 6 +- utils/utils.go | 20 ++----- 20 files changed, 164 insertions(+), 74 deletions(-) diff --git a/commands.go b/commands.go index b5041ea95..169014a4e 100644 --- a/commands.go +++ b/commands.go @@ -21,7 +21,7 @@ import ( type FrogbotCommand interface { // Run the command - Run(config utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) error + Run(config utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker, sarifPath string) error } func GetCommands() []*clitool.Command { @@ -100,7 +100,7 @@ func Exec(command FrogbotCommand, commandName string) (err error) { // Invoke the command interface log.Info(fmt.Sprintf("Running Frogbot %q command", commandName)) - err = command.Run(frogbotDetails.Repositories, frogbotDetails.GitClient, frogbotRepoConnection) + err = command.Run(frogbotDetails.Repositories, frogbotDetails.GitClient, frogbotRepoConnection, frogbotDetails.SarifPath) // Wait for usage reporting to finish. waitForUsageResponse() diff --git a/docs/templates/jfrog-pipelines/pipelines-dotnet.yml b/docs/templates/jfrog-pipelines/pipelines-dotnet.yml index 43f84c715..4b43e0e4d 100644 --- a/docs/templates/jfrog-pipelines/pipelines-dotnet.yml +++ b/docs/templates/jfrog-pipelines/pipelines-dotnet.yml @@ -198,6 +198,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-go.yml b/docs/templates/jfrog-pipelines/pipelines-go.yml index 24d4f2823..ed4f6f5b0 100644 --- a/docs/templates/jfrog-pipelines/pipelines-go.yml +++ b/docs/templates/jfrog-pipelines/pipelines-go.yml @@ -199,6 +199,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-gradle.yml b/docs/templates/jfrog-pipelines/pipelines-gradle.yml index afec547c5..a87a08f73 100644 --- a/docs/templates/jfrog-pipelines/pipelines-gradle.yml +++ b/docs/templates/jfrog-pipelines/pipelines-gradle.yml @@ -203,6 +203,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-maven.yml b/docs/templates/jfrog-pipelines/pipelines-maven.yml index 6dc5250cb..827874e15 100644 --- a/docs/templates/jfrog-pipelines/pipelines-maven.yml +++ b/docs/templates/jfrog-pipelines/pipelines-maven.yml @@ -191,6 +191,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-npm.yml b/docs/templates/jfrog-pipelines/pipelines-npm.yml index 98086d254..1071c93ab 100644 --- a/docs/templates/jfrog-pipelines/pipelines-npm.yml +++ b/docs/templates/jfrog-pipelines/pipelines-npm.yml @@ -202,6 +202,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-pip.yml b/docs/templates/jfrog-pipelines/pipelines-pip.yml index de4c47628..646a2c6a1 100644 --- a/docs/templates/jfrog-pipelines/pipelines-pip.yml +++ b/docs/templates/jfrog-pipelines/pipelines-pip.yml @@ -202,6 +202,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-pipenv.yml b/docs/templates/jfrog-pipelines/pipelines-pipenv.yml index 5f463f7b7..e5e7c5644 100644 --- a/docs/templates/jfrog-pipelines/pipelines-pipenv.yml +++ b/docs/templates/jfrog-pipelines/pipelines-pipenv.yml @@ -195,6 +195,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-poetry.yml b/docs/templates/jfrog-pipelines/pipelines-poetry.yml index fa78264b7..07774c5ec 100644 --- a/docs/templates/jfrog-pipelines/pipelines-poetry.yml +++ b/docs/templates/jfrog-pipelines/pipelines-poetry.yml @@ -195,6 +195,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path generated by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/docs/templates/jfrog-pipelines/pipelines-yarn2.yml b/docs/templates/jfrog-pipelines/pipelines-yarn2.yml index bd8652aeb..0bb11005f 100644 --- a/docs/templates/jfrog-pipelines/pipelines-yarn2.yml +++ b/docs/templates/jfrog-pipelines/pipelines-yarn2.yml @@ -198,6 +198,10 @@ pipelines: # Add a title to pull request comments generated by Frogbot. # JF_PR_COMMENT_TITLE: "" + # [Optional] + # Add a SARIF output path by Frogbot. + # JF_SARIF_OUTPUT_PATH: "" + execution: onExecute: - cd $res_frogbotGitRepo_resourcePath diff --git a/scanpullrequest/scanallpullrequests.go b/scanpullrequest/scanallpullrequests.go index 4a3fbe3d2..c976f3a92 100644 --- a/scanpullrequest/scanallpullrequests.go +++ b/scanpullrequest/scanallpullrequests.go @@ -17,12 +17,12 @@ var errPullRequestScan = "pull request #%d scan in the '%s' repository returned type ScanAllPullRequestsCmd struct { } -func (cmd ScanAllPullRequestsCmd) Run(configAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) error { +func (cmd ScanAllPullRequestsCmd) Run(configAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker, sarifPath string) error { for _, config := range configAggregator { log.Info("Scanning all open pull requests for repository:", config.RepoName) log.Info("-----------------------------------------------------------") config.OutputWriter.SetHasInternetConnection(frogbotRepoConnection.IsConnected()) - err := scanAllPullRequests(config, client) + err := scanAllPullRequests(config, client, sarifPath) if err != nil { return err } @@ -35,7 +35,7 @@ func (cmd ScanAllPullRequestsCmd) Run(configAggregator utils.RepoAggregator, cli // b. Find the ones that should be scanned (new PRs or PRs with a 're-scan' comment) // c. Audit the dependencies of the source and the target branches. // d. Compare the vulnerabilities found in source and target branches, and show only the new vulnerabilities added by the pull request. -func scanAllPullRequests(repo utils.Repository, client vcsclient.VcsClient) (err error) { +func scanAllPullRequests(repo utils.Repository, client vcsclient.VcsClient, sarifPath string) (err error) { openPullRequests, err := client.ListOpenPullRequests(context.Background(), repo.RepoOwner, repo.RepoName) if err != nil { return err @@ -50,7 +50,7 @@ func scanAllPullRequests(repo utils.Repository, client vcsclient.VcsClient) (err continue } repo.PullRequestDetails = pr - if e = scanPullRequest(&repo, client); e != nil { + if e = scanPullRequest(&repo, client, sarifPath); e != nil { // If error, write it in errList and continue to the next PR. err = errors.Join(err, fmt.Errorf(errPullRequestScan, int(pr.ID), repo.RepoName, e.Error())) } diff --git a/scanpullrequest/scanallpullrequests_test.go b/scanpullrequest/scanallpullrequests_test.go index cd42f8aa3..81e6dbc95 100644 --- a/scanpullrequest/scanallpullrequests_test.go +++ b/scanpullrequest/scanallpullrequests_test.go @@ -3,6 +3,11 @@ package scanpullrequest import ( "context" "fmt" + "os" + "path/filepath" + "testing" + "time" + "github.com/golang/mock/gomock" biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/frogbot/v2/testdata" @@ -11,9 +16,6 @@ import ( "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/froggit-go/vcsutils" "github.com/stretchr/testify/assert" - "path/filepath" - "testing" - "time" ) var ( @@ -104,6 +106,13 @@ func TestScanAllPullRequestsMultiRepo(t *testing.T) { _, restoreJfrogHomeFunc := utils.CreateTempJfrogHomeWithCallback(t) defer restoreJfrogHomeFunc() + // Create a temporary file for SARIF output + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() + failOnSecurityIssues := false firstRepoParams := utils.Params{ Scan: utils.Scan{ @@ -143,7 +152,7 @@ func TestScanAllPullRequestsMultiRepo(t *testing.T) { var frogbotMessages []string client := getMockClient(t, &frogbotMessages, mockParams...) scanAllPullRequestsCmd := &ScanAllPullRequestsCmd{} - err := scanAllPullRequestsCmd.Run(configAggregator, client, utils.MockHasConnection()) + err = scanAllPullRequestsCmd.Run(configAggregator, client, utils.MockHasConnection(), sarifPath) if assert.NoError(t, err) { assert.Len(t, frogbotMessages, 4) expectedMessage := outputwriter.GetOutputFromFile(t, filepath.Join(allPrIntegrationPath, "test_proj_with_vulnerability_standard.md")) @@ -154,6 +163,8 @@ func TestScanAllPullRequestsMultiRepo(t *testing.T) { assert.Equal(t, expectedMessage, frogbotMessages[2]) expectedMessage = outputwriter.GetPRSummaryContentNoIssues(t, outputwriter.TestSummaryCommentDir, true, false) assert.Equal(t, expectedMessage, frogbotMessages[3]) + _, err = os.Stat(sarifPath) + assert.NoError(t, err, "SARIF file should exist at the specified path") } } @@ -163,6 +174,14 @@ func TestScanAllPullRequests(t *testing.T) { defer restoreEnv() falseVal := false gitParams.Git.GitProvider = vcsutils.BitbucketServer + + // Create a temporary file for SARIF output + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() + params := utils.Params{ Scan: utils.Scan{ FailOnSecurityIssues: &falseVal, @@ -185,13 +204,15 @@ func TestScanAllPullRequests(t *testing.T) { var frogbotMessages []string client := getMockClient(t, &frogbotMessages, MockParams{repoParams.RepoName, repoParams.RepoOwner, "test-proj-with-vulnerability", "test-proj"}) scanAllPullRequestsCmd := &ScanAllPullRequestsCmd{} - err := scanAllPullRequestsCmd.Run(paramsAggregator, client, utils.MockHasConnection()) + err = scanAllPullRequestsCmd.Run(paramsAggregator, client, utils.MockHasConnection(), sarifPath) assert.NoError(t, err) assert.Len(t, frogbotMessages, 2) expectedMessage := outputwriter.GetOutputFromFile(t, filepath.Join(allPrIntegrationPath, "test_proj_with_vulnerability_simplified.md")) assert.Equal(t, expectedMessage, frogbotMessages[0]) expectedMessage = outputwriter.GetPRSummaryContentNoIssues(t, outputwriter.TestSummaryCommentDir, true, true) assert.Equal(t, expectedMessage, frogbotMessages[1]) + _, err = os.Stat(sarifPath) + assert.NoError(t, err, "SARIF file should exist at the specified path") } func getMockClient(t *testing.T, frogbotMessages *[]string, mockParams ...MockParams) *testdata.MockVcsClient { diff --git a/scanpullrequest/scanpullrequest.go b/scanpullrequest/scanpullrequest.go index 5d5646d8a..989506bbf 100644 --- a/scanpullrequest/scanpullrequest.go +++ b/scanpullrequest/scanpullrequest.go @@ -29,7 +29,7 @@ type ScanPullRequestCmd struct{} // Run ScanPullRequest method only works for a single repository scan. // Therefore, the first repository config represents the repository on which Frogbot runs, and it is the only one that matters. -func (cmd *ScanPullRequestCmd) Run(configAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) (err error) { +func (cmd *ScanPullRequestCmd) Run(configAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker, sarifPath string) (err error) { if err = utils.ValidateSingleRepoConfiguration(&configAggregator); err != nil { return } @@ -43,7 +43,7 @@ func (cmd *ScanPullRequestCmd) Run(configAggregator utils.RepoAggregator, client if repoConfig.PullRequestDetails, err = client.GetPullRequestByID(context.Background(), repoConfig.RepoOwner, repoConfig.RepoName, int(repoConfig.PullRequestDetails.ID)); err != nil { return } - return scanPullRequest(repoConfig, client) + return scanPullRequest(repoConfig, client, sarifPath) } // Verify that the 'frogbot' GitHub environment was properly configured on the repository @@ -82,7 +82,7 @@ func verifyGitHubFrogbotEnvironment(client vcsclient.VcsClient, repoConfig *util // a. Audit the dependencies of the source and the target branches. // b. Compare the vulnerabilities found in source and target branches, and show only the new vulnerabilities added by the pull request. // Otherwise, only the source branch is scanned and all found vulnerabilities are being displayed. -func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err error) { +func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient, sarifPath string) (err error) { pullRequestDetails := repo.PullRequestDetails log.Info(fmt.Sprintf("Scanning Pull Request #%d (from source branch: <%s/%s/%s> to target branch: <%s/%s/%s>)", pullRequestDetails.ID, @@ -96,7 +96,7 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err er }() // Audit PR code - issues, err := auditPullRequest(repo, client, analyticsService) + issues, err := auditPullRequest(repo, client, analyticsService, sarifPath) if err != nil { return } @@ -129,7 +129,7 @@ func toFailTaskStatus(repo *utils.Repository, issues *utils.IssuesCollection) bo } // Downloads Pull Requests branches code and audits them -func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, analyticsService *xsc.AnalyticsMetricsService) (issuesCollection *utils.IssuesCollection, err error) { +func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, analyticsService *xsc.AnalyticsMetricsService, sarifPath string) (issuesCollection *utils.IssuesCollection, err error) { scanDetails := utils.NewScanDetails(client, &repoConfig.Server, &repoConfig.Git). SetXrayGraphScanParams(repoConfig.Watches, repoConfig.JFrogProjectKey, len(repoConfig.AllowedLicenses) > 0). SetFixableOnly(repoConfig.FixableOnly). @@ -148,7 +148,7 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, for i := range repoConfig.Projects { scanDetails.SetProject(&repoConfig.Projects[i]) var projectIssues *utils.IssuesCollection - if projectIssues, err = auditPullRequestInProject(repoConfig, scanDetails); err != nil { + if projectIssues, err = auditPullRequestInProject(repoConfig, scanDetails, sarifPath); err != nil { return } issuesCollection.Append(projectIssues) @@ -159,7 +159,29 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, return } -func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils.ScanDetails) (auditIssues *utils.IssuesCollection, err error) { +// Generate and write SARIF report to sarifPath +func generateAndWriteSarifReport(sourceResults *securityutils.Results, repoConfig *utils.Repository, sarifPath string) error { + // Generate SARIF report + log.Info("Generating SARIF report...") + sarifReportStr, err := utils.GenerateFrogbotSarifReport(sourceResults, sourceResults.IsMultipleProject(), repoConfig.AllowedLicenses) + if err != nil { + log.Error("Error generating SARIF report: ", err) + return err + } + + // Write the SARIF report to a file + log.Info("Writing SARIF report to file: ", sarifPath) + err = ioutil.WriteFile(sarifPath, []byte(sarifReportStr), 0644) + if err != nil { + log.Error("Error writing SARIF report to file: ", err) + return err + } + + log.Info("SARIF report successfully written to file: ", sarifPath) + return nil +} + +func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils.ScanDetails, sarifPath string) (auditIssues *utils.IssuesCollection, err error) { // Download source branch sourcePullRequestInfo := scanDetails.PullRequestDetails.Source sourceBranchWd, cleanupSource, err := utils.DownloadRepoToTempDir(scanDetails.Client(), sourcePullRequestInfo.Owner, sourcePullRequestInfo.Repository, sourcePullRequestInfo.Name) @@ -179,24 +201,12 @@ func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils. return } - // Generate SARIF report - log.Info("Generating SARIF report...") - sarifReportStr, err := utils.GenerateFrogbotSarifReport(sourceResults, sourceResults.IsMultipleProject(), repoConfig.AllowedLicenses) - if err != nil { - log.Error("Error generating SARIF report: ", err) - return - } - - // Get SARIF output path from environment variable - sarifFilePath := "/tmp/sarifOutputPath.sarif" - // Write the SARIF report to a file - log.Info("Writing SARIF report to file: ", sarifFilePath) - err = ioutil.WriteFile(sarifFilePath, []byte(sarifReportStr), 0644) - if err != nil { - log.Error("Error writing SARIF report to file: ", err) - return + // If sarifPath is provided, generate and write SARIF report + if sarifPath != "" { + if err = generateAndWriteSarifReport(sourceResults, repoConfig, sarifPath); err != nil { + return + } } - log.Info("SARIF report successfully written to file: ", sarifFilePath) // Set JAS output flags sourceScanResults := sourceResults.ExtendedScanResults diff --git a/scanpullrequest/scanpullrequest_test.go b/scanpullrequest/scanpullrequest_test.go index 74d323a25..72c642c26 100644 --- a/scanpullrequest/scanpullrequest_test.go +++ b/scanpullrequest/scanpullrequest_test.go @@ -604,6 +604,13 @@ func testScanPullRequest(t *testing.T, configPath, projectName string, failOnSec testDir, cleanUp := utils.CopyTestdataProjectsToTemp(t, "scanpullrequest") defer cleanUp() + // Create a temporary file for SARIF output + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() + // Renames test git folder to .git currentDir := filepath.Join(testDir, projectName) restoreDir, err := utils.Chdir(currentDir) @@ -615,12 +622,14 @@ func testScanPullRequest(t *testing.T, configPath, projectName string, failOnSec // Run "frogbot scan pull request" var scanPullRequest ScanPullRequestCmd - err = scanPullRequest.Run(configAggregator, client, utils.MockHasConnection()) + err = scanPullRequest.Run(configAggregator, client, utils.MockHasConnection(), sarifPath) if failOnSecurityIssues { assert.EqualErrorf(t, err, SecurityIssueFoundErr, "Error should be: %v, got: %v", SecurityIssueFoundErr, err) } else { assert.NoError(t, err) } + _, err = os.Stat(sarifPath) + assert.NoError(t, err, "SARIF file should exist at the specified path") // Check env sanitize err = utils.SanitizeEnv() diff --git a/scanrepository/scanmultiplerepositories.go b/scanrepository/scanmultiplerepositories.go index 901f06ca6..46ef09a78 100644 --- a/scanrepository/scanmultiplerepositories.go +++ b/scanrepository/scanmultiplerepositories.go @@ -2,6 +2,7 @@ package scanrepository import ( "errors" + "github.com/jfrog/frogbot/v2/utils" "github.com/jfrog/froggit-go/vcsclient" ) @@ -13,11 +14,11 @@ type ScanMultipleRepositories struct { dryRunRepoPath string } -func (saf *ScanMultipleRepositories) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) (err error) { +func (saf *ScanMultipleRepositories) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker, sarifPath string) (err error) { scanRepositoryCmd := &ScanRepositoryCmd{dryRun: saf.dryRun, dryRunRepoPath: saf.dryRunRepoPath, baseWd: saf.dryRunRepoPath} for repoNum := range repoAggregator { repoAggregator[repoNum].OutputWriter.SetHasInternetConnection(frogbotRepoConnection.IsConnected()) - if e := scanRepositoryCmd.scanAndFixRepository(&repoAggregator[repoNum], client); e != nil { + if e := scanRepositoryCmd.scanAndFixRepository(&repoAggregator[repoNum], client, sarifPath); e != nil { err = errors.Join(err, e) } } diff --git a/scanrepository/scanmultiplerepositories_test.go b/scanrepository/scanmultiplerepositories_test.go index bc8e2ff6f..e6fdc4283 100644 --- a/scanrepository/scanmultiplerepositories_test.go +++ b/scanrepository/scanmultiplerepositories_test.go @@ -4,6 +4,13 @@ import ( "bytes" "encoding/json" "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/protocol/packp" "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability" @@ -11,12 +18,6 @@ import ( "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/froggit-go/vcsutils" "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" ) var testScanMultipleRepositoriesConfigPath = filepath.Join("..", "testdata", "config", "frogbot-config-scan-multiple-repositories.yml") @@ -38,6 +39,13 @@ func TestScanAndFixRepos(t *testing.T) { client, err := vcsclient.NewClientBuilder(vcsutils.GitHub).ApiEndpoint(server.URL).Token("123456").Build() assert.NoError(t, err) + // Create a temporary file for SARIF output + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() + gitTestParams := utils.Git{ GitProvider: vcsutils.GitHub, RepoOwner: "jfrog", @@ -61,7 +69,7 @@ func TestScanAndFixRepos(t *testing.T) { assert.NoError(t, err) var cmd = ScanMultipleRepositories{dryRun: true, dryRunRepoPath: testDir} - assert.NoError(t, cmd.Run(configAggregator, client, utils.MockHasConnection())) + assert.NoError(t, cmd.Run(configAggregator, client, utils.MockHasConnection(), sarifPath)) } func createScanRepoGitHubHandler(t *testing.T, port *string, response interface{}, projectNames ...string) http.HandlerFunc { diff --git a/scanrepository/scanrepository.go b/scanrepository/scanrepository.go index b2263c24c..0f84b3c91 100644 --- a/scanrepository/scanrepository.go +++ b/scanrepository/scanrepository.go @@ -49,30 +49,30 @@ type ScanRepositoryCmd struct { analyticsService *xsc.AnalyticsMetricsService } -func (cfp *ScanRepositoryCmd) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) (err error) { +func (cfp *ScanRepositoryCmd) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker, sarifPath string) (err error) { if err = utils.ValidateSingleRepoConfiguration(&repoAggregator); err != nil { return err } repository := repoAggregator[0] repository.OutputWriter.SetHasInternetConnection(frogbotRepoConnection.IsConnected()) - return cfp.scanAndFixRepository(&repository, client) + return cfp.scanAndFixRepository(&repository, client, sarifPath) } -func (cfp *ScanRepositoryCmd) scanAndFixRepository(repository *utils.Repository, client vcsclient.VcsClient) (err error) { +func (cfp *ScanRepositoryCmd) scanAndFixRepository(repository *utils.Repository, client vcsclient.VcsClient, sarifPath string) (err error) { if err = cfp.setCommandPrerequisites(repository, client); err != nil { return } for _, branch := range repository.Branches { cfp.scanDetails.SetBaseBranch(branch) cfp.scanDetails.SetXscGitInfoContext(branch, repository.Project, client) - if err = cfp.scanAndFixBranch(repository); err != nil { + if err = cfp.scanAndFixBranch(repository, sarifPath); err != nil { return } } return } -func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (err error) { +func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository, sarifPath string) (err error) { cfp.analyticsService = utils.AddAnalyticsGeneralEvent(cfp.scanDetails.XscGitInfoContext, cfp.scanDetails.ServerDetails, analyticsScanRepositoryScanType) defer func() { cfp.analyticsService.UpdateAndSendXscAnalyticsGeneralEventFinalize(err) @@ -104,7 +104,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (er for i := range repository.Projects { cfp.scanDetails.Project = &repository.Projects[i] cfp.projectTech = []techutils.Technology{} - if err = cfp.scanAndFixProject(repository); err != nil { + if err = cfp.scanAndFixProject(repository, sarifPath); err != nil { return } } @@ -142,11 +142,12 @@ func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Reposito return } -func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) error { +func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository, sarifPath string) error { var fixNeeded bool // A map that contains the full project paths as a keys // The value is a map of vulnerable package names -> the scanDetails of the vulnerable packages. // That means we have a map of all the vulnerabilities that were found in a specific folder, along with their full scanDetails. + log.Info("sarifPath is %s", sarifPath) vulnerabilitiesByPathMap := make(map[string]map[string]*utils.VulnerabilityDetails) projectFullPathWorkingDirs := utils.GetFullPathWorkingDirs(cfp.scanDetails.Project.WorkingDirs, cfp.baseWd) for _, fullPathWd := range projectFullPathWorkingDirs { @@ -164,9 +165,9 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er if err = utils.UploadSarifResultsToGithubSecurityTab(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { log.Warn(err) } - } else if repository.GitProvider.String() == vcsutils.GitLab.String() { + } else if sarifPath != "" { // Uploads SARIF result to gitlab Dashborad - if err = utils.UploadSarifResults(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { + if err = utils.UploadSarifResults(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client(), sarifPath); err != nil { log.Warn(err) } } diff --git a/scanrepository/scanrepository_test.go b/scanrepository/scanrepository_test.go index 01359d24e..10eef48ee 100644 --- a/scanrepository/scanrepository_test.go +++ b/scanrepository/scanrepository_test.go @@ -177,13 +177,20 @@ func TestScanRepositoryCmd_Run(t *testing.T) { // Manual set of "JF_GIT_BASE_BRANCH" gitTestParams.Branches = []string{"master"} } + // Create a temporary file for SARIF output in this test case + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() utils.CreateDotGitWithCommit(t, testDir, port, test.testName) configAggregator, err := utils.BuildRepoAggregator(client, configData, &gitTestParams, &serverParams, utils.ScanRepository) assert.NoError(t, err) // Run var cmd = ScanRepositoryCmd{dryRun: true, dryRunRepoPath: testDir} - err = cmd.Run(configAggregator, client, utils.MockHasConnection()) + err = cmd.Run(configAggregator, client, utils.MockHasConnection(), sarifPath) defer func() { assert.NoError(t, os.Chdir(baseDir)) }() @@ -292,6 +299,13 @@ pr body APIEndpoint: server.URL, }, RepoName: test.testName, } + // Create a temporary file for SARIF output in this test case + tmpFile, err := os.CreateTemp("", "sarifOutputPath-*.sarif") + assert.NoError(t, err, "Temporary file for SARIF path should be created successfully") + defer os.Remove(tmpFile.Name()) // Clean up the file at the end of the test + + // Use the temporary file's path as sarifPath + sarifPath := tmpFile.Name() utils.CreateDotGitWithCommit(t, testDir, port, test.testName) client, err := vcsclient.NewClientBuilder(vcsutils.GitHub).ApiEndpoint(server.URL).Token("123456").Build() @@ -303,7 +317,7 @@ pr body assert.NoError(t, err) // Run var cmd = ScanRepositoryCmd{dryRun: true, dryRunRepoPath: testDir} - err = cmd.Run(configAggregator, client, utils.MockHasConnection()) + err = cmd.Run(configAggregator, client, utils.MockHasConnection(), sarifPath) defer func() { assert.NoError(t, os.Chdir(baseDir)) }() diff --git a/utils/params.go b/utils/params.go index 6e6f27b07..f839ed5e7 100644 --- a/utils/params.go +++ b/utils/params.go @@ -39,6 +39,7 @@ type FrogbotDetails struct { ServerDetails *coreconfig.ServerDetails GitClient vcsclient.VcsClient ReleasesRepo string + SarifPath string } type RepoAggregator []Repository @@ -358,11 +359,10 @@ func GetFrogbotDetails(commandName string) (frogbotDetails *FrogbotDetails, err if err != nil { return } - + sarifPath := getTrimmedEnv(SarifOutputPathEnv) defer func() { err = errors.Join(err, SanitizeEnv()) }() - // Build a version control client for REST API requests client, err := vcsclient. NewClientBuilder(gitParamsFromEnv.GitProvider). @@ -381,7 +381,7 @@ func GetFrogbotDetails(commandName string) (frogbotDetails *FrogbotDetails, err return } - frogbotDetails = &FrogbotDetails{Repositories: configAggregator, GitClient: client, ServerDetails: jfrogServer, ReleasesRepo: os.Getenv(jfrogReleasesRepoEnv)} + frogbotDetails = &FrogbotDetails{Repositories: configAggregator, GitClient: client, ServerDetails: jfrogServer, ReleasesRepo: os.Getenv(jfrogReleasesRepoEnv), SarifPath: sarifPath} return } diff --git a/utils/utils.go b/utils/utils.go index c4baee056..2d34b5142 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -239,13 +239,8 @@ func UploadSarifResultsToGithubSecurityTab(scanResults *xrayutils.Results, repo return nil } -func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { - // Get SARIF output path from environment variable - sarifOutputPath := getTrimmedEnv(SarifOutputPathEnv) +func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch string, client vcsclient.VcsClient, sarifPath string) error { - if sarifOutputPath == "" { - sarifOutputPath = "/tmp/sarifOutputPath.sarif" // Fallback path if env variable is not set - } // Generate SARIF report sarifReport, err := xrayutils.GenereateSarifReportFromResults(scanResults, scanResults.IsMultipleProject(), false, repo.AllowedLicenses) if err != nil { @@ -257,27 +252,22 @@ func UploadSarifResults(scanResults *xrayutils.Results, repo *Repository, branch if err != nil { return fmt.Errorf("failed to marshal SARIF report to JSON: %w", err) } - //report, err := GenerateFrogbotSarifReport(scanResults, scanResults.IsMultipleProject(), repo.AllowedLicenses) - //if err != nil { - // return err - //} + //// Open or create the SARIF output file - file, err := os.Create(sarifOutputPath) + file, err := os.Create(sarifPath) if err != nil { - return fmt.Errorf("failed to create SARIF file at %s: %w", sarifOutputPath, err) + return fmt.Errorf("failed to create SARIF file at %s: %w", sarifPath, err) } defer file.Close() // Ensure the file is closed even if an error occurs // Write the SARIF report to the file - //_, err = file.Write([]byte(sarifReport)) _, err = file.Write(reportBytes) if err != nil { return fmt.Errorf("failed to write SARIF file: %w", err) } - // Log the action - log.Info("SARIF report has been written to %s", sarifOutputPath) + log.Info("SARIF report has been written to %s", sarifPath) return nil }