Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitLab Pipeline Artifacts/Reports #745

Open
wants to merge 13 commits into
base: dev
Choose a base branch
from
4 changes: 2 additions & 2 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-pipenv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-poetry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/templates/jfrog-pipelines/pipelines-yarn2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions scanpullrequest/scanallpullrequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand All @@ -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()))
}
Expand Down
31 changes: 26 additions & 5 deletions scanpullrequest/scanallpullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 (
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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"))
Expand All @@ -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")
}
}

Expand All @@ -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,
Expand All @@ -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 {
Expand Down
44 changes: 37 additions & 7 deletions scanpullrequest/scanpullrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"

"github.com/jfrog/frogbot/v2/utils"
Expand All @@ -28,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
}
Expand All @@ -42,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
Expand Down Expand Up @@ -81,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,
Expand All @@ -95,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
}
Expand Down Expand Up @@ -128,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).
Expand All @@ -148,7 +149,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)
Expand All @@ -159,7 +160,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)
Expand All @@ -179,6 +202,13 @@ func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils.
return
}

// If sarifPath is provided, generate and write SARIF report
if sarifPath != "" {
if err = generateAndWriteSarifReport(sourceResults, repoConfig, sarifPath); err != nil {
return
}
}

// Set JAS output flags
sourceScanResults := sourceResults.ExtendedScanResults
repoConfig.OutputWriter.SetJasOutputFlags(sourceScanResults.EntitledForJas, len(sourceScanResults.ApplicabilityScanResults) > 0)
Expand Down
11 changes: 10 additions & 1 deletion scanpullrequest/scanpullrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down
5 changes: 3 additions & 2 deletions scanrepository/scanmultiplerepositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scanrepository

import (
"errors"

"github.com/jfrog/frogbot/v2/utils"
"github.com/jfrog/froggit-go/vcsclient"
)
Expand All @@ -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)
}
}
Expand Down
Loading
Loading