Skip to content

Commit

Permalink
merge request decoration in gitlab (#600)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagobcx authored Nov 13, 2023
1 parent 6c1ba24 commit e146ff0
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ jobs:
PR_GITHUB_NAMESPACE: "checkmarx"
PR_GITHUB_REPO_NAME: "ast-cli"
PR_GITHUB_NUMBER: 418
PR_GITLAB_TOKEN : ${{ secrets.PR_GITLAB_TOKEN }}
PR_GITLAB_NAMESPACE: "tiagobcx"
PR_GITLAB_REPO_NAME: "testProject"
PR_GITLAB_PROJECT_ID: 40227565
PR_GITLAB_IID: 19
AZURE_ORG: ${{ secrets.AZURE_ORG }}
AZURE_PROJECT: ${{ secrets.AZURE_PROJECT }}
AZURE_REPOS: ${{ secrets.AZURE_REPOS }}
Expand Down
3 changes: 2 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func main() {
codebashing := viper.GetString(params.CodeBashingPathKey)
bfl := viper.GetString(params.BflPathKey)
prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey)
prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey)
descriptionsPath := viper.GetString(params.DescriptionsPathKey)
tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey)
resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey)
Expand All @@ -66,7 +67,7 @@ func main() {
bitBucketServerWrapper := bitbucketserver.NewBitbucketServerWrapper()
gitLabWrapper := wrappers.NewGitLabWrapper()
bflWrapper := wrappers.NewBflHTTPWrapper(bfl)
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath)
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath)
learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(descriptionsPath)
tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath)
jwtWrapper := wrappers.NewJwtWrapper()
Expand Down
90 changes: 85 additions & 5 deletions internal/commands/util/pr.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package util

import (
"fmt"

"github.com/MakeNowJust/heredoc"
"github.com/checkmarx/ast-cli/internal/logger"
"github.com/checkmarx/ast-cli/internal/params"
Expand All @@ -11,8 +13,9 @@ import (
)

const (
failedCreatingPrDecoration = "Failed creating PR Decoration"
errorCodeFormat = "%s: CODE: %d, %s\n"
failedCreatingGithubPrDecoration = "Failed creating github PR Decoration"
failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration"
errorCodeFormat = "%s: CODE: %d, %s\n"
)

func NewPRDecorationCommand(prWrapper wrappers.PRWrapper) *cobra.Command {
Expand All @@ -27,8 +30,10 @@ func NewPRDecorationCommand(prWrapper wrappers.PRWrapper) *cobra.Command {
}

prDecorationGithub := PRDecorationGithub(prWrapper)
prDecorationGitlab := PRDecorationGitlab(prWrapper)

cmd.AddCommand(prDecorationGithub)
cmd.AddCommand(prDecorationGitlab)
return cmd
}

Expand All @@ -54,8 +59,8 @@ func PRDecorationGithub(prWrapper wrappers.PRWrapper) *cobra.Command {

prDecorationGithub.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from")
prDecorationGithub.Flags().String(params.SCMTokenFlag, "", params.GithubTokenUsage)
prDecorationGithub.Flags().String(params.NamespaceFlag, "", params.NamespaceFlagUsage)
prDecorationGithub.Flags().String(params.RepoNameFlag, "", params.RepoNameFlagUsage)
prDecorationGithub.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Github"))
prDecorationGithub.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Github"))
prDecorationGithub.Flags().Int(params.PRNumberFlag, 0, params.PRNumberFlagUsage)

// Set the value for token to mask the scm token
Expand All @@ -71,6 +76,47 @@ func PRDecorationGithub(prWrapper wrappers.PRWrapper) *cobra.Command {
return prDecorationGithub
}

func PRDecorationGitlab(prWrapper wrappers.PRWrapper) *cobra.Command {
prDecorationGitlab := &cobra.Command{
Use: "gitlab",
Short: "Decorate gitlab PR with vulnerabilities",
Long: "Decorate gitlab PR with vulnerabilities",
Example: heredoc.Doc(
`
$ cx utils pr gitlab --scan-id <scan-id> --token <PAT> --namespace <organization> --repo-name <repository>
--iid <pr iid> --gitlab-project <gitlab project ID>
`,
),
Annotations: map[string]string{
"command:doc": heredoc.Doc(
`
`,
),
},
RunE: runPRDecorationGitlab(prWrapper),
}

prDecorationGitlab.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from")
prDecorationGitlab.Flags().String(params.SCMTokenFlag, "", params.GitLabTokenUsage)
prDecorationGitlab.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Gitlab"))
prDecorationGitlab.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Gitlab"))
prDecorationGitlab.Flags().Int(params.PRIidFlag, 0, params.PRIidFlagUsage)
prDecorationGitlab.Flags().Int(params.PRGitlabProjectFlag, 0, params.PRGitlabProjectFlagUsage)

// Set the value for token to mask the scm token
_ = viper.BindPFlag(params.SCMTokenFlag, prDecorationGitlab.Flags().Lookup(params.SCMTokenFlag))

// mark all fields as required\
_ = prDecorationGitlab.MarkFlagRequired(params.ScanIDFlag)
_ = prDecorationGitlab.MarkFlagRequired(params.SCMTokenFlag)
_ = prDecorationGitlab.MarkFlagRequired(params.NamespaceFlag)
_ = prDecorationGitlab.MarkFlagRequired(params.RepoNameFlag)
_ = prDecorationGitlab.MarkFlagRequired(params.PRIidFlag)
_ = prDecorationGitlab.MarkFlagRequired(params.PRGitlabProjectFlag)

return prDecorationGitlab
}

func runPRDecoration(prWrapper wrappers.PRWrapper) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
Expand All @@ -94,7 +140,41 @@ func runPRDecoration(prWrapper wrappers.PRWrapper) func(cmd *cobra.Command, args
}

if errorModel != nil {
return errors.Errorf(errorCodeFormat, failedCreatingPrDecoration, errorModel.Code, errorModel.Message)
return errors.Errorf(errorCodeFormat, failedCreatingGithubPrDecoration, errorModel.Code, errorModel.Message)
}

logger.Print(prResponse)

return nil
}
}

func runPRDecorationGitlab(prWrapper wrappers.PRWrapper) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
scmTokenFlag, _ := cmd.Flags().GetString(params.SCMTokenFlag)
namespaceFlag, _ := cmd.Flags().GetString(params.NamespaceFlag)
repoNameFlag, _ := cmd.Flags().GetString(params.RepoNameFlag)
iIDFlag, _ := cmd.Flags().GetInt(params.PRIidFlag)
gitlabProjectIDFlag, _ := cmd.Flags().GetInt(params.PRGitlabProjectFlag)

prModel := &wrappers.GitlabPRModel{
ScanID: scanID,
ScmToken: scmTokenFlag,
Namespace: namespaceFlag,
RepoName: repoNameFlag,
IiD: iIDFlag,
GitlabProjectID: gitlabProjectIDFlag,
}

prResponse, errorModel, err := prWrapper.PostGitlabPRDecoration(prModel)

if err != nil {
return err
}

if errorModel != nil {
return errors.Errorf(errorCodeFormat, failedCreatingGitlabPrDecoration, errorModel.Code, errorModel.Message)
}

logger.Print(prResponse)
Expand Down
8 changes: 8 additions & 0 deletions internal/commands/util/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ func TestNewPRDecorationCommandMustExist(t *testing.T) {
err := cmd.Execute()
assert.ErrorContains(t, err, "scan-id")
}

func TestNewMRDecorationCommandMustExist(t *testing.T) {
cmd := PRDecorationGitlab(nil)
assert.Assert(t, cmd != nil, "MR decoration command must exist")

err := cmd.Execute()
assert.ErrorContains(t, err, "scan-id")
}
1 change: 1 addition & 0 deletions internal/params/binds.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var EnvVarsBinds = []struct {
{KicsResultsPredicatesPathKey, KicsResultsPredicatesPathEnv, "api/kics-results-predicates"},
{BflPathKey, BflPathEnv, "api/bfl"},
{PRDecorationGithubPathKey, PRDecorationGithubPathEnv, "api/flow-publisher/pr/github"},
{PRDecorationGitlabPathKey, PRDecorationGitlabPathEnv, "api/flow-publisher/pr/gitlab"},
{DescriptionsPathKey, DescriptionsPathEnv, "api/queries/descriptions"},
{TenantConfigurationPathKey, TenantConfigurationPathEnv, "api/configuration/tenant"},
{UploadsPathKey, UploadsPathEnv, "api/uploads"},
Expand Down
1 change: 1 addition & 0 deletions internal/params/envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
KicsResultsPredicatesPathEnv = "CX_KICS_RESULTS_PREDICATES_PATH"
BflPathEnv = "CX_BFL_PATH"
PRDecorationGithubPathEnv = "CX_PR_DECORATION_GITHUB_PATH"
PRDecorationGitlabPathEnv = "CX_PR_DECORATION_GITLAB_PATH"
SastRmPathEnv = "CX_SAST_RM_PATH"
UploadsPathEnv = "CX_UPLOADS_PATH"
TokenExpirySecondsEnv = "CX_TOKEN_EXPIRY_SECONDS"
Expand Down
16 changes: 10 additions & 6 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,16 @@ const (
ScaFilterUsage = "SCA filter"

// PR decoration flags
NamespaceFlag = "namespace"
NamespaceFlagUsage = "Github namespace is required to post the comments"
RepoNameFlag = "repo-name"
RepoNameFlagUsage = "Github repository details"
PRNumberFlag = "pr-number"
PRNumberFlagUsage = "Pull Request number for posting notifications and comments"
NamespaceFlag = "namespace"
NamespaceFlagUsage = "%s namespace is required to post the comments"
RepoNameFlag = "repo-name"
RepoNameFlagUsage = "%s repository details"
PRNumberFlag = "pr-number"
PRNumberFlagUsage = "Pull Request number for posting notifications and comments"
PRIidFlag = "mr-iid"
PRIidFlagUsage = "Gitlab IID (internal ID) of the merge request"
PRGitlabProjectFlag = "gitlab-project-id"
PRGitlabProjectFlagUsage = "Gitlab project ID"

// Chat
ChatAPIKey = "chat-apikey"
Expand Down
1 change: 1 addition & 0 deletions internal/params/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
KicsResultsPathKey = strings.ToLower(KicsResultsPathEnv)
BflPathKey = strings.ToLower(BflPathEnv)
PRDecorationGithubPathKey = strings.ToLower(PRDecorationGithubPathEnv)
PRDecorationGitlabPathKey = strings.ToLower(PRDecorationGitlabPathEnv)
UploadsPathKey = strings.ToLower(UploadsPathEnv)
SastRmPathKey = strings.ToLower(SastRmPathEnv)
AccessKeyIDConfigKey = strings.ToLower(AccessKeyIDEnv)
Expand Down
4 changes: 4 additions & 0 deletions internal/wrappers/mock/pr-mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ func (pr *PRMockWrapper) PostPRDecoration(model *wrappers.PRModel) (
) {
return "PR comment created successfully.", nil, nil
}

func (pr *PRMockWrapper) PostGitlabPRDecoration(model *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) {
return "MR comment created successfully.", nil, nil
}
22 changes: 21 additions & 1 deletion internal/wrappers/pr-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ const (

type PRHTTPWrapper struct {
githubPath string
gitlabPath string
}

func NewHTTPPRWrapper(githubPath string) PRWrapper {
func NewHTTPPRWrapper(githubPath, gitlabPath string) PRWrapper {
return &PRHTTPWrapper{
githubPath: githubPath,
gitlabPath: gitlabPath,
}
}

Expand All @@ -43,6 +45,24 @@ func (r *PRHTTPWrapper) PostPRDecoration(model *PRModel) (
return handlePRResponseWithBody(resp, err)
}

func (r *PRHTTPWrapper) PostGitlabPRDecoration(model *GitlabPRModel) (
string,
*WebError,
error,
) {
clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey)
jsonBytes, err := json.Marshal(model)
if err != nil {
return "", nil, err
}
resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.gitlabPath, bytes.NewBuffer(jsonBytes), true, clientTimeout)
if err != nil {
return "", nil, err
}
defer resp.Body.Close()
return handlePRResponseWithBody(resp, err)
}

func handlePRResponseWithBody(resp *http.Response, err error) (string, *WebError, error) {
if err != nil {
return "", nil, err
Expand Down
10 changes: 10 additions & 0 deletions internal/wrappers/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ type PRModel struct {
PrNumber int `json:"prNumber"`
}

type GitlabPRModel struct {
ScanID string `json:"scanId"`
ScmToken string `json:"scmToken"`
Namespace string `json:"namespace"`
RepoName string `json:"repoName"`
IiD int `json:"iid"`
GitlabProjectID int `json:"gitlabProjectID"`
}

type PRWrapper interface {
PostPRDecoration(model *PRModel) (string, *WebError, error)
PostGitlabPRDecoration(model *GitlabPRModel) (string, *WebError, error)
}
58 changes: 55 additions & 3 deletions test/integration/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ const (
prGithubNamespace = "PR_GITHUB_NAMESPACE"
prGithubNumber = "PR_GITHUB_NUMBER"
prGithubRepoName = "PR_GITHUB_REPO_NAME"
prGitlabRepoName = "PR_GITLAB_REPO_NAME"
prGitlabToken = "PR_GITLAB_TOKEN"
prGitlabNamespace = "PR_GITLAB_NAMESPACE"
prGitlabProjectId = "PR_GITLAB_PROJECT_ID"
prGitlabIid = "PR_GITLAB_IID"
)

func TestPRDecorationSuccessCase(t *testing.T) {
func TestPRGithubDecorationSuccessCase(t *testing.T) {
scanID, _ := getRootScan(t)

args := []string{
Expand All @@ -39,7 +44,7 @@ func TestPRDecorationSuccessCase(t *testing.T) {
assert.NilError(t, err, "Error should be nil")
}

func TestPRDecorationFailure(t *testing.T) {
func TestPRGithubDecorationFailure(t *testing.T) {
args := []string{
"utils",
"pr",
Expand All @@ -56,5 +61,52 @@ func TestPRDecorationFailure(t *testing.T) {
os.Getenv(prGithubRepoName),
}
err, _ := executeCommand(t, args...)
assert.ErrorContains(t, err, "Failed creating PR Decoration")
assert.ErrorContains(t, err, "Failed creating github PR Decoration")
}

func TestPRGitlabDecorationSuccessCase(t *testing.T) {
scanID, _ := getRootScan(t)

args := []string{
"utils",
"pr",
"gitlab",
flag(params.ScanIDFlag),
scanID,
flag(params.SCMTokenFlag),
os.Getenv(prGitlabToken),
flag(params.NamespaceFlag),
os.Getenv(prGitlabNamespace),
flag(params.RepoNameFlag),
os.Getenv(prGitlabRepoName),
flag(params.PRGitlabProjectFlag),
os.Getenv(prGitlabProjectId),
flag(params.PRIidFlag),
os.Getenv(prGitlabIid),
}
err, _ := executeCommand(t, args...)
assert.NilError(t, err, "Error should be nil")
}

func TestPRGitlabDecorationFailure(t *testing.T) {

args := []string{
"utils",
"pr",
"gitlab",
flag(params.ScanIDFlag),
"",
flag(params.SCMTokenFlag),
os.Getenv(prGitlabToken),
flag(params.NamespaceFlag),
os.Getenv(prGitlabNamespace),
flag(params.RepoNameFlag),
os.Getenv(prGitlabRepoName),
flag(params.PRGitlabProjectFlag),
os.Getenv(prGitlabProjectId),
flag(params.PRIidFlag),
os.Getenv(prGitlabIid),
}
err, _ := executeCommand(t, args...)
assert.ErrorContains(t, err, "Failed creating gitlab MR Decoration")
}
3 changes: 2 additions & 1 deletion test/integration/util_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command {
bfl := viper.GetString(params.BflPathKey)
learnMore := viper.GetString(params.DescriptionsPathKey)
prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey)
prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey)
tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey)
resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey)
resultsSbomPath := viper.GetString(params.ResultsSbomReportPathKey)
Expand All @@ -98,7 +99,7 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command {
bitBucketWrapper := wrappers.NewBitbucketWrapper()
bflWrapper := wrappers.NewBflHTTPWrapper(bfl)
learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(learnMore)
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath)
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath,prDecorationGitlabPath)
tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath)
jwtWrapper := wrappers.NewJwtWrapper()
scaRealtimeWrapper := wrappers.NewHTTPScaRealTimeWrapper()
Expand Down

0 comments on commit e146ff0

Please sign in to comment.