@@ -3,6 +3,7 @@ package util
33import (
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
1718const (
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
3538func 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+
208253func 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+
339431func 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+
418565func 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