github.com/jfrog/frogbot@v1.1.1-0.20231221090046-821a26f50338/scanpullrequest/scanallpullrequests.go (about)

     1  package scanpullrequest
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/jfrog/frogbot/utils"
     9  	"github.com/jfrog/frogbot/utils/outputwriter"
    10  	"github.com/jfrog/jfrog-client-go/utils/log"
    11  
    12  	"github.com/jfrog/froggit-go/vcsclient"
    13  )
    14  
    15  var errPullRequestScan = "pull request #%d scan in the '%s' repository returned the following error:\n%s"
    16  
    17  type ScanAllPullRequestsCmd struct {
    18  }
    19  
    20  func (cmd ScanAllPullRequestsCmd) Run(configAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) error {
    21  	for _, config := range configAggregator {
    22  		log.Info("Scanning all open pull requests for repository:", config.RepoName)
    23  		log.Info("-----------------------------------------------------------")
    24  		config.OutputWriter.SetHasInternetConnection(frogbotRepoConnection.IsConnected())
    25  		err := scanAllPullRequests(config, client)
    26  		if err != nil {
    27  			return err
    28  		}
    29  	}
    30  	return nil
    31  }
    32  
    33  // Scan pull requests as follows:
    34  // a. Retrieve all open pull requests
    35  // b. Find the ones that should be scanned (new PRs or PRs with a 're-scan' comment)
    36  // c. Audit the dependencies of the source and the target branches.
    37  // d. Compare the vulnerabilities found in source and target branches, and show only the new vulnerabilities added by the pull request.
    38  func scanAllPullRequests(repo utils.Repository, client vcsclient.VcsClient) (err error) {
    39  	openPullRequests, err := client.ListOpenPullRequests(context.Background(), repo.RepoOwner, repo.RepoName)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	for _, pr := range openPullRequests {
    44  		shouldScan, e := shouldScanPullRequest(repo, client, int(pr.ID))
    45  		if e != nil {
    46  			err = errors.Join(err, fmt.Errorf(errPullRequestScan, int(pr.ID), repo.RepoName, e.Error()))
    47  		}
    48  		if !shouldScan {
    49  			log.Info("Pull Request", pr.ID, "has already been scanned before. If you wish to scan it again, please comment \"rescan\".")
    50  			return
    51  		}
    52  		repo.PullRequestDetails = pr
    53  		if e = scanPullRequest(&repo, client); e != nil {
    54  			// If error, write it in errList and continue to the next PR.
    55  			err = errors.Join(err, fmt.Errorf(errPullRequestScan, int(pr.ID), repo.RepoName, e.Error()))
    56  		}
    57  	}
    58  	return
    59  }
    60  
    61  func shouldScanPullRequest(repo utils.Repository, client vcsclient.VcsClient, prID int) (shouldScan bool, err error) {
    62  	pullRequestsComments, err := utils.GetSortedPullRequestComments(client, repo.RepoOwner, repo.RepoName, prID)
    63  	if err != nil {
    64  		return
    65  	}
    66  
    67  	for _, comment := range pullRequestsComments {
    68  		// If this a 're-scan' request comment
    69  		if utils.IsFrogbotRescanComment(comment.Content) {
    70  			return true, nil
    71  		}
    72  		// if this is a Frogbot 'scan results' comment and not 're-scan' request comment, do not scan this pull request.
    73  		if outputwriter.IsFrogbotSummaryComment(repo.OutputWriter, comment.Content) {
    74  			return false, nil
    75  		}
    76  	}
    77  	// This is a new pull request, and it therefore should be scanned.
    78  	return true, nil
    79  }