From bfa795558e240087c0d3f496d465d50009d54835 Mon Sep 17 00:00:00 2001 From: Masayoshi Mizutani Date: Tue, 9 Jul 2024 08:55:36 +0900 Subject: [PATCH] Add the option to disable no detection comment (#113) --- pkg/controller/cli/serve/serve.go | 18 +++- pkg/usecase/comment_githug_pr.go | 14 +++ pkg/usecase/scan_github_repo_test.go | 123 +++++++++++++++++++++++++++ pkg/usecase/usecase.go | 8 ++ 4 files changed, 160 insertions(+), 3 deletions(-) diff --git a/pkg/controller/cli/serve/serve.go b/pkg/controller/cli/serve/serve.go index 8a501ec..8d594aa 100644 --- a/pkg/controller/cli/serve/serve.go +++ b/pkg/controller/cli/serve/serve.go @@ -26,8 +26,9 @@ import ( func New() *cli.Command { var ( - addr string - trivyPath string + addr string + trivyPath string + disableNoDetectionComment bool githubApp config.GitHubApp bigQuery config.BigQuery @@ -50,6 +51,12 @@ func New() *cli.Command { EnvVars: []string{"OCTOVY_TRIVY_PATH"}, Destination: &trivyPath, }, + &cli.BoolFlag{ + Name: "disable-no-detection-comment", + Usage: "Disable comment to PR if no detection", + EnvVars: []string{"OCTOVY_DISABLE_NO_DETECTION_COMMENT"}, + Destination: &disableNoDetectionComment, + }, } return &cli.Command{ @@ -109,7 +116,12 @@ func New() *cli.Command { clients := infra.New(infraOptions...) - uc := usecase.New(clients) + var ucOptions []usecase.Option + if disableNoDetectionComment { + ucOptions = append(ucOptions, usecase.WithDisableNoDetectionComment()) + } + + uc := usecase.New(clients, ucOptions...) s := server.New(uc, server.WithGitHubSecret(githubApp.Secret)) serverErr := make(chan error, 1) diff --git a/pkg/usecase/comment_githug_pr.go b/pkg/usecase/comment_githug_pr.go index dc445ae..0e3d38d 100644 --- a/pkg/usecase/comment_githug_pr.go +++ b/pkg/usecase/comment_githug_pr.go @@ -73,6 +73,20 @@ func (x *UseCase) CommentGitHubPR(ctx context.Context, input *model.ScanGitHubRe return err } + if x.disableNoDetectionComment { + var fixableVulnCount int + for _, result := range report.Results { + for _, vuln := range result.Vulnerabilities { + if vuln.FixedVersion != "" { + fixableVulnCount++ + } + } + } + if fixableVulnCount == 0 { + return nil + } + } + if err := x.clients.GitHubApp().CreateIssueComment(ctx, &input.GitHubMetadata.GitHubRepo, input.InstallID, input.PullRequest.Number, body); err != nil { return err } diff --git a/pkg/usecase/scan_github_repo_test.go b/pkg/usecase/scan_github_repo_test.go index acbaed8..3de18fc 100644 --- a/pkg/usecase/scan_github_repo_test.go +++ b/pkg/usecase/scan_github_repo_test.go @@ -3,6 +3,7 @@ package usecase_test import ( "context" _ "embed" + "encoding/json" "os" "strconv" @@ -19,6 +20,7 @@ import ( "github.com/m-mizutani/octovy/pkg/domain/interfaces" "github.com/m-mizutani/octovy/pkg/domain/mock" "github.com/m-mizutani/octovy/pkg/domain/model" + "github.com/m-mizutani/octovy/pkg/domain/model/trivy" "github.com/m-mizutani/octovy/pkg/domain/types" "github.com/m-mizutani/octovy/pkg/infra" "github.com/m-mizutani/octovy/pkg/infra/gh" @@ -301,3 +303,124 @@ func TestScanGitHubRepoWithPR(t *testing.T) { gt.NoError(t, mockStorage.Unmarshal("m-mizutani/octovy/branch/main/scan.json.gz", &branchScan)) gt.Equal(t, branchScan.GitHub.Owner, "m-mizutani") } + +func TestScanGitHubRepoWithPRAndNoComment(t *testing.T) { + mockGH := &mock.GitHubMock{} + mockHTTP := &httpMock{} + mockTrivy := &trivyMock{} + mockBQ := &mock.BigQueryMock{} + mockStorage := mock.NewStorageMock() + + uc := usecase.New(infra.New( + infra.WithGitHubApp(mockGH), + infra.WithHTTPClient(mockHTTP), + infra.WithTrivy(mockTrivy), + infra.WithBigQuery(mockBQ), + infra.WithStorage(mockStorage), + ), usecase.WithDisableNoDetectionComment()) + + ctx := context.Background() + + mockHTTP.mockDo = func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(testCodeZip)), + }, nil + } + + mockTrivy.mockRun = func(ctx context.Context, args []string) error { + var report trivy.Report + gt.NoError(t, json.Unmarshal(testTrivyResult, &report)) + report.Results[0].Vulnerabilities[0].FixedVersion = "" + newResult := gt.R1(json.Marshal(report)).NoError(t) + + for i := range args { + if args[i] == "--output" { + fd := gt.R1(os.Create(args[i+1])).NoError(t) + gt.R1(fd.Write(newResult)).NoError(t) + gt.NoError(t, fd.Close()) + return nil + } + } + t.Fatalf("no --output option") + return nil + } + + var calledBQCreateTable int + mockBQ.CreateTableFunc = func(ctx context.Context, table types.BQTableID, md *bigquery.TableMetadata) error { + calledBQCreateTable++ + return nil + } + mockBQ.GetMetadataFunc = func(ctx context.Context, table types.BQTableID) (*bigquery.TableMetadata, error) { + return nil, nil + } + var calledBQInsert int + mockBQ.InsertFunc = func(ctx context.Context, tableID types.BQTableID, schema bigquery.Schema, data any) error { + calledBQInsert++ + return nil + } + + mockGH.GetArchiveURLFunc = func(ctx context.Context, input *interfaces.GetArchiveURLInput) (*url.URL, error) { + u := gt.R1(url.Parse("https://example.com/some/url.zip")).NoError(t) + return u, nil + } + var calledMockListIssueComments int + mockGH.ListIssueCommentsFunc = func(ctx context.Context, repo *model.GitHubRepo, id types.GitHubAppInstallID, prID int) ([]*model.GitHubIssueComment, error) { + calledMockListIssueComments++ + return nil, nil + } + var calledMockCreateIssueComment int + mockGH.CreateIssueCommentFunc = func(ctx context.Context, repo *model.GitHubRepo, id types.GitHubAppInstallID, prID int, body string) error { + calledMockCreateIssueComment++ + return nil + } + var calledMockGHCreateCheckRun int + mockGH.CreateCheckRunFunc = func(ctx context.Context, id types.GitHubAppInstallID, repo *model.GitHubRepo, commit string) (int64, error) { + calledMockGHCreateCheckRun++ + return 5, nil + } + var calledMockGHUpdateCheckRun int + mockGH.UpdateCheckRunFunc = func(ctx context.Context, id types.GitHubAppInstallID, repo *model.GitHubRepo, checkID int64, opt *github.UpdateCheckRunOptions) error { + gt.Equal(t, checkID, 5) + gt.Equal(t, *opt.Status, "completed") + gt.Equal(t, *opt.Conclusion, "success") + calledMockGHUpdateCheckRun++ + return nil + } + + gt.NoError(t, uc.ScanGitHubRepo(ctx, &model.ScanGitHubRepoInput{ + GitHubMetadata: model.GitHubMetadata{ + GitHubCommit: model.GitHubCommit{ + GitHubRepo: model.GitHubRepo{ + RepoID: 12345, + Owner: "m-mizutani", + RepoName: "octovy", + }, + CommitID: "f7c8851da7c7fcc46212fccfb6c9c4bda520f1ca", + Branch: "main", + }, + PullRequest: &model.GitHubPullRequest{ + Number: 123, + ID: 12345, + BaseBranch: "main", + BaseCommitID: "0f2324c367815ec3d928d21b892ce0ed9963aef3", + }, + }, + InstallID: 12345, + })) + + gt.Equal(t, calledBQCreateTable, 1) + gt.Equal(t, calledBQInsert, 1) + gt.Equal(t, calledMockListIssueComments, 1) + gt.Equal(t, calledMockCreateIssueComment, 0) + gt.Equal(t, calledMockGHCreateCheckRun, 1) + gt.Equal(t, calledMockGHUpdateCheckRun, 1) + + var commitScan *model.Scan + gt.NoError(t, mockStorage.Unmarshal("m-mizutani/octovy/commit/f7c8851da7c7fcc46212fccfb6c9c4bda520f1ca/scan.json.gz", &commitScan)) + gt.Equal(t, commitScan.GitHub.Owner, "m-mizutani") + + var branchScan *model.Scan + gt.NoError(t, mockStorage.Unmarshal("m-mizutani/octovy/branch/main/scan.json.gz", &branchScan)) + gt.Equal(t, branchScan.GitHub.Owner, "m-mizutani") +} diff --git a/pkg/usecase/usecase.go b/pkg/usecase/usecase.go index 29a22d3..1a2f1ee 100644 --- a/pkg/usecase/usecase.go +++ b/pkg/usecase/usecase.go @@ -8,6 +8,8 @@ import ( type UseCase struct { tableID types.BQTableID clients *infra.Clients + + disableNoDetectionComment bool } func New(clients *infra.Clients, options ...Option) *UseCase { @@ -30,3 +32,9 @@ func WithBigQueryTableID(tableID types.BQTableID) Option { x.tableID = tableID } } + +func WithDisableNoDetectionComment() Option { + return func(x *UseCase) { + x.disableNoDetectionComment = true + } +}