Skip to content

Commit 4f2d887

Browse files
Merge pull request #939 from Checkmarx/feature/prDecorationBB
Support PR Decoration For BB On-cloud and On-prem(AST-70121)
2 parents 1fdee89 + f7cebf7 commit 4f2d887

File tree

14 files changed

+484
-87
lines changed

14 files changed

+484
-87
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ jobs:
8484
BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }}
8585
BITBUCKET_PASSWORD: ${{ secrets.BITBUCKET_PASSWORD }}
8686
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
87+
PR_BITBUCKET_TOKEN: ${{ secrets.PR_BITBUCKET_TOKEN }}
88+
PR_BITBUCKET_NAMESPACE: "AstSystemTest"
89+
PR_BITBUCKET_REPO_NAME: "cliIntegrationTest"
90+
PR_BITBUCKET_ID: 1
8791
run: |
8892
sudo chmod +x ./internal/commands/.scripts/integration_up.sh ./internal/commands/.scripts/integration_down.sh
8993
./internal/commands/.scripts/integration_up.sh

.github/workflows/manual-integration-test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ jobs:
8787
BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }}
8888
BITBUCKET_PASSWORD: ${{ secrets.BITBUCKET_PASSWORD }}
8989
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
90+
PR_BITBUCKET_TOKEN: ${{ secrets.PR_BITBUCKET_TOKEN }}
91+
PR_BITBUCKET_NAMESPACE: "AstSystemTest"
92+
PR_BITBUCKET_REPO_NAME: "cliIntegrationTest"
93+
PR_BITBUCKET_ID: 1
9094
run: |
9195
sudo chmod +x ./internal/commands/.scripts/integration_up.sh ./internal/commands/.scripts/integration_down.sh
9296
./internal/commands/.scripts/integration_up.sh

cmd/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func main() {
4242
bfl := viper.GetString(params.BflPathKey)
4343
prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey)
4444
prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey)
45+
bitbucketServerPath := viper.GetString(params.PRDecorationBitbucketServerPathKey)
46+
bitbucketCloudPath := viper.GetString(params.PRDecorationBitbucketCloudPathKey)
4547
prDecorationAzurePath := viper.GetString(params.PRDecorationAzurePathKey)
4648
descriptionsPath := viper.GetString(params.DescriptionsPathKey)
4749
tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey)
@@ -73,7 +75,7 @@ func main() {
7375
bitBucketServerWrapper := bitbucketserver.NewBitbucketServerWrapper()
7476
gitLabWrapper := wrappers.NewGitLabWrapper()
7577
bflWrapper := wrappers.NewBflHTTPWrapper(bfl)
76-
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, prDecorationAzurePath)
78+
prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, bitbucketCloudPath, bitbucketServerPath, prDecorationAzurePath)
7779
learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(descriptionsPath)
7880
tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath)
7981
jwtWrapper := wrappers.NewJwtWrapper()

internal/commands/util/pr.go

Lines changed: 164 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package util
33
import (
44
"fmt"
55
"log"
6+
"strings"
67

78
"github.com/MakeNowJust/heredoc"
89
"github.com/checkmarx/ast-cli/internal/commands/policymanagement"
@@ -15,21 +16,23 @@ import (
1516
)
1617

1718
const (
18-
failedCreatingGithubPrDecoration = "Failed creating github PR Decoration"
19-
failedCreatingAzurePrDecoration = "Failed creating azure PR Decoration"
20-
failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration"
21-
errorCodeFormat = "%s: CODE: %d, %s\n"
22-
policyErrorFormat = "%s: Failed to get scanID policy information"
23-
waitDelayDefault = 5
24-
resultPolicyDefaultTimeout = 1
25-
failedGettingScanError = "Failed showing a scan"
26-
noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress."
27-
githubOnPremURLSuffix = "/api/v3/repos/"
28-
gitlabOnPremURLSuffix = "/api/v4/"
29-
githubCloudURL = "https://api.github.com/repos/"
30-
gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix
31-
azureCloudURL = "https://dev.azure.com/"
32-
errorAzureOnPremParams = "code-repository-url must be set when code-repository-username is set"
19+
failedCreatingGithubPrDecoration = "Failed creating github PR Decoration"
20+
failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration"
21+
failedCreatingBitbucketPrDecoration = "Failed creating bitbucket PR Decoration"
22+
failedCreatingAzurePrDecoration = "Failed creating azure PR Decoration"
23+
errorCodeFormat = "%s: CODE: %d, %s\n"
24+
policyErrorFormat = "%s: Failed to get scanID policy information"
25+
waitDelayDefault = 5
26+
resultPolicyDefaultTimeout = 1
27+
failedGettingScanError = "Failed showing a scan"
28+
noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress."
29+
githubOnPremURLSuffix = "/api/v3/repos/"
30+
gitlabOnPremURLSuffix = "/api/v4/"
31+
githubCloudURL = "https://api.github.com/repos/"
32+
gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix
33+
azureCloudURL = "https://dev.azure.com/"
34+
bitbucketCloudURL = "bitbucket.org"
35+
errorAzureOnPremParams = "code-repository-url must be set when code-repository-username is set"
3336
)
3437

3538
func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command {
@@ -45,10 +48,12 @@ func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers
4548

4649
prDecorationGithub := PRDecorationGithub(prWrapper, policyWrapper, scansWrapper)
4750
prDecorationGitlab := PRDecorationGitlab(prWrapper, policyWrapper, scansWrapper)
51+
prDecorationBitbucket := PRDecorationBitbucket(prWrapper, policyWrapper, scansWrapper)
4852
prDecorationAzure := PRDecorationAzure(prWrapper, policyWrapper, scansWrapper)
4953

5054
cmd.AddCommand(prDecorationGithub)
5155
cmd.AddCommand(prDecorationGitlab)
56+
cmd.AddCommand(prDecorationBitbucket)
5257
cmd.AddCommand(prDecorationAzure)
5358
return cmd
5459
}
@@ -205,6 +210,46 @@ func PRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.Poli
205210
return prDecorationAzure
206211
}
207212

