Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ type FrogbotCommand interface {
Run(config utils.RepoAggregator, client vcsclient.VcsClient) error
}

func Exec(command FrogbotCommand, name string) (err error) {
func Exec(command FrogbotCommand, commandName string) (err error) {
// Get frogbotUtils that contains the config, server, and VCS client
log.Info("Frogbot version:", utils.FrogbotVersion)
frogbotUtils, err := utils.GetFrogbotDetails()
frogbotUtils, err := utils.GetFrogbotDetails(commandName)
if err != nil {
return err
}
Expand All @@ -47,25 +47,25 @@ func Exec(command FrogbotCommand, name string) (err error) {

// Send a usage report
usageReportSent := make(chan error)
go utils.ReportUsage(name, frogbotUtils.ServerDetails, usageReportSent)
go utils.ReportUsage(commandName, frogbotUtils.ServerDetails, usageReportSent)

// Invoke the command interface
log.Info(fmt.Sprintf("Running Frogbot %q command", name))
err = command.Run(frogbotUtils.Repositories, frogbotUtils.Client)
log.Info(fmt.Sprintf("Running Frogbot %q command", commandName))
err = command.Run(frogbotUtils.Repositories, frogbotUtils.GitClient)

// Wait for a signal, letting us know that the usage reporting is done.
<-usageReportSent

if err == nil {
log.Info(fmt.Sprintf("Frogbot %q command finished successfully", name))
log.Info(fmt.Sprintf("Frogbot %q command finished successfully", commandName))
}
return err
}

func GetCommands() []*clitool.Command {
return []*clitool.Command{
{
Name: "scan-pull-request",
Name: utils.ScanPullRequest,
Aliases: []string{"spr"},
Usage: "Scans a pull request with JFrog Xray for security vulnerabilities.",
Action: func(ctx *clitool.Context) error {
Expand All @@ -74,7 +74,7 @@ func GetCommands() []*clitool.Command {
Flags: []clitool.Flag{},
},
{
Name: "create-fix-pull-requests",
Name: utils.CreateFixPullRequests,
Aliases: []string{"cfpr"},
Usage: "Scan the current branch and create pull requests with fixes if needed",
Action: func(ctx *clitool.Context) error {
Expand All @@ -83,7 +83,7 @@ func GetCommands() []*clitool.Command {
Flags: []clitool.Flag{},
},
{
Name: "scan-pull-requests",
Name: utils.ScanPullRequests,
Aliases: []string{"sprs"},
Usage: "Scans all the open pull requests within a single or multiple repositories with JFrog Xray for security vulnerabilities",
Action: func(ctx *clitool.Context) error {
Expand All @@ -92,7 +92,7 @@ func GetCommands() []*clitool.Command {
Flags: []clitool.Flag{},
},
{
Name: "scan-and-fix-repos",
Name: utils.ScanAndFixRepos,
Aliases: []string{"safr"},
Usage: "Scan single or multiple repositories and create pull requests with fixes if any security vulnerabilities are found",
Action: func(ctx *clitool.Context) error {
Expand Down
45 changes: 22 additions & 23 deletions commands/createfixpullrequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,29 @@ type CreateFixPullRequestsCmd struct {
handlers map[coreutils.Technology]packagehandlers.PackageHandler
}

func (cfp *CreateFixPullRequestsCmd) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient) error {
if err := utils.ValidateSingleRepoConfiguration(&repoAggregator); err != nil {
func (cfp *CreateFixPullRequestsCmd) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient) (err error) {
if err = utils.ValidateSingleRepoConfiguration(&repoAggregator); err != nil {
return err
}
repository := repoAggregator[0]
for _, branch := range repository.Branches {
err := cfp.scanAndFixRepository(&repository, branch, client)
if err != nil {
return err
if err = cfp.scanAndFixRepository(&repository, branch, client); err != nil {
return
}
}
return nil
return
}

func (cfp *CreateFixPullRequestsCmd) scanAndFixRepository(repository *utils.Repository, branch string, client vcsclient.VcsClient) (err error) {
cfp.baseWd, err = os.Getwd()
if err != nil {
if cfp.baseWd, err = os.Getwd(); err != nil {
return
}
if err = cfp.setCommandPrerequisites(repository, branch, client); err != nil {
return
}
cfp.setCommandPrerequisites(repository, branch, client)
if err = cfp.gitManager.Checkout(branch); err != nil {
return fmt.Errorf("failed to checkout to %s branch before scanning. The following error has been received:\n%s", branch, err.Error())
}
for i := range repository.Projects {
cfp.details.Project = &repository.Projects[i]
cfp.projectTech = ""
Expand All @@ -73,7 +76,7 @@ func (cfp *CreateFixPullRequestsCmd) scanAndFixRepository(repository *utils.Repo
return
}

func (cfp *CreateFixPullRequestsCmd) setCommandPrerequisites(repository *utils.Repository, branch string, client vcsclient.VcsClient) {
func (cfp *CreateFixPullRequestsCmd) setCommandPrerequisites(repository *utils.Repository, branch string, client vcsclient.VcsClient) (err error) {
cfp.details = utils.NewScanDetails(client, &repository.Server, &repository.Git).
SetXrayGraphScanParams(repository.Watches, repository.JFrogProjectKey).
SetFailOnInstallationErrors(*repository.FailOnSecurityIssues).
Expand All @@ -82,12 +85,14 @@ func (cfp *CreateFixPullRequestsCmd) setCommandPrerequisites(repository *utils.R
SetMinSeverity(repository.MinSeverity)
cfp.aggregateFixes = repository.Git.AggregateFixes
cfp.OutputWriter = utils.GetCompatibleOutputWriter(repository.GitProvider)
cfp.gitManager, err = utils.NewGitManager(cfp.dryRun, cfp.dryRunRepoPath, ".", "origin", cfp.details.Token, cfp.details.Username, cfp.details.Git)
return
}

func (cfp *CreateFixPullRequestsCmd) scanAndFixProject(repository *utils.Repository) 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 details of the vulnerable packages.x
// The value is a map of vulnerable package names -> the details 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 details.
vulnerabilitiesByPathMap := make(map[string]map[string]*utils.VulnerabilityDetails)
projectFullPathWorkingDirs := getFullPathWorkingDirs(cfp.details.Project.WorkingDirs, cfp.baseWd)
Expand Down Expand Up @@ -147,12 +152,6 @@ func (cfp *CreateFixPullRequestsCmd) getVulnerabilitiesMap(scanResults *xrayutil
}

func (cfp *CreateFixPullRequestsCmd) fixVulnerablePackages(vulnerabilitiesByWdMap map[string]map[string]*utils.VulnerabilityDetails) (err error) {
if cfp.gitManager == nil {
cfp.gitManager, err = utils.NewGitManager(cfp.dryRun, cfp.dryRunRepoPath, ".", "origin", cfp.details.Token, cfp.details.Username, cfp.details.Git)
if err != nil {
return
}
}
clonedRepoDir, restoreBaseDir, err := cfp.cloneRepository()
if err != nil {
return
Expand Down Expand Up @@ -186,12 +185,12 @@ func (cfp *CreateFixPullRequestsCmd) fixProjectVulnerabilities(fullProjectPath s

// 'CD' into the relevant working directory
if projectWorkingDir != "" {
restoreDir, err := utils.Chdir(projectWorkingDir)
if err != nil {
return err
var restoreDirFunc func() error
if restoreDirFunc, err = utils.Chdir(projectWorkingDir); err != nil {
return
}
defer func() {
err = errors.Join(err, restoreDir())
err = errors.Join(err, restoreDirFunc())
}()
}

Expand All @@ -203,7 +202,7 @@ func (cfp *CreateFixPullRequestsCmd) fixProjectVulnerabilities(fullProjectPath s

// After fixing the current vulnerability, checkout to the base branch to start fixing the next vulnerability
log.Debug("Running git checkout to base branch:", cfp.details.Branch())
if e := cfp.gitManager.CheckoutLocalBranch(cfp.details.Branch()); e != nil {
if e := cfp.gitManager.Checkout(cfp.details.Branch()); e != nil {
err = errors.Join(err, cfp.handleUpdatePackageErrors(e))
return
}
Expand Down Expand Up @@ -545,7 +544,7 @@ func (cfp *CreateFixPullRequestsCmd) aggregateFixAndOpenPullRequest(vulnerabilit
}
}
log.Info("-----------------------------------------------------------------")
if e := cfp.gitManager.CheckoutLocalBranch(cfp.details.Branch()); e != nil {
if e := cfp.gitManager.Checkout(cfp.details.Branch()); e != nil {
err = errors.Join(err, e)
}
return
Expand Down
6 changes: 3 additions & 3 deletions commands/createfixpullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestCreateFixPullRequestsCmd_Run(t *testing.T) {
repoName: "aggregate-multi-dir",
testDir: "createfixpullrequests/aggregate-multi-dir",
expectedBranches: []string{"frogbot-update-npm-dependencies"},
expectedDiff: []string{"diff --git a/npm1/package.json b/npm1/package.json\nindex ae09978..286211d 100644\n--- a/npm1/package.json\n+++ b/npm1/package.json\n@@ -9,8 +9,8 @@\n \"author\": \"\",\n \"license\": \"ISC\",\n \"dependencies\": {\n- \"uuid\": \"^9.0.0\",\n- \"minimatch\":\"3.0.2\",\n- \"mpath\": \"0.7.0\"\n+ \"minimatch\": \"^3.0.5\",\n+ \"mpath\": \"^0.8.4\",\n+ \"uuid\": \"^9.0.0\"\n }\n-}\n\\ No newline at end of file\n+}\ndiff --git a/npm2/package.json b/npm2/package.json\nindex be180a6..14b5c7a 100644\n--- a/npm2/package.json\n+++ b/npm2/package.json\n@@ -1,5 +1,5 @@\n {\n \"dependencies\": {\n- \"minimist\": \"^1.2.5\"\n+ \"minimist\": \"^1.2.6\"\n }\n }\n"},
expectedDiff: []string{"diff --git a/npm1/package.json b/npm1/package.json\nindex ae09978..286211d 100644\n--- a/npm1/package.json\n+++ b/npm1/package.json\n@@ -9,8 +9,8 @@\n \"author\": \"\",\n \"license\": \"ISC\",\n \"dependencies\": {\n- \"uuid\": \"^9.0.0\",\n- \"minimatch\":\"3.0.2\",\n- \"mpath\": \"0.7.0\"\n+ \"minimatch\": \"^3.0.5\",\n+ \"mpath\": \"^0.8.4\",\n+ \"uuid\": \"^9.0.0\"\n }\n-}\n\\ No newline at end of file\n+}\ndiff --git a/npm2/package.json b/npm2/package.json\nindex ff94a18..14b5c7a 100644\n--- a/npm2/package.json\n+++ b/npm2/package.json\n@@ -1,5 +1,5 @@\n {\n \"dependencies\": {\n- \"minimist\": \"1.2.5\"\n+ \"minimist\": \"^1.2.6\"\n }\n }\n"},
packageDescriptorPaths: []string{"npm1/package.json", "npm2/package.json"},
aggregateFixes: true,
configPath: "testdata/createfixpullrequests/aggregate-multi-dir/.frogbot/frogbot-config.yml",
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestCreateFixPullRequestsCmd_Run(t *testing.T) {
var port string
server := httptest.NewServer(createHttpHandler(t, &port, test.repoName))
port = server.URL[strings.LastIndex(server.URL, ":")+1:]
gitTestParams := utils.GitClientInfo{
gitTestParams := utils.Git{
GitProvider: vcsutils.GitHub,
VcsInfo: vcsclient.VcsInfo{
Token: "123456",
Expand Down Expand Up @@ -243,7 +243,7 @@ pr body
server.Close()
}()
port = server.URL[strings.LastIndex(server.URL, ":")+1:]
gitTestParams := &utils.GitClientInfo{
gitTestParams := &utils.Git{
GitProvider: vcsutils.GitHub,
VcsInfo: vcsclient.VcsInfo{
Token: "123456",
Expand Down
2 changes: 1 addition & 1 deletion commands/scanandfixrepos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestScanAndFixRepos(t *testing.T) {
defer server.Close()
port = server.URL[strings.LastIndex(server.URL, ":")+1:]

gitTestParams := utils.GitClientInfo{
gitTestParams := utils.Git{
GitProvider: vcsutils.GitHub,
RepoOwner: "jfrog",
VcsInfo: vcsclient.VcsInfo{
Expand Down
9 changes: 5 additions & 4 deletions commands/scanpullrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,8 @@ func TestVerifyGitHubFrogbotEnvironmentNoReviewers(t *testing.T) {

func TestVerifyGitHubFrogbotEnvironmentOnPrem(t *testing.T) {
repoConfig := &utils.Repository{
Params: utils.Params{Git: utils.Git{GitClientInfo: utils.GitClientInfo{
VcsInfo: vcsclient.VcsInfo{APIEndpoint: "https://acme.vcs.io"}}},
Params: utils.Params{Git: utils.Git{
VcsInfo: vcsclient.VcsInfo{APIEndpoint: "https://acme.vcs.io"}},
},
}

Expand All @@ -609,7 +609,7 @@ func TestVerifyGitHubFrogbotEnvironmentOnPrem(t *testing.T) {
}

func prepareConfigAndClient(t *testing.T, configPath string, server *httptest.Server, serverParams coreconfig.ServerDetails) (utils.RepoAggregator, vcsclient.VcsClient) {
gitTestParams := &utils.GitClientInfo{
gitTestParams := &utils.Git{
GitProvider: vcsutils.GitHub,
RepoOwner: "jfrog",
VcsInfo: vcsclient.VcsInfo{
Expand Down Expand Up @@ -839,7 +839,8 @@ func TestDeletePreviousPullRequestMessages(t *testing.T) {
repository := &utils.Repository{
Params: utils.Params{
Git: utils.Git{
GitClientInfo: utils.GitClientInfo{RepoName: "repo", RepoOwner: "owner"},
RepoName: "repo",
RepoOwner: "owner",
PullRequestID: 17,
},
},
Expand Down
8 changes: 3 additions & 5 deletions commands/scanpullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ var gitParams = &utils.Repository{
OutputWriter: &utils.SimplifiedOutput{},
Params: utils.Params{
Git: utils.Git{
GitClientInfo: utils.GitClientInfo{
RepoOwner: "repo-owner",
Branches: []string{"master"},
RepoName: "repo-name",
},
RepoOwner: "repo-owner",
Branches: []string{"master"},
RepoName: "repo-name",
},
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fix tests
init
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0000000000000000000000000000000000000000 20cfb0cbabf22f1c12d626eec6e6dd834f4e43d1 Omer Zidkoni <[email protected]> 1689852711 +0300 commit (initial): fix tests
0000000000000000000000000000000000000000 59fbea5efff5cfe6a005f33aaadbf21d58dc4af8 Omer Zidkoni <[email protected]> 1691578013 +0300 commit (initial): init
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0000000000000000000000000000000000000000 20cfb0cbabf22f1c12d626eec6e6dd834f4e43d1 Omer Zidkoni <[email protected]> 1689852711 +0300 commit (initial): fix tests
0000000000000000000000000000000000000000 59fbea5efff5cfe6a005f33aaadbf21d58dc4af8 Omer Zidkoni <[email protected]> 1691578013 +0300 commit (initial): init
Binary file not shown.

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x��A� E]s�ٛ�)��x��� EC'!����+�|?y�'n��ٞF�(O��b,��2{k�P�ő�9`"a���wX[�p�ۇ�
�n���yIܮ�h��Hp֤��U��H����u��d5
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20cfb0cbabf22f1c12d626eec6e6dd834f4e43d1
59fbea5efff5cfe6a005f33aaadbf21d58dc4af8
49 changes: 21 additions & 28 deletions commands/utils/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,12 @@ func NewGitManager(dryRun bool, clonedRepoPath, projectPath, remoteName, token,
return &GitManager{repository: repository, dryRunRepoPath: clonedRepoPath, remoteName: remoteName, auth: basicAuth, dryRun: dryRun, customTemplates: templates, git: g}, nil
}

func (gm *GitManager) CheckoutLocalBranch(branchName string) error {
err := gm.createBranchAndCheckout(branchName, false)
if err != nil {
err = fmt.Errorf("'git checkout %s' failed with error: %s", branchName, err.Error())
func (gm *GitManager) Checkout(branchName string) error {
log.Debug("Running git checkout to branch:", branchName)
if err := gm.createBranchAndCheckout(branchName, false); err != nil {
return fmt.Errorf("'git checkout %s' failed with error: %s", branchName, err.Error())
}
log.Debug("Running git checkout to local branch:", branchName)
return err
return nil
}

func (gm *GitManager) Clone(destinationPath, branchName string) error {
Expand Down Expand Up @@ -164,6 +163,14 @@ func (gm *GitManager) createBranchAndCheckout(branchName string, create bool) er
return worktree.Checkout(checkoutConfig)
}

func getCurrentBranch(repository *git.Repository) (string, error) {
head, err := repository.Head()
if err != nil {
return "", err
}
return head.Name().Short(), nil
}

func (gm *GitManager) AddAllAndCommit(commitMessage string) error {
log.Debug("Running git add all and commit...")
err := gm.addAll()
Expand Down Expand Up @@ -388,28 +395,6 @@ func (gm *GitManager) generateHTTPSCloneUrl() (url string, err error) {
}
}

func (gm *GitManager) CheckoutRemoteBranch(branchName string) error {
var checkoutConfig *git.CheckoutOptions
if gm.dryRun {
// On dry runs we mimic remote as local branches.
checkoutConfig = &git.CheckoutOptions{
Branch: plumbing.NewBranchReferenceName(branchName),
Force: true,
}
} else {
checkoutConfig = &git.CheckoutOptions{
Branch: plumbing.NewRemoteReferenceName(gm.remoteName, branchName),
Force: true,
}
}
log.Debug("Running git checkout to remote branch:", branchName)
worktree, err := gm.repository.Worktree()
if err != nil {
return err
}
return worktree.Checkout(checkoutConfig)
}

func toBasicAuth(token, username string) *githttp.BasicAuth {
// The username can be anything except for an empty string
if username == "" {
Expand All @@ -429,6 +414,14 @@ func getFullBranchName(branchName string) plumbing.ReferenceName {
return plumbing.NewBranchReferenceName(plumbing.ReferenceName(branchName).Short())
}

func GetBranchFromDotGit() (string, error) {
currentRepo, err := git.PlainOpen(".")
if err != nil {
return "", errors.New("unable to retrieve the branch to scan, as the .git folder was not found in the current working directory. The error that was received: " + err.Error())
}
return getCurrentBranch(currentRepo)
}

func loadCustomTemplates(commitMessageTemplate, branchNameTemplate, pullRequestTitleTemplate string) (CustomTemplates, error) {
template := CustomTemplates{
commitMessageTemplate: commitMessageTemplate,
Expand Down
Loading