Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/CLI Add Pr-Decoration Support on ADO(AST-70135) #915

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down
82 changes: 81 additions & 1 deletion internal/commands/util/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@
`,
),
}

prDecorationAzure := PRDecorationAzure(prWrapper, policyWrapper, scansWrapper)
prDecorationGithub := PRDecorationGithub(prWrapper, policyWrapper, scansWrapper)
prDecorationGitlab := PRDecorationGitlab(prWrapper, policyWrapper, scansWrapper)

cmd.AddCommand(prDecorationAzure)
cmd.AddCommand(prDecorationGithub)
cmd.AddCommand(prDecorationGitlab)
return cmd
Expand Down Expand Up @@ -73,6 +74,38 @@
return false, nil
}

func PRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command {
prDecorationAzure := &cobra.Command{
Use: "azure",
Short: "Decorate Azure DevOps PR with vulnerabilities",
Long: "Decorate Azure DevOps PR with vulnerabilities",
Example: heredoc.Doc(
`
$ cx utils pr azure --scan-id <scan-id> --token <AAD> --namespace <organization> --project <project-name>
--pr-number <pr number> --code-repository-url <repository-url>
`,
),
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 DevOps"))
prDecorationAzure.Flags().String(params.ProjectName, "", "Azure DevOps project name")
prDecorationAzure.Flags().Int(params.PRNumberFlag, 0, params.PRNumberFlagUsage)
prDecorationAzure.Flags().String(params.ServerURLFlag, "", params.ServerURLFlagUsage)

_ = viper.BindPFlag(params.SCMTokenFlag, prDecorationAzure.Flags().Lookup(params.SCMTokenFlag))

_ = prDecorationAzure.MarkFlagRequired(params.ScanIDFlag)
_ = prDecorationAzure.MarkFlagRequired(params.SCMTokenFlag)
_ = prDecorationAzure.MarkFlagRequired(params.NamespaceFlag)
_ = prDecorationAzure.MarkFlagRequired(params.ProjectName)
_ = prDecorationAzure.MarkFlagRequired(params.PRNumberFlag)

return prDecorationAzure
}

func PRDecorationGithub(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command {
prDecorationGithub := &cobra.Command{
Use: "github",
Expand Down Expand Up @@ -153,6 +186,53 @@
return prDecorationGitlab
}

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 {

Check failure on line 190 in internal/commands/util/pr.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary leading newline (whitespace)

scanID, _ := cmd.Flags().GetString(params.ScanIDFlag)
scmTokenFlag, _ := cmd.Flags().GetString(params.SCMTokenFlag)
organizationFlag, _ := cmd.Flags().GetString(params.NamespaceFlag)
projectFlag, _ := cmd.Flags().GetString(params.ProjectName)
prNumberFlag, _ := cmd.Flags().GetInt(params.PRNumberFlag)
serverURLFlag, _ := cmd.Flags().GetString(params.ServerURLFlag)

scanRunningOrQueued, err := isScanRunningOrQueued(scansWrapper, scanID)
if err != nil {
return err
}
if scanRunningOrQueued {
log.Println(noPRDecorationCreated)
return nil
}

policies, policyError := getScanViolatedPolicies(scansWrapper, policyWrapper, scanID, cmd)
if policyError != nil {
return errors.Errorf(policyErrorFormat, failedCreatingGithubPrDecoration)
}

prModel := &wrappers.AzurePRModel{
ScanID: scanID,
ScmToken: scmTokenFlag,
Organization: organizationFlag,
ProjectName: projectFlag,
PrNumber: prNumberFlag,
Policies: policies,
ServerUrl: serverURLFlag,
}

prResponse, errorModel, err := prWrapper.PostAzurePRDecoration(prModel)
if err != nil {
return err
}
if errorModel != nil {
return errors.Errorf(errorCodeFormat, failedCreatingGithubPrDecoration, errorModel.Code, errorModel.Message)
}

logger.Print(prResponse)
return nil
}
}

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)
Expand Down
1 change: 1 addition & 0 deletions internal/params/envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ const (
PRIidFlagUsage = "Gitlab IID (internal ID) of the merge request"
PRGitlabProjectFlag = "gitlab-project-id"
PRGitlabProjectFlagUsage = "Gitlab project ID"
ServerURLFlag = "code-repository-url"
ServerURLFlagUsage = "Code repository URL (optional for self-hosted Azure DevOps) "

// Chat (General)
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 @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions internal/wrappers/mock/pr-mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ 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 "Azure PR comment created successfully.", nil, nil
}
26 changes: 25 additions & 1 deletion internal/wrappers/pr-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,39 @@
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,
}
}

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)

Check failure on line 52 in internal/wrappers/pr-http.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)
}
func (r *PRHTTPWrapper) PostPRDecoration(model *PRModel) (
string,
*WebError,
Expand Down
11 changes: 11 additions & 0 deletions internal/wrappers/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@
Policies []PrPolicy `json:"violatedPolicyList"`
}

type AzurePRModel struct {
ScanID string `json:"scanId"`
ScmToken string `json:"scmToken"`
Organization string `json:"organization"`
ProjectName string `json:"projectName"`
PrNumber int `json:"prNumber"`
Policies []PrPolicy `json:"violatedPolicyList"`
ServerUrl string `json:"serverUrl"`

Check failure on line 33 in internal/wrappers/pr.go

View workflow job for this annotation

GitHub Actions / lint

struct field `ServerUrl` should be `ServerURL` (golint)
}

type PRWrapper interface {
PostPRDecoration(model *PRModel) (string, *WebError, error)
PostGitlabPRDecoration(model *GitlabPRModel) (string, *WebError, error)
PostAzurePRDecoration(model *AzurePRModel) (string, *WebError, error)
}
5 changes: 3 additions & 2 deletions test/integration/util_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -209,7 +210,7 @@ func executeCmdWithTimeOutNilAssertion(
func executeWithTimeout(cmd *cobra.Command, timeout time.Duration, args ...string) error {

args = append(args, flag(params.RetryFlag), "3", flag(params.RetryDelayFlag), "5")
args = appendProxyArgs(args)
//args = appendProxyArgs(args)
cmd.SetArgs(args)

ctx, cancel := context.WithTimeout(context.Background(), timeout)
Expand Down
Loading