Skip to content

Commit

Permalink
Split summary comment if exceeded max characters (#647)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored Mar 24, 2024
1 parent e712bb9 commit 206b4b8
Show file tree
Hide file tree
Showing 55 changed files with 662 additions and 379 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
)

replace github.com/jfrog/froggit-go => github.com/jfrog/froggit-go v1.14.7-0.20240324075617-8b1026034580

// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 dev

// replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security dev
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -892,8 +892,8 @@ github.com/jfrog/archiver/v3 v3.6.0 h1:OVZ50vudkIQmKMgA8mmFF9S0gA47lcag22N13iV3F
github.com/jfrog/archiver/v3 v3.6.0/go.mod h1:fCAof46C3rAXgZurS8kNRNdSVMKBbZs+bNNhPYxLldI=
github.com/jfrog/build-info-go v1.9.23 h1:+TwUIBEJwRvz9skR8xBfY5ti8Vl4Z6iMCkFbkclnEN0=
github.com/jfrog/build-info-go v1.9.23/go.mod h1:QHcKuesY4MrBVBuEwwBz4uIsX6mwYuMEDV09ng4AvAU=
github.com/jfrog/froggit-go v1.14.6 h1:7Z2KgDgdjGAyVt1GROe2PwzllgJhw2s2g64dA9MoYCU=
github.com/jfrog/froggit-go v1.14.6/go.mod h1:TEJSzgiV+3D/GVGE8Y6j46ut1jrBLD1FL6WdMdKwwCE=
github.com/jfrog/froggit-go v1.14.7-0.20240324075617-8b1026034580 h1:gszovE2btg1q9Lw8FIlD6G4DIeujOkUfKdP5nhxv1Ys=
github.com/jfrog/froggit-go v1.14.7-0.20240324075617-8b1026034580/go.mod h1:TEJSzgiV+3D/GVGE8Y6j46ut1jrBLD1FL6WdMdKwwCE=
github.com/jfrog/gofrog v1.6.0 h1:jOwb37nHY2PnxePNFJ6e6279Pgkr3di05SbQQw47Mq8=
github.com/jfrog/gofrog v1.6.0/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg=
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
Expand Down
2 changes: 1 addition & 1 deletion scanpullrequest/scanallpullrequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func shouldScanPullRequest(repo utils.Repository, client vcsclient.VcsClient, pr
return true, nil
}
// if this is a Frogbot 'scan results' comment and not 're-scan' request comment, do not scan this pull request.
if outputwriter.IsFrogbotSummaryComment(repo.OutputWriter, comment.Content) {
if outputwriter.IsFrogbotComment(comment.Content) {
return false, nil
}
}
Expand Down
6 changes: 3 additions & 3 deletions scanpullrequest/scanallpullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ func TestShouldNotScanPullRequestReScan(t *testing.T) {
client := CreateMockVcsClient(t)
prID := 0
client.EXPECT().ListPullRequestComments(context.Background(), gitParams.RepoOwner, gitParams.RepoName, prID).Return([]vcsclient.CommentInfo{
{Content: outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.VulnerabilitiesPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(1, 0)},
{Content: outputwriter.MarkdownComment(outputwriter.ReviewCommentId) + outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.VulnerabilitiesPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(1, 0)},
{Content: utils.RescanRequestComment, Created: time.Unix(1, 1)},
{Content: outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.NoVulnerabilityPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(3, 0)},
{Content: outputwriter.MarkdownComment(outputwriter.ReviewCommentId) + outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.NoVulnerabilityPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(3, 0)},
}, nil)
shouldScan, err := shouldScanPullRequest(*gitParams, client, prID)
assert.NoError(t, err)
Expand All @@ -81,7 +81,7 @@ func TestShouldNotScanPullRequest(t *testing.T) {
client := CreateMockVcsClient(t)
prID := 0
client.EXPECT().ListPullRequestComments(context.Background(), gitParams.RepoOwner, gitParams.RepoName, prID).Return([]vcsclient.CommentInfo{
{Content: outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.NoVulnerabilityPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(3, 0)},
{Content: outputwriter.MarkdownComment(outputwriter.ReviewCommentId) + outputwriter.MarkAsBold(outputwriter.GetSimplifiedTitle(outputwriter.NoVulnerabilityPrBannerSource)) + "text \n table\n text text text", Created: time.Unix(3, 0)},
}, nil)
shouldScan, err := shouldScanPullRequest(*gitParams, client, prID)
assert.NoError(t, err)
Expand Down
7 changes: 4 additions & 3 deletions scanpullrequest/scanpullrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -690,13 +690,14 @@ func prepareConfigAndClient(t *testing.T, configPath string, server *httptest.Se
}
utils.SetEnvAndAssert(t, map[string]string{utils.GitPullRequestIDEnv: "1"})

client, err := vcsclient.NewClientBuilder(vcsutils.GitLab).ApiEndpoint(server.URL).Token("123456").Build()
assert.NoError(t, err)

configData, err := utils.ReadConfigFromFileSystem(configPath)
assert.NoError(t, err)
configAggregator, err := utils.BuildRepoAggregator(configData, gitTestParams, &serverParams, utils.ScanPullRequest)
configAggregator, err := utils.BuildRepoAggregator(client, configData, gitTestParams, &serverParams, utils.ScanPullRequest)
assert.NoError(t, err)

client, err := vcsclient.NewClientBuilder(vcsutils.GitLab).ApiEndpoint(server.URL).Token("123456").Build()
assert.NoError(t, err)
return configAggregator, client
}

Expand Down
2 changes: 1 addition & 1 deletion scanrepository/scanmultiplerepositories_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestScanAndFixRepos(t *testing.T) {
}()

utils.CreateDotGitWithCommit(t, testDir, port, testRepositories...)
configAggregator, err := utils.BuildRepoAggregator(configData, &gitTestParams, &serverParams, utils.ScanMultipleRepositories)
configAggregator, err := utils.BuildRepoAggregator(client, configData, &gitTestParams, &serverParams, utils.ScanMultipleRepositories)
assert.NoError(t, err)

var cmd = ScanMultipleRepositories{dryRun: true, dryRunRepoPath: testDir}
Expand Down
95 changes: 60 additions & 35 deletions scanrepository/scanrepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Reposito
cfp.aggregateFixes = repository.Git.AggregateFixes
// Set the outputwriter interface for the relevant vcs git provider
cfp.OutputWriter = outputwriter.GetCompatibleOutputWriter(repository.GitProvider)
cfp.OutputWriter.SetSizeLimit(client)
// Set the git client to perform git operations
cfp.gitManager, err = utils.NewGitManager().
SetAuth(cfp.scanDetails.Username, cfp.scanDetails.Token).
Expand Down Expand Up @@ -153,7 +154,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er
vulnerabilitiesByPathMap[fullPathWd] = currPathVulnerabilities
}
if fixNeeded {
return cfp.fixVulnerablePackages(vulnerabilitiesByPathMap)
return cfp.fixVulnerablePackages(repository, vulnerabilitiesByPathMap)
}
return nil
}
Expand Down Expand Up @@ -186,24 +187,24 @@ func (cfp *ScanRepositoryCmd) getVulnerabilitiesMap(scanResults *xrayutils.Resul
return vulnerabilitiesMap, nil
}

func (cfp *ScanRepositoryCmd) fixVulnerablePackages(vulnerabilitiesByWdMap map[string]map[string]*utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) fixVulnerablePackages(repository *utils.Repository, vulnerabilitiesByWdMap map[string]map[string]*utils.VulnerabilityDetails) (err error) {
if cfp.aggregateFixes {
return cfp.fixIssuesSinglePR(vulnerabilitiesByWdMap)
return cfp.fixIssuesSinglePR(repository, vulnerabilitiesByWdMap)
}
return cfp.fixIssuesSeparatePRs(vulnerabilitiesByWdMap)
return cfp.fixIssuesSeparatePRs(repository, vulnerabilitiesByWdMap)
}

func (cfp *ScanRepositoryCmd) fixIssuesSeparatePRs(vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails) error {
func (cfp *ScanRepositoryCmd) fixIssuesSeparatePRs(repository *utils.Repository, vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails) error {
var err error
for fullPath, vulnerabilities := range vulnerabilitiesMap {
if e := cfp.fixProjectVulnerabilities(fullPath, vulnerabilities); e != nil {
if e := cfp.fixProjectVulnerabilities(repository, fullPath, vulnerabilities); e != nil {
err = errors.Join(err, fmt.Errorf("the following errors occured while fixing vulnerabilities in '%s':\n%s", fullPath, e))
}
}
return err
}

func (cfp *ScanRepositoryCmd) fixProjectVulnerabilities(fullProjectPath string, vulnerabilities map[string]*utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) fixProjectVulnerabilities(repository *utils.Repository, fullProjectPath string, vulnerabilities map[string]*utils.VulnerabilityDetails) (err error) {
// Update the working directory to the project's current working directory
projectWorkingDir := utils.GetRelativeWd(fullProjectPath, cfp.baseWd)

Expand All @@ -220,7 +221,7 @@ func (cfp *ScanRepositoryCmd) fixProjectVulnerabilities(fullProjectPath string,

// Fix every vulnerability in a separate pull request and branch
for _, vulnerability := range vulnerabilities {
if e := cfp.fixSinglePackageAndCreatePR(vulnerability); e != nil {
if e := cfp.fixSinglePackageAndCreatePR(repository, vulnerability); e != nil {
err = errors.Join(err, cfp.handleUpdatePackageErrors(e))
}

Expand Down Expand Up @@ -265,13 +266,13 @@ func (cfp *ScanRepositoryCmd) fixMultiplePackages(fullProjectPath string, vulner
// If the scan results are the same, no action is taken.
// Otherwise, it performs a force push to the same branch and reopens the pull request if it was closed.
// Only one aggregated pull request should remain open at all times.
func (cfp *ScanRepositoryCmd) fixIssuesSinglePR(vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) fixIssuesSinglePR(repository *utils.Repository, vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails) (err error) {
aggregatedFixBranchName := cfp.gitManager.GenerateAggregatedFixBranchName(cfp.scanDetails.BaseBranch(), cfp.projectTech)
existingPullRequestDetails, err := cfp.getOpenPullRequestBySourceBranch(aggregatedFixBranchName)
if err != nil {
return
}
return cfp.aggregateFixAndOpenPullRequest(vulnerabilitiesMap, aggregatedFixBranchName, existingPullRequestDetails)
return cfp.aggregateFixAndOpenPullRequest(repository, vulnerabilitiesMap, aggregatedFixBranchName, existingPullRequestDetails)
}

// Handles possible error of update package operation
Expand All @@ -294,7 +295,7 @@ func (cfp *ScanRepositoryCmd) handleUpdatePackageErrors(err error) error {

// Creates a branch for the fixed package and open pull request against the target branch.
// In case a branch already exists on remote, we skip it.
func (cfp *ScanRepositoryCmd) fixSinglePackageAndCreatePR(vulnDetails *utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) fixSinglePackageAndCreatePR(repository *utils.Repository, vulnDetails *utils.VulnerabilityDetails) (err error) {
fixVersion := vulnDetails.SuggestedFixedVersion
log.Debug("Attempting to fix", fmt.Sprintf("%s:%s", vulnDetails.ImpactedDependencyName, vulnDetails.ImpactedDependencyVersion), "with", fixVersion)
fixBranchName, err := cfp.gitManager.GenerateFixBranchName(cfp.scanDetails.BaseBranch(), vulnDetails.ImpactedDependencyName, fixVersion)
Expand Down Expand Up @@ -327,14 +328,14 @@ func (cfp *ScanRepositoryCmd) fixSinglePackageAndCreatePR(vulnDetails *utils.Vul
if err = cfp.updatePackageToFixedVersion(vulnDetails); err != nil {
return
}
if err = cfp.openFixingPullRequest(fixBranchName, vulnDetails); err != nil {
if err = cfp.openFixingPullRequest(repository, fixBranchName, vulnDetails); err != nil {
return errors.Join(fmt.Errorf("failed while creating a fixing pull request for: %s with version: %s with error: ", vulnDetails.ImpactedDependencyName, fixVersion), err)
}
log.Info(fmt.Sprintf("Created Pull Request updating dependency '%s' to version '%s'", vulnDetails.ImpactedDependencyName, vulnDetails.SuggestedFixedVersion))
return
}

func (cfp *ScanRepositoryCmd) openFixingPullRequest(fixBranchName string, vulnDetails *utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) openFixingPullRequest(repository *utils.Repository, fixBranchName string, vulnDetails *utils.VulnerabilityDetails) (err error) {
log.Debug("Checking if there are changes to commit")
isClean, err := cfp.gitManager.IsClean()
if err != nil {
Expand All @@ -351,54 +352,78 @@ func (cfp *ScanRepositoryCmd) openFixingPullRequest(fixBranchName string, vulnDe
if err = cfp.gitManager.Push(false, fixBranchName); err != nil {
return
}
pullRequestTitle, prBody, err := cfp.preparePullRequestDetails(vulnDetails)
return cfp.handleFixPullRequestContent(repository, fixBranchName, nil, vulnDetails)
}

func (cfp *ScanRepositoryCmd) handleFixPullRequestContent(repository *utils.Repository, fixBranchName string, pullRequestInfo *vcsclient.PullRequestInfo, vulnerabilities ...*utils.VulnerabilityDetails) (err error) {
pullRequestTitle, prBody, extraComments, err := cfp.preparePullRequestDetails(vulnerabilities...)
if err != nil {
return
}
log.Debug("Creating Pull Request form:", fixBranchName, " to:", cfp.scanDetails.BaseBranch())
return cfp.scanDetails.Client().CreatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, fixBranchName, cfp.scanDetails.BaseBranch(), pullRequestTitle, prBody)
// Update PR description
if pullRequestInfo, err = cfp.createOrUpdatePullRequest(repository, pullRequestInfo, fixBranchName, pullRequestTitle, prBody); err != nil {
return
}
// Update PR extra comments
client := cfp.scanDetails.Client()
for _, comment := range extraComments {
if err = client.AddPullRequestComment(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, comment, int(pullRequestInfo.ID)); err != nil {
err = errors.New("couldn't add pull request comment: " + err.Error())
return
}
}
return
}

func (cfp *ScanRepositoryCmd) createOrUpdatePullRequest(repository *utils.Repository, pullRequestInfo *vcsclient.PullRequestInfo, fixBranchName, pullRequestTitle, prBody string) (prInfo *vcsclient.PullRequestInfo, err error) {
if pullRequestInfo == nil {
log.Info("Creating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch())
if err = cfp.scanDetails.Client().CreatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, fixBranchName, cfp.scanDetails.BaseBranch(), pullRequestTitle, prBody); err != nil {
return
}
return cfp.getOpenPullRequestBySourceBranch(fixBranchName)
}
log.Info("Updating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch())
if err = cfp.scanDetails.Client().UpdatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, pullRequestTitle, prBody, pullRequestInfo.Target.Name, int(pullRequestInfo.ID), vcsutils.Open); err != nil {
return
}
// Delete old extra comments
return pullRequestInfo, utils.DeletePullRequestComments(repository, cfp.scanDetails.Client(), int(pullRequestInfo.ID))
}

// openAggregatedPullRequest handles the opening or updating of a pull request when the aggregate mode is active.
// If a pull request is already open, Frogbot will update the branch and the pull request body.
func (cfp *ScanRepositoryCmd) openAggregatedPullRequest(fixBranchName string, pullRequestInfo *vcsclient.PullRequestInfo, vulnerabilities []*utils.VulnerabilityDetails) (err error) {
func (cfp *ScanRepositoryCmd) openAggregatedPullRequest(repository *utils.Repository, fixBranchName string, pullRequestInfo *vcsclient.PullRequestInfo, vulnerabilities []*utils.VulnerabilityDetails) (err error) {
commitMessage := cfp.gitManager.GenerateAggregatedCommitMessage(cfp.projectTech)
if err = cfp.gitManager.AddAllAndCommit(commitMessage); err != nil {
return
}
if err = cfp.gitManager.Push(true, fixBranchName); err != nil {
return
}
pullRequestTitle, prBody, err := cfp.preparePullRequestDetails(vulnerabilities...)
if err != nil {
return
}
if pullRequestInfo == nil {
log.Info("Creating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch())
return cfp.scanDetails.Client().CreatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, fixBranchName, cfp.scanDetails.BaseBranch(), pullRequestTitle, prBody)
}
log.Info("Updating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch())
return cfp.scanDetails.Client().UpdatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, pullRequestTitle, prBody, pullRequestInfo.Target.Name, int(pullRequestInfo.ID), vcsutils.Open)
return cfp.handleFixPullRequestContent(repository, fixBranchName, pullRequestInfo, vulnerabilities...)
}

func (cfp *ScanRepositoryCmd) preparePullRequestDetails(vulnerabilitiesDetails ...*utils.VulnerabilityDetails) (prTitle string, prBody string, err error) {
func (cfp *ScanRepositoryCmd) preparePullRequestDetails(vulnerabilitiesDetails ...*utils.VulnerabilityDetails) (prTitle, prBody string, otherComments []string, err error) {
if cfp.dryRun && cfp.aggregateFixes {
// For testings, don't compare pull request body as scan results order may change.
return cfp.gitManager.GenerateAggregatedPullRequestTitle(cfp.projectTech), "", nil
return cfp.gitManager.GenerateAggregatedPullRequestTitle(cfp.projectTech), "", []string{}, nil
}
vulnerabilitiesRows := utils.ExtractVulnerabilitiesDetailsToRows(vulnerabilitiesDetails)
prBody = utils.GenerateFixPullRequestDetails(vulnerabilitiesRows, cfp.OutputWriter)

prBody, extraComments := utils.GenerateFixPullRequestDetails(vulnerabilitiesRows, cfp.OutputWriter)

if cfp.aggregateFixes {
var scanHash string
if scanHash, err = utils.VulnerabilityDetailsToMD5Hash(vulnerabilitiesRows...); err != nil {
return
}
return cfp.gitManager.GenerateAggregatedPullRequestTitle(cfp.projectTech), prBody + outputwriter.MarkdownComment(fmt.Sprintf("Checksum: %s", scanHash)), nil
return cfp.gitManager.GenerateAggregatedPullRequestTitle(cfp.projectTech), prBody + outputwriter.MarkdownComment(fmt.Sprintf("Checksum: %s", scanHash)), extraComments, nil
}
// In separate pull requests there is only one vulnerability
vulnDetails := vulnerabilitiesDetails[0]
pullRequestTitle := cfp.gitManager.GeneratePullRequestTitle(vulnDetails.ImpactedDependencyName, vulnDetails.SuggestedFixedVersion)
return pullRequestTitle, prBody, nil
return pullRequestTitle, prBody, extraComments, nil
}

func (cfp *ScanRepositoryCmd) cloneRepositoryAndCheckoutToBranch() (tempWd string, restoreDir func() error, err error) {
Expand Down Expand Up @@ -536,7 +561,7 @@ func (cfp *ScanRepositoryCmd) getOpenPullRequestBySourceBranch(branchName string
return
}

func (cfp *ScanRepositoryCmd) aggregateFixAndOpenPullRequest(vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails, aggregatedFixBranchName string, existingPullRequestInfo *vcsclient.PullRequestInfo) (err error) {
func (cfp *ScanRepositoryCmd) aggregateFixAndOpenPullRequest(repository *utils.Repository, vulnerabilitiesMap map[string]map[string]*utils.VulnerabilityDetails, aggregatedFixBranchName string, existingPullRequestInfo *vcsclient.PullRequestInfo) (err error) {
log.Info("-----------------------------------------------------------------")
log.Info("Starting aggregated dependencies fix")

Expand Down Expand Up @@ -575,8 +600,8 @@ func (cfp *ScanRepositoryCmd) aggregateFixAndOpenPullRequest(vulnerabilitiesMap
return
}
if len(fixedVulnerabilities) > 0 {
if e = cfp.openAggregatedPullRequest(aggregatedFixBranchName, existingPullRequestInfo, fixedVulnerabilities); e != nil {
err = errors.Join(err, fmt.Errorf("failed while creating aggreagted pull request. Error: \n%s", e.Error()))
if e = cfp.openAggregatedPullRequest(repository, aggregatedFixBranchName, existingPullRequestInfo, fixedVulnerabilities); e != nil {
err = errors.Join(err, fmt.Errorf("failed while creating aggregated pull request. Error: \n%s", e.Error()))
}
}
log.Info("-----------------------------------------------------------------")
Expand Down
Loading

0 comments on commit 206b4b8

Please sign in to comment.