diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d9484f42..bdb0d87ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,10 @@ jobs: AZURE_PROJECT: ${{ secrets.AZURE_PROJECT }} AZURE_REPOS: ${{ secrets.AZURE_REPOS }} AZURE_TOKEN: ${{ secrets.AZURE_TOKEN }} + AZURE_NEW_ORG: "azureAccountTests" + AZURE_PROJECT_NAME: "testsProject" + AZURE_PR_NUMBER: 1 + AZURE_NEW_TOKEN: ${{ secrets.AZURE_NEW_TOKEN }} BITBUCKET_WORKSPACE: ${{ secrets.BITBUCKET_WORKSPACE }} BITBUCKET_REPOS: ${{ secrets.BITBUCKET_REPOS }} BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} diff --git a/.github/workflows/manual-integration-test.yml b/.github/workflows/manual-integration-test.yml index 81219f263..59a30b05d 100644 --- a/.github/workflows/manual-integration-test.yml +++ b/.github/workflows/manual-integration-test.yml @@ -78,6 +78,10 @@ jobs: AZURE_PROJECT: ${{ secrets.AZURE_PROJECT }} AZURE_REPOS: ${{ secrets.AZURE_REPOS }} AZURE_TOKEN: ${{ secrets.AZURE_TOKEN }} + AZURE_NEW_ORG: "azureAccountTests" + AZURE_PROJECT_NAME: "testsProject" + AZURE_PR_NUMBER: 1 + AZURE_NEW_TOKEN: ${{ secrets.AZURE_NEW_TOKEN }} BITBUCKET_WORKSPACE: ${{ secrets.BITBUCKET_WORKSPACE }} BITBUCKET_REPOS: ${{ secrets.BITBUCKET_REPOS }} BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} diff --git a/cmd/main.go b/cmd/main.go index 8e6c70cfb..36ee14044 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -42,6 +42,7 @@ func main() { bfl := viper.GetString(params.BflPathKey) prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey) prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey) + prDecorationAzurePath := viper.GetString(params.PRDecorationAzurePathKey) descriptionsPath := viper.GetString(params.DescriptionsPathKey) tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey) resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey) @@ -72,7 +73,7 @@ func main() { bitBucketServerWrapper := bitbucketserver.NewBitbucketServerWrapper() gitLabWrapper := wrappers.NewGitLabWrapper() bflWrapper := wrappers.NewBflHTTPWrapper(bfl) - prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath) + prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, prDecorationAzurePath) learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(descriptionsPath) tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath) jwtWrapper := wrappers.NewJwtWrapper() diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index ab5207d0d..4060e3217 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -16,6 +16,7 @@ import ( const ( failedCreatingGithubPrDecoration = "Failed creating github PR Decoration" + failedCreatingAzurePrDecoration = "Failed creating azure PR Decoration" failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" errorCodeFormat = "%s: CODE: %d, %s\n" policyErrorFormat = "%s: Failed to get scanID policy information" @@ -27,6 +28,8 @@ const ( gitlabOnPremURLSuffix = "/api/v4/" githubCloudURL = "https://api.github.com/repos/" gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix + azureCloudURL = "https://dev.azure.com/" + errorAzureOnPremParams = "code-repository-url must be set when code-repository-username is set" ) func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { @@ -42,9 +45,11 @@ func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers prDecorationGithub := PRDecorationGithub(prWrapper, policyWrapper, scansWrapper) prDecorationGitlab := PRDecorationGitlab(prWrapper, policyWrapper, scansWrapper) + prDecorationAzure := PRDecorationAzure(prWrapper, policyWrapper, scansWrapper) cmd.AddCommand(prDecorationGithub) cmd.AddCommand(prDecorationGitlab) + cmd.AddCommand(prDecorationAzure) return cmd } @@ -159,6 +164,47 @@ func PRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers.Pol return prDecorationGitlab } +func PRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { + prDecorationAzure := &cobra.Command{ + Use: "azure", + Short: "Decorate azure PR with vulnerabilities", + Long: "Decorate azure PR with vulnerabilities", + Example: heredoc.Doc( + ` + $ cx utils pr azure --scan-id --token --namespace --project + --pr-number --code-repository-url + `, + ), + Annotations: map[string]string{ + "command:doc": heredoc.Doc( + `https://checkmarx.com/resource/documents/en/34965-68653-utils.html + `, + ), + }, + RunE: runPRDecorationAzure(prWrapper, policyWrapper, scansWrapper), + } + + prDecorationAzure.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from") + prDecorationAzure.Flags().String(params.SCMTokenFlag, "", params.AzureTokenUsage) + prDecorationAzure.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Azure")) + prDecorationAzure.Flags().String(params.AzureProjectFlag, "", fmt.Sprintf(params.AzureProjectFlagUsage)) + prDecorationAzure.Flags().Int(params.PRNumberFlag, 0, params.PRNumberFlagUsage) + prDecorationAzure.Flags().String(params.CodeRepositoryFlag, "", params.CodeRepositoryFlagUsage) + prDecorationAzure.Flags().String(params.CodeRespositoryUsernameFlag, "", fmt.Sprintf(params.CodeRespositoryUsernameFlagUsage)) + + // Set the value for token to mask the scm token + _ = viper.BindPFlag(params.SCMTokenFlag, prDecorationAzure.Flags().Lookup(params.SCMTokenFlag)) + + // mark some fields as required\ + _ = prDecorationAzure.MarkFlagRequired(params.ScanIDFlag) + _ = prDecorationAzure.MarkFlagRequired(params.SCMTokenFlag) + _ = prDecorationAzure.MarkFlagRequired(params.NamespaceFlag) + _ = prDecorationAzure.MarkFlagRequired(params.AzureProjectFlag) + _ = prDecorationAzure.MarkFlagRequired(params.PRNumberFlag) + + return prDecorationAzure +} + func runPRDecoration(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { scanID, _ := cmd.Flags().GetString(params.ScanIDFlag) @@ -226,6 +272,13 @@ func updateAPIURLForGitlabOnPrem(apiURL string) string { return gitlabCloudURL } +func getAzureAPIURL(apiURL string) string { + if apiURL != "" { + return apiURL + } + return azureCloudURL +} + func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { scanID, _ := cmd.Flags().GetString(params.ScanIDFlag) @@ -283,6 +336,85 @@ func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers. } } +func runPRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) 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) + projectNameFlag, _ := cmd.Flags().GetString(params.AzureProjectFlag) + prNumberFlag, _ := cmd.Flags().GetInt(params.PRNumberFlag) + apiURL, _ := cmd.Flags().GetString(params.CodeRepositoryFlag) + codeRepositoryUserName, _ := cmd.Flags().GetString(params.CodeRespositoryUsernameFlag) + + errParams := validateAzureOnPremParameters(apiURL, codeRepositoryUserName) + if errParams != nil { + return errParams + } + + scanRunningOrQueued, err := IsScanRunningOrQueued(scansWrapper, scanID) + + if err != nil { + return err + } + + if scanRunningOrQueued { + log.Println(noPRDecorationCreated) + return nil + } + + // Retrieve policies related to the scan and project to include in the PR decoration + policies, policyError := getScanViolatedPolicies(scansWrapper, policyWrapper, scanID, cmd) + if policyError != nil { + return errors.Errorf(policyErrorFormat, failedCreatingAzurePrDecoration) + } + + // Build and post the pr decoration + updatedAPIURL := getAzureAPIURL(apiURL) + updatedScmToken := updateScmTokenForAzure(scmTokenFlag, codeRepositoryUserName) + azureNameSpace := createAzureNameSpace(namespaceFlag, projectNameFlag) + + prModel := &wrappers.AzurePRModel{ + ScanID: scanID, + ScmToken: updatedScmToken, + Namespace: azureNameSpace, + PrNumber: prNumberFlag, + Policies: policies, + APIURL: updatedAPIURL, + } + prResponse, errorModel, err := prWrapper.PostAzurePRDecoration(prModel) + if err != nil { + return err + } + + if errorModel != nil { + return errors.Errorf(errorCodeFormat, failedCreatingAzurePrDecoration, errorModel.Code, errorModel.Message) + } + + logger.Print(prResponse) + + return nil + } +} + +func validateAzureOnPremParameters(apiURL, codeRepositoryUserName string) error { + if apiURL == "" && codeRepositoryUserName != "" { + log.Println(errorAzureOnPremParams) + return errors.New(errorAzureOnPremParams) + } + return nil +} + +func createAzureNameSpace(namespace, projectName string) string { + return fmt.Sprintf("%s/%s", namespace, projectName) +} + +func updateScmTokenForAzure(scmTokenFlag, codeRepositoryUserName string) string { + if codeRepositoryUserName != "" { + return fmt.Sprintf("%s:%s", codeRepositoryUserName, scmTokenFlag) + } + return scmTokenFlag +} + func getScanViolatedPolicies(scansWrapper wrappers.ScansWrapper, policyWrapper wrappers.PolicyWrapper, scanID string, cmd *cobra.Command) ([]wrappers.PrPolicy, error) { // retrieve scan model to get the projectID scanResponseModel, errorScanModel, err := scansWrapper.GetByID(scanID) diff --git a/internal/commands/util/pr_test.go b/internal/commands/util/pr_test.go index 9e11b8385..96add7129 100644 --- a/internal/commands/util/pr_test.go +++ b/internal/commands/util/pr_test.go @@ -8,7 +8,11 @@ import ( "gotest.tools/assert" ) -func TestNewPRDecorationCommandMustExist(t *testing.T) { +const ( + token = "token" +) + +func TestNewGithubPRDecorationCommandMustExist(t *testing.T) { cmd := PRDecorationGithub(nil, nil, nil) assert.Assert(t, cmd != nil, "PR decoration command must exist") @@ -16,7 +20,7 @@ func TestNewPRDecorationCommandMustExist(t *testing.T) { assert.ErrorContains(t, err, "scan-id") } -func TestNewMRDecorationCommandMustExist(t *testing.T) { +func TestNewGitlabMRDecorationCommandMustExist(t *testing.T) { cmd := PRDecorationGitlab(nil, nil, nil) assert.Assert(t, cmd != nil, "MR decoration command must exist") @@ -24,6 +28,14 @@ func TestNewMRDecorationCommandMustExist(t *testing.T) { assert.ErrorContains(t, err, "scan-id") } +func TestNewAzurePRDecorationCommandMustExist(t *testing.T) { + cmd := PRDecorationAzure(nil, nil, nil) + assert.Assert(t, cmd != nil, "PR decoration command must exist") + + err := cmd.Execute() + assert.ErrorContains(t, err, "scan-id") +} + func TestIsScanRunning_WhenScanRunning_ShouldReturnTrue(t *testing.T) { scansMockWrapper := &mock.ScansMockWrapper{Running: true} @@ -66,3 +78,43 @@ func TestUpdateAPIURLForGitlabOnPrem_whenAPIURLIsNotSet_ShouldReturnCloudAPIURL( cloudAPIURL := updateAPIURLForGitlabOnPrem("") asserts.Equal(t, gitlabCloudURL, cloudAPIURL) } + +func TestGetAzureAPIURL_whenAPIURLIsSet_ShouldUpdateAPIURL(t *testing.T) { + selfHostedURL := "https://azure.example.com" + updatedAPIURL := getAzureAPIURL(selfHostedURL) + asserts.Equal(t, selfHostedURL, updatedAPIURL) +} + +func TestGetAzureAPIURL_whenAPIURLIsNotSet_ShouldReturnCloudAPIURL(t *testing.T) { + cloudAPIURL := getAzureAPIURL("") + asserts.Equal(t, azureCloudURL, cloudAPIURL) +} + +func TestUpdateScmTokenForAzureOnPrem_whenUserNameIsSet_ShouldUpdateToken(t *testing.T) { + username := "username" + expectedToken := username + ":" + token + updatedToken := updateScmTokenForAzure(token, username) + asserts.Equal(t, expectedToken, updatedToken) +} + +func TestUpdateScmTokenForAzureOnPrem_whenUserNameNotSet_ShouldNotUpdateToken(t *testing.T) { + username := "" + expectedToken := token + updatedToken := updateScmTokenForAzure(token, username) + asserts.Equal(t, expectedToken, updatedToken) +} + +func TestCreateAzureNameSpace_ShouldCreateNamespace(t *testing.T) { + azureNamespace := createAzureNameSpace("organization", "project") + asserts.Equal(t, "organization/project", azureNamespace) +} + +func TestValidateAzureOnPremParameters_WhenParametersAreValid_ShouldReturnNil(t *testing.T) { + err := validateAzureOnPremParameters("https://azure.example.com", "username") + asserts.Nil(t, err) +} + +func TestValidateAzureOnPremParameters_WhenParametersAreNotValid_ShouldReturnError(t *testing.T) { + err := validateAzureOnPremParameters("", "username") + asserts.NotNil(t, err) +} diff --git a/internal/params/binds.go b/internal/params/binds.go index 4a5b10e0b..71b359d1d 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -28,6 +28,7 @@ var EnvVarsBinds = []struct { {BflPathKey, BflPathEnv, "api/bfl"}, {PRDecorationGithubPathKey, PRDecorationGithubPathEnv, "api/flow-publisher/pr/github"}, {PRDecorationGitlabPathKey, PRDecorationGitlabPathEnv, "api/flow-publisher/pr/gitlab"}, + {PRDecorationAzurePathKey, PRDecorationAzurePathEnv, "api/flow-publisher/pr/azure"}, {DescriptionsPathKey, DescriptionsPathEnv, "api/queries/descriptions"}, {TenantConfigurationPathKey, TenantConfigurationPathEnv, "api/configuration/tenant"}, {UploadsPathKey, UploadsPathEnv, "api/uploads"}, diff --git a/internal/params/envs.go b/internal/params/envs.go index 9ea114d98..cd29f1081 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -30,6 +30,7 @@ const ( BflPathEnv = "CX_BFL_PATH" PRDecorationGithubPathEnv = "CX_PR_DECORATION_GITHUB_PATH" PRDecorationGitlabPathEnv = "CX_PR_DECORATION_GITLAB_PATH" + PRDecorationAzurePathEnv = "CX_PR_DECORATION_AZURE_PATH" SastRmPathEnv = "CX_SAST_RM_PATH" UploadsPathEnv = "CX_UPLOADS_PATH" TokenExpirySecondsEnv = "CX_TOKEN_EXPIRY_SECONDS" diff --git a/internal/params/flags.go b/internal/params/flags.go index b52903aee..8c364f1f7 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -161,16 +161,20 @@ const ( ScaFilterUsage = "SCA filter" // PR decoration flags - 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" + 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" + AzureProjectFlag = "project" + AzureProjectFlagUsage = "Azure project name or project ID" + CodeRespositoryUsernameFlag = "code-repository-username" + CodeRespositoryUsernameFlagUsage = "Azure username for code repository" // Chat (General) ChatAPIKey = "chat-apikey" diff --git a/internal/params/keys.go b/internal/params/keys.go index 1da512e43..ed23c6b9a 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -28,6 +28,7 @@ var ( BflPathKey = strings.ToLower(BflPathEnv) PRDecorationGithubPathKey = strings.ToLower(PRDecorationGithubPathEnv) PRDecorationGitlabPathKey = strings.ToLower(PRDecorationGitlabPathEnv) + PRDecorationAzurePathKey = strings.ToLower(PRDecorationAzurePathEnv) UploadsPathKey = strings.ToLower(UploadsPathEnv) SastRmPathKey = strings.ToLower(SastRmPathEnv) AccessKeyIDConfigKey = strings.ToLower(AccessKeyIDEnv) diff --git a/internal/wrappers/mock/pr-mock.go b/internal/wrappers/mock/pr-mock.go index 4d5a464aa..e608fec9b 100644 --- a/internal/wrappers/mock/pr-mock.go +++ b/internal/wrappers/mock/pr-mock.go @@ -18,3 +18,7 @@ func (pr *PRMockWrapper) PostPRDecoration(model *wrappers.PRModel) ( func (pr *PRMockWrapper) PostGitlabPRDecoration(model *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) { return "MR comment created successfully.", nil, nil } + +func (pr *PRMockWrapper) PostAzurePRDecoration(model *wrappers.AzurePRModel) (string, *wrappers.WebError, error) { + return "PR comment created successfully.", nil, nil +} diff --git a/internal/wrappers/pr-http.go b/internal/wrappers/pr-http.go index 8756d74ac..e114cea51 100644 --- a/internal/wrappers/pr-http.go +++ b/internal/wrappers/pr-http.go @@ -18,12 +18,14 @@ const ( type PRHTTPWrapper struct { githubPath string gitlabPath string + azurePath string } -func NewHTTPPRWrapper(githubPath, gitlabPath string) PRWrapper { +func NewHTTPPRWrapper(githubPath, gitlabPath, azurePath string) PRWrapper { return &PRHTTPWrapper{ githubPath: githubPath, gitlabPath: gitlabPath, + azurePath: azurePath, } } @@ -71,6 +73,28 @@ func (r *PRHTTPWrapper) PostGitlabPRDecoration(model *GitlabPRModel) ( return handlePRResponseWithBody(resp, err) } +func (r *PRHTTPWrapper) PostAzurePRDecoration(model *AzurePRModel) ( + 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.azurePath, bytes.NewBuffer(jsonBytes), true, clientTimeout) + if err != nil { + return "", nil, err + } + defer func() { + if err == nil { + _ = resp.Body.Close() + } + }() + return handlePRResponseWithBody(resp, err) +} + func handlePRResponseWithBody(resp *http.Response, err error) (string, *WebError, error) { if err != nil { return "", nil, err diff --git a/internal/wrappers/pr.go b/internal/wrappers/pr.go index 40ccdb159..376541d33 100644 --- a/internal/wrappers/pr.go +++ b/internal/wrappers/pr.go @@ -25,7 +25,17 @@ type GitlabPRModel struct { APIURL string `json:"apiUrl"` } +type AzurePRModel struct { + ScanID string `json:"scanId"` + ScmToken string `json:"scmToken"` + Namespace string `json:"namespace"` + PrNumber int `json:"prNumber"` + Policies []PrPolicy `json:"violatedPolicyList"` + APIURL string `json:"apiUrl"` +} + type PRWrapper interface { PostPRDecoration(model *PRModel) (string, *WebError, error) PostGitlabPRDecoration(model *GitlabPRModel) (string, *WebError, error) + PostAzurePRDecoration(model *AzurePRModel) (string, *WebError, error) } diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index a2d624d73..55560cb8e 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -27,15 +27,21 @@ const ( prGitlabNamespace = "PR_GITLAB_NAMESPACE" prGitlabProjectId = "PR_GITLAB_PROJECT_ID" prGitlabIid = "PR_GITLAB_IID" + prAzureToken = "AZURE_NEW_TOKEN" + prAzureOrganization = "AZURE_NEW_ORG" + prAzureProject = "AZURE_PROJECT_NAME" + prAzureNumber = "AZURE_PR_NUMBER" prdDecorationForbiddenMessage = "A PR couldn't be created for this scan because it is still in progress." failedGettingScanError = "Failed showing a scan" githubPRCommentCreated = "github PR comment created successfully." gitlabPRCommentCreated = "gitlab PR comment created successfully." + azurePRCommentCreated = "azure PR comment created successfully." outputFileName = "test_output.log" scans = "api/scans" ) var completedScanId = "" +var runningScanId = "" func getCompletedScanID(t *testing.T) string { if completedScanId != "" { @@ -58,6 +64,19 @@ func getCompletedScanID(t *testing.T) string { return scanID } +func getRunningScanId(t *testing.T) string { + scanWrapper := wrappers.NewHTTPScansWrapper(scans) + if runningScanId != "" { + isRunning, _ := util.IsScanRunningOrQueued(scanWrapper, runningScanId) + if isRunning { + return runningScanId + } + } + scanID, _ := createScanNoWait(t, Zip, Tags, getProjectNameForScanTests()) + runningScanId = scanID + return scanID +} + func TestPRGithubDecorationSuccessCase(t *testing.T) { args := []string{ "utils", @@ -226,14 +245,93 @@ func TestPRGitlabDecorationFailure(t *testing.T) { assert.ErrorContains(t, err, "scan not found") } +func TestPRAzureDecorationSuccessCase(t *testing.T) { + args := []string{ + "utils", + "pr", + "azure", + flag(params.ScanIDFlag), + getCompletedScanID(t), + flag(params.SCMTokenFlag), + os.Getenv(prAzureToken), + flag(params.NamespaceFlag), + os.Getenv(prAzureOrganization), + flag(params.AzureProjectFlag), + os.Getenv(prAzureProject), + flag(params.PRNumberFlag), + os.Getenv(prAzureNumber), + } + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Error should be nil") +} + +func TestPRAzureDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T) { + args := []string{ + "utils", + "pr", + "azure", + flag(params.ScanIDFlag), + getCompletedScanID(t), + flag(params.SCMTokenFlag), + os.Getenv(prAzureToken), + flag(params.NamespaceFlag), + os.Getenv(prAzureOrganization), + flag(params.AzureProjectFlag), + os.Getenv(prAzureProject), + flag(params.PRNumberFlag), + os.Getenv(prAzureNumber), + flag(params.CodeRepositoryFlag), + "https://azure.example.com", + } + + monkey.Patch((*wrappers.PRHTTPWrapper).PostAzurePRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.AzurePRModel) (string, *wrappers.WebError, error) { + return azurePRCommentCreated, nil, nil + }) + defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostAzurePRDecoration) + + file := createOutputFile(t, outputFileName) + defer deleteOutputFile(t, file) + defer logger.SetOutput(os.Stdout) + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Error should be nil") + + stdoutString, err := util.ReadFileAsString(file.Name()) + if err != nil { + t.Fatalf("Failed to read log file: %v", err) + } + + assert.Equal(t, strings.Contains(stdoutString, azurePRCommentCreated), true, "Expected output: %s", azurePRCommentCreated) +} + +func TestPRAzureDecorationFailure(t *testing.T) { + + args := []string{ + "utils", + "pr", + "azure", + flag(params.ScanIDFlag), + "fakeScanID", + flag(params.SCMTokenFlag), + os.Getenv(prAzureToken), + flag(params.NamespaceFlag), + os.Getenv(prAzureOrganization), + flag(params.AzureProjectFlag), + os.Getenv(prAzureProject), + flag(params.PRNumberFlag), + os.Getenv(prAzureNumber), + } + err, _ := executeCommand(t, args...) + assert.ErrorContains(t, err, "scan not found") +} + func TestPRGithubDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t *testing.T) { - scanID, _ := createScanNoWait(t, Zip, Tags, getProjectNameForScanTests()) args := []string{ "utils", "pr", "github", flag(params.ScanIDFlag), - scanID, + getRunningScanId(t), flag(params.SCMTokenFlag), os.Getenv(prGithubToken), flag(params.NamespaceFlag), @@ -242,29 +340,18 @@ func TestPRGithubDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t * os.Getenv(prGithubNumber), flag(params.RepoNameFlag), os.Getenv(prGithubRepoName), - "--debug", - } - - file := createOutputFile(t, outputFileName) - _, _ = executeCommand(t, args...) - stdoutString, err := util.ReadFileAsString(file.Name()) - if err != nil { - t.Fatalf("Failed to read log file: %v", err) } - assert.Equal(t, strings.Contains(stdoutString, prdDecorationForbiddenMessage), true, "Expected output: %s", prdDecorationForbiddenMessage) - defer deleteOutputFile(t, file) - defer logger.SetOutput(os.Stdout) + runPRTestForRunningScan(t, args) } func TestPRGitlabDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t *testing.T) { - scanID, _ := createScanNoWait(t, Zip, Tags, getProjectNameForScanTests()) args := []string{ "utils", "pr", "gitlab", flag(params.ScanIDFlag), - scanID, + getRunningScanId(t), flag(params.SCMTokenFlag), os.Getenv(prGitlabToken), flag(params.NamespaceFlag), @@ -277,16 +364,27 @@ func TestPRGitlabDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t * os.Getenv(prGitlabIid), } - file := createOutputFile(t, outputFileName) - _, _ = executeCommand(t, args...) - stdoutString, err := util.ReadFileAsString(file.Name()) - if err != nil { - t.Fatalf("Failed to read log file: %v", err) + runPRTestForRunningScan(t, args) +} + +func TestPRAzureDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t *testing.T) { + args := []string{ + "utils", + "pr", + "azure", + flag(params.ScanIDFlag), + getRunningScanId(t), + flag(params.SCMTokenFlag), + os.Getenv(prAzureToken), + flag(params.NamespaceFlag), + os.Getenv(prAzureOrganization), + flag(params.AzureProjectFlag), + os.Getenv(prAzureProject), + flag(params.PRNumberFlag), + os.Getenv(prAzureNumber), } - assert.Equal(t, strings.Contains(stdoutString, prdDecorationForbiddenMessage), true, "Expected output: %s", prdDecorationForbiddenMessage) - defer deleteOutputFile(t, file) - defer logger.SetOutput(os.Stdout) + runPRTestForRunningScan(t, args) } func createOutputFile(t *testing.T, fileName string) *os.File { @@ -305,3 +403,16 @@ func deleteOutputFile(t *testing.T, file *os.File) { logger.Printf("Failed to remove log file: %v", err) } } + +func runPRTestForRunningScan(t *testing.T, args []string) { + file := createOutputFile(t, outputFileName) + _, _ = executeCommand(t, args...) + stdoutString, err := util.ReadFileAsString(file.Name()) + if err != nil { + t.Fatalf("Failed to read log file: %v", err) + } + assert.Equal(t, strings.Contains(stdoutString, prdDecorationForbiddenMessage), true, "Expected output: %s", prdDecorationForbiddenMessage) + + defer deleteOutputFile(t, file) + defer logger.SetOutput(os.Stdout) +} diff --git a/test/integration/util_command.go b/test/integration/util_command.go index 8c91f45ec..3655a0ee1 100644 --- a/test/integration/util_command.go +++ b/test/integration/util_command.go @@ -74,6 +74,7 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command { learnMore := viper.GetString(params.DescriptionsPathKey) prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey) prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey) + prDecorationAzurePath := viper.GetString(params.PRDecorationAzurePathKey) tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey) resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey) exportPath := viper.GetString(params.ExportPathKey) @@ -104,7 +105,7 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command { bitBucketWrapper := wrappers.NewBitbucketWrapper() bflWrapper := wrappers.NewBflHTTPWrapper(bfl) learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(learnMore) - prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath) + prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, prDecorationAzurePath) tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath) jwtWrapper := wrappers.NewJwtWrapper() scaRealtimeWrapper := wrappers.NewHTTPScaRealTimeWrapper()