From 698aeb3fefbf0cc853ed159161b23b78abc69697 Mon Sep 17 00:00:00 2001 From: Oran Moshai <12291998+oranmoshai@users.noreply.github.com> Date: Tue, 31 May 2022 13:23:10 +0300 Subject: [PATCH] Feat/pr comment (#111) * feat(pr-comments): github support Adding PR comments for every secret or misconf findings * feat(comments): gitlab * lint Co-authored-by: oranmoshai --- go.mod | 7 +- go.sum | 17 +++-- pkg/buildClient/comments.go | 125 +++++++++++++++++++++++++++++++++ pkg/buildClient/upload.go | 14 +++- pkg/metadata/build_env_vars.go | 24 +++---- pkg/metadata/collector.go | 10 +-- pkg/metadata/pr.go | 10 +-- 7 files changed, 177 insertions(+), 30 deletions(-) create mode 100644 pkg/buildClient/comments.go diff --git a/go.mod b/go.mod index 8e7fa65f..7024621f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/aquasecurity/fanal v0.0.0-20220503163617-7b81bb08565f github.com/aquasecurity/go-dep-parser v0.0.0-20220503151658-d316f5cc2cff + github.com/aquasecurity/go-git-pr-commenter v0.0.0-20220530100252-61bd9b3af2df github.com/aquasecurity/trivy v0.27.1-0.20220426130527-b6baa65ff20b github.com/aquasecurity/trivy-db v0.0.0-20220327074450-74195d9604b2 github.com/mitchellh/mapstructure v1.5.0 @@ -12,7 +13,7 @@ require ( github.com/stretchr/testify v1.7.1 github.com/thoas/go-funk v0.9.2 github.com/twitchtv/twirp v8.1.2+incompatible - github.com/urfave/cli/v2 v2.4.0 + github.com/urfave/cli/v2 v2.5.1 go.uber.org/zap v1.21.0 golang.org/x/text v0.3.7 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 @@ -87,6 +88,8 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475 // indirect + github.com/google/go-github/v44 v44.1.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect @@ -160,7 +163,7 @@ require ( golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4 // indirect golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/tools v0.1.8 // indirect diff --git a/go.sum b/go.sum index 3f2d45e7..2e3c0f41 100644 --- a/go.sum +++ b/go.sum @@ -252,6 +252,8 @@ github.com/aquasecurity/go-dep-parser v0.0.0-20220503151658-d316f5cc2cff h1:YNlz github.com/aquasecurity/go-dep-parser v0.0.0-20220503151658-d316f5cc2cff/go.mod h1:7EOQWQmyavVPY3fScbbPdd3dB/b0Q4ZbJ/NZCvNKrLs= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s= +github.com/aquasecurity/go-git-pr-commenter v0.0.0-20220530100252-61bd9b3af2df h1:+XkzXb+3fykN/cG6/agPyaUrNgM+6AHeQy8ZFYwfbWY= +github.com/aquasecurity/go-git-pr-commenter v0.0.0-20220530100252-61bd9b3af2df/go.mod h1:qqqStAf73A6em7ZqAIKXFSgLp3dm5EQaPFfuLU5dU7E= github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 h1:eveqE9ivrt30CJ7dOajOfBavhZ4zPqHcZe/4tKp0alc= github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798/go.mod h1:hxbJZtKlO4P8sZ9nztizR6XLoE33O+BkPmuYQ4ACyz0= github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4= @@ -822,8 +824,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE= github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= @@ -831,7 +833,11 @@ github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475 h1:d github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475/go.mod h1:IwJblnDNiCs8sxubbfPNniYsUqr8m+nt7YbPzecsGuE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA= +github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -1535,8 +1541,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I= -github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg= +github.com/urfave/cli/v2 v2.5.1 h1:YKwdkyA0xTBzOaP2G0DVxBnCheHGP+Y9VbKAs4K1Ess= +github.com/urfave/cli/v2 v2.5.1/go.mod h1:oDzoM7pVwz6wHn5ogWgFUU1s4VJayeQS+aEZDqXIEJs= github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= @@ -1859,8 +1865,9 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2607,4 +2614,4 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= -sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= \ No newline at end of file +sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= diff --git a/pkg/buildClient/comments.go b/pkg/buildClient/comments.go new file mode 100644 index 00000000..9ccfbcb7 --- /dev/null +++ b/pkg/buildClient/comments.go @@ -0,0 +1,125 @@ +package buildClient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + + "github.com/aquasecurity/go-git-pr-commenter/pkg/commenter" + "github.com/aquasecurity/go-git-pr-commenter/pkg/commenter/github" + "github.com/aquasecurity/go-git-pr-commenter/pkg/commenter/gitlab" + "github.com/aquasecurity/trivy-plugin-aqua/pkg/metadata" + "github.com/aquasecurity/trivy-plugin-aqua/pkg/proto/buildsecurity" +) + +// prComments send results PR comments +func prComments(buildSystem string, result []*buildsecurity.Result) error { + var c = commenter.Repository(nil) + switch buildSystem { + case metadata.Github: + owner, repo, err := getGitHubRepositoryDetails() + if err != nil { + return err + } + prNumber, err := extractGitHubActionPrNumber() + if err != nil { + return err + } + r, err := github.NewGithub(os.Getenv("GITHUB_TOKEN"), + owner, + repo, + prNumber) + if err != nil { + return err + } + c = commenter.Repository(r) + case metadata.Gitlab: + r, err := gitlab.NewGitlab(os.Getenv("GITLAB_TOKEN")) + if err != nil { + return err + } + c = commenter.Repository(r) + default: + return nil + } + + for _, r := range result { + switch r.Type { + case buildsecurity.Result_TYPE_TERRAFORM, buildsecurity.Result_TYPE_CLOUDFORMATION, + buildsecurity.Result_TYPE_KUBERNETES, buildsecurity.Result_TYPE_DOCKERFILE, + buildsecurity.Result_TYPE_HCL, buildsecurity.Result_TYPE_YAML: + err := c.WriteMultiLineComment(r.Filename, returnMisconfMsg(r), int(r.StartLine), int(r.EndLine)) + if err != nil { + return fmt.Errorf("failed write misconfiguration comment: %w", err) + } + + case buildsecurity.Result_TYPE_SECRETS: + err := c.WriteMultiLineComment(r.Filename, returnSecretMsg(r), int(r.StartLine), int(r.EndLine)) + if err != nil { + return fmt.Errorf("failed write secret findings comment: %w", err) + } + } + } + + return nil +} + +func returnSecretMsg(r *buildsecurity.Result) string { + return fmt.Sprintf("### :warning: Aqua found issue"+ + "\nCATEGORY: %s "+ + "\nDESCRIPTION: %s "+ + "\nSEVERITY: %s "+ + "\nMATCH: %s", + r.Resource, + r.Title, + r.Severity.String(), + r.Message) +} +func returnMisconfMsg(r *buildsecurity.Result) string { + return fmt.Sprintf("### :warning: Aqua found issue "+ + "\nMISCONF ID: %s "+ + "\nCHECK: %s "+ + "\nSEVERITY: %s "+ + "\nMESSAGE: %s", + r.AVDID, + r.Title, + r.Severity.String(), + r.Message) +} + +func getGitHubRepositoryDetails() (owner, repo string, err error) { + r := os.Getenv("GITHUB_REPOSITORY") + s := strings.Split(r, "/") + if len(s) != 2 { + return owner, repo, + fmt.Errorf("failed unexpected value for GITHUB_REPOSITORY."+ + " Expected , found %v", r) + } + + return s[0], s[1], nil +} + +// extractGitHubActionPrNumber take the pull request number from the GitHub action run +func extractGitHubActionPrNumber() (int, error) { + githubEventFile := "/github/workflow/event.json" + file, err := ioutil.ReadFile(githubEventFile) + if err != nil { + return 0, fmt.Errorf("failed gitHub event payload not found in %s", githubEventFile) + } + + var data interface{} + err = json.Unmarshal(file, &data) + if err != nil { + return 0, err + } + payload := data.(map[string]interface{}) + + prNumber, err := strconv.Atoi(fmt.Sprintf("%v", payload["number"])) + if err != nil { + return 0, fmt.Errorf("failed not a valid PR") + } + return prNumber, nil +} diff --git a/pkg/buildClient/upload.go b/pkg/buildClient/upload.go index c1a61fab..50e10303 100644 --- a/pkg/buildClient/upload.go +++ b/pkg/buildClient/upload.go @@ -4,6 +4,8 @@ import ( "fmt" "net/http" + "github.com/aquasecurity/trivy-plugin-aqua/pkg/log" + "github.com/aquasecurity/trivy-plugin-aqua/pkg/scanner" "github.com/aquasecurity/trivy-plugin-aqua/pkg/metadata" @@ -29,6 +31,7 @@ func (bc *TwirpClient) Upload(results []*buildsecurity.Result, tags map[string]s run, buildID := metadata.GetBuildInfo(buildSystem) + triggeredBy := bc.c.String("triggered-by") createScanReq := &buildsecurity.CreateScanReq{ RepositoryID: bc.repoId, Results: results, @@ -37,7 +40,7 @@ func (bc *TwirpClient) Upload(results []*buildsecurity.Result, tags map[string]s Commit: commitId, System: buildSystem, Tags: tags, - TriggeredBy: scanner.MatchTriggeredBy(bc.c.String("triggered-by")), + TriggeredBy: scanner.MatchTriggeredBy(triggeredBy), Run: run, BuildID: buildID, } @@ -46,5 +49,14 @@ func (bc *TwirpClient) Upload(results []*buildsecurity.Result, tags map[string]s if err != nil { return fmt.Errorf("failed sending results with error: %w", err) } + + // Send pull request comments + if triggeredBy == "pr" && len(results) > 0 { + err = prComments(buildSystem, results) + if err != nil { + log.Logger.Info("failed send PR comment logging and continue the scan err: ", err) + } + } + return nil } diff --git a/pkg/metadata/build_env_vars.go b/pkg/metadata/build_env_vars.go index 6789e137..e761fe8f 100644 --- a/pkg/metadata/build_env_vars.go +++ b/pkg/metadata/build_env_vars.go @@ -1,12 +1,12 @@ package metadata const ( - bitbucket = "bitbucket" - jenkins = "jenkins" - gitlab = "gitlab" - codebuild = "codebuild" - azure = "azure" - github = "github" + Bitbucket = "bitbucket" + Jenkins = "jenkins" + Gitlab = "gitlab" + Codebuild = "codebuild" + Azure = "azure" + Github = "github" overrideBuildSystem = "OVERRIDE_BUILDSYSTEM" ) @@ -45,10 +45,10 @@ var possibleUserEnvVars = []string{ } var possibleBuildSystems = map[string]string{ - "BITBUCKET_COMMIT": bitbucket, - "GIT_COMMIT": jenkins, - "CI_COMMIT_SHA": gitlab, - "CODEBUILD_GIT_COMMIT": codebuild, - "BUILD_SOURCEBRANCH": azure, - "GITHUB_SHA": github, + "BITBUCKET_COMMIT": Bitbucket, + "GIT_COMMIT": Jenkins, + "CI_COMMIT_SHA": Gitlab, + "CODEBUILD_GIT_COMMIT": Codebuild, + "BUILD_SOURCEBRANCH": Azure, + "GITHUB_SHA": Github, } diff --git a/pkg/metadata/collector.go b/pkg/metadata/collector.go index 86864ea8..5c20892b 100644 --- a/pkg/metadata/collector.go +++ b/pkg/metadata/collector.go @@ -264,15 +264,15 @@ func lastLogsHead(scanPath string) (s []string, err error) { // GetBuildInfo the vendor build run number and id func GetBuildInfo(buildSystem string) (run string, buildID string) { switch buildSystem { - case github: + case Github: return os.Getenv("GITHUB_RUN_NUMBER"), os.Getenv("GITHUB_RUN_ID") - case bitbucket: + case Bitbucket: return os.Getenv("BITBUCKET_BUILD_NUMBER"), os.Getenv("BITBUCKET_PR_ID") - case gitlab: + case Gitlab: return os.Getenv("CI_JOB_ID"), os.Getenv("CI_MERGE_REQUEST_IID") - case azure: + case Azure: return os.Getenv("BUILD_BUILDID"), os.Getenv("SYSTEM_PULLREQUEST_PULLREQUESTID") - case jenkins: + case Jenkins: return os.Getenv("BUILD_ID"), os.Getenv("BUILD_NUMBER") default: return "", "" diff --git a/pkg/metadata/pr.go b/pkg/metadata/pr.go index 5f815bc6..9f9dba8f 100644 --- a/pkg/metadata/pr.go +++ b/pkg/metadata/pr.go @@ -9,17 +9,17 @@ import ( func GetBaseRef() (r string) { buildSystem := GetBuildSystem() switch buildSystem { - case azure: + case Azure: return fmt.Sprintf( "origin/%s", strings.ReplaceAll(os.Getenv("SYSTEM_PULLREQUEST_TARGETBRANCH"), "refs/heads/", "")) - case bitbucket: + case Bitbucket: return os.Getenv("BITBUCKET_PR_DESTINATION_COMMIT") - case github: + case Github: return "FETCH_HEAD" - case gitlab: + case Gitlab: return os.Getenv("CI_MERGE_REQUEST_DIFF_BASE_SHA") - case jenkins: + case Jenkins: return fmt.Sprintf("origin/%s", os.Getenv("CHANGE_TARGET")) default: return "origin/master"