213+
func PRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command {
214+
prDecorationBitbucket := &cobra.Command{
215+
Use: "bitbucket ",
216+
Short: "Decorate bitbucket PR with vulnerabilities",
217+
Long: "Decorate bitbucket PR with vulnerabilities",
218+
Example: heredoc.Doc(
219+
`
220+
$ cx utils pr bitbucket --scan-id <scan-id> --token <PAT> --namespace <username (required for cloud services)> --repo-name <repository-slug>
221+
--pr-id <pr number> --code-repository-url <bitbucket-server-url (required for self-hosted)>
222+
`,
223+
),
224+
Annotations: map[string]string{
225+
"command:doc": heredoc.Doc(
226+
`
227+
`,
228+
),
229+
},
230+
RunE: runPRDecorationBitbucket(prWrapper, policyWrapper, scansWrapper),
231+
}
232+
233+
prDecorationBitbucket.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from")
234+
prDecorationBitbucket.Flags().String(params.SCMTokenFlag, "", params.BitbucketTokenUsage)
235+
prDecorationBitbucket.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Bitbucket"))
236+
prDecorationBitbucket.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Bitbucket"))
237+
prDecorationBitbucket.Flags().Int(params.PRBBIDFlag, 0, params.PRBBIDFlagUsage)
238+
prDecorationBitbucket.Flags().String(params.ProjectKeyFlag, "", params.ProjectKeyFlagUsage)
239+
prDecorationBitbucket.Flags().String(params.CodeRepositoryFlag, "", params.CodeRepositoryFlagUsage)
240+
241+
// Set the value for token to mask the scm token
242+
_ = viper.BindPFlag(params.SCMTokenFlag, prDecorationBitbucket.Flags().Lookup(params.SCMTokenFlag))
243+
244+
// mark all fields as required\
245+
_ = prDecorationBitbucket.MarkFlagRequired(params.ScanIDFlag)
246+
_ = prDecorationBitbucket.MarkFlagRequired(params.SCMTokenFlag)
247+
_ = prDecorationBitbucket.MarkFlagRequired(params.RepoNameFlag)
248+
_ = prDecorationBitbucket.MarkFlagRequired(params.PRBBIDFlag)
249+
250+
return prDecorationBitbucket
251+
}
252+
208253
func runPRDecoration(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error {
209254
return func(cmd *cobra.Command, args []string) error {
210255
scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
@@ -320,7 +365,7 @@ func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers.
320365
APIURL: updatedAPIURL,
321366
}
322367

323-
prResponse, errorModel, err := prWrapper.PostGitlabPRDecoration(prModel)
368+
prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel)
324369

325370
if err != nil {
326371
return err
@@ -336,6 +381,53 @@ func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers.
336381
}
337382
}
338383

384+
func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error {
385+
return func(cmd *cobra.Command, args []string) error {
386+
scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
387+
scmTokenFlag, _ := cmd.Flags().GetString(params.SCMTokenFlag)
388+
namespaceFlag, _ := cmd.Flags().GetString(params.NamespaceFlag)
389+
repoNameFlag, _ := cmd.Flags().GetString(params.RepoNameFlag)
390+
prIDFlag, _ := cmd.Flags().GetInt(params.PRBBIDFlag)
391+
apiURL, _ := cmd.Flags().GetString(params.CodeRepositoryFlag)
392+
projectKey, _ := cmd.Flags().GetString(params.ProjectKeyFlag)
393+
394+
isCloud, flagRequiredErr := checkIsCloudAndValidateFlag(apiURL, namespaceFlag, projectKey)
395+
if flagRequiredErr != nil {
396+
return flagRequiredErr
397+
}
398+
399+
scanRunningOrQueued, err := IsScanRunningOrQueued(scansWrapper, scanID)
400+
401+
if err != nil {
402+
return err
403+
}
404+
405+
if scanRunningOrQueued {
406+
log.Println(noPRDecorationCreated)
407+
return nil
408+
}
409+
410+
policies, policyError := getScanViolatedPolicies(scansWrapper, policyWrapper, scanID, cmd)
411+
if policyError != nil {
412+
return errors.Errorf(policyErrorFormat, failedCreatingBitbucketPrDecoration)
413+
}
414+
415+
prModel := createBBPRModel(isCloud, scanID, scmTokenFlag, namespaceFlag, repoNameFlag, prIDFlag, apiURL, projectKey, policies)
416+
prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel)
417+
418+
if err != nil {
419+
return err
420+
}
421+
422+
if errorModel != nil {
423+
return errors.Errorf(errorCodeFormat, failedCreatingBitbucketPrDecoration, errorModel.Code, errorModel.Message)
424+
}
425+
426+
logger.Print(prResponse)
427+
return nil
428+
}
429+
}
430+
339431
func runPRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error {
340432
return func(cmd *cobra.Command, args []string) error {
341433
scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
@@ -381,7 +473,7 @@ func runPRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.P
381473
Policies: policies,
382474
APIURL: updatedAPIURL,
383475
}
384-
prResponse, errorModel, err := prWrapper.PostAzurePRDecoration(prModel)
476+
prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel)
385477
if err != nil {
386478
return err
387479
}
@@ -415,6 +507,61 @@ func updateScmTokenForAzure(scmTokenFlag, codeRepositoryUserName string) string
415507
return scmTokenFlag
416508
}
417509

510+
func formatRepoNameSlugBB(repoName string) string {
511+
repoSlug := strings.Replace(strings.TrimSpace(repoName), " ", "-", -1)
512+
return repoSlug
513+
}
514+
515+
func checkIsCloudAndValidateFlag(apiURL, namespaceFlag, projectKey string) (bool, error) {
516+
isCloud := isBitbucketCloud(apiURL)
517+
flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey)
518+
return isCloud, flagRequiredErr
519+
}
520+
521+
func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey string) error {
522+
if isCloud {
523+
if namespaceFlag == "" {
524+
return errors.New("namespace is required for Bitbucket Cloud")
525+
}
526+
} else {
527+
if projectKey == "" {
528+
return errors.New("project key is required for Bitbucket Server")
529+
}
530+
}
531+
return nil
532+
}
533+
534+
func isBitbucketCloud(apiURL string) bool {
535+
if apiURL == "" || strings.Contains(apiURL, bitbucketCloudURL) {
536+
return true
537+
}
538+
return false
539+
}
540+
541+
func createBBPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFlag string, prIDFlag int, apiURL, projectKey string, policies []wrappers.PrPolicy) interface{} {
542+
formattedRepoNameSlug := formatRepoNameSlugBB(repoNameFlag)
543+
544+
if isCloud {
545+
return &wrappers.BitbucketCloudPRModel{
546+
ScanID: scanID,
547+
ScmToken: scmTokenFlag,
548+
Namespace: namespaceFlag,
549+
RepoName: formattedRepoNameSlug,
550+
PRID: prIDFlag,
551+
Policies: policies,
552+
}
553+
}
554+
return &wrappers.BitbucketServerPRModel{
555+
ScanID: scanID,
556+
ScmToken: scmTokenFlag,
557+
ProjectKey: projectKey,
558+
RepoName: formattedRepoNameSlug,
559+
PRID: prIDFlag,
560+
Policies: policies,
561+
ServerURL: apiURL,
562+
}
563+
}
564+
418565
func getScanViolatedPolicies(scansWrapper wrappers.ScansWrapper, policyWrapper wrappers.PolicyWrapper, scanID string, cmd *cobra.Command) ([]wrappers.PrPolicy, error) {
419566
// retrieve scan model to get the projectID
420567
scanResponseModel, errorScanModel, err := scansWrapper.GetByID(scanID)

0 commit comments

Comments
 (0)