diff --git a/github/github-accessors.go b/github/github-accessors.go index 02c538b750c..957e2784249 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -2222,6 +2222,14 @@ func (b *BypassActor) GetBypassMode() *BypassMode { return b.BypassMode } +// GetSecurityConfigurationID returns the SecurityConfigurationID field if it's non-nil, zero value otherwise. +func (b *BypassReviewer) GetSecurityConfigurationID() int64 { + if b == nil || b.SecurityConfigurationID == nil { + return 0 + } + return *b.SecurityConfigurationID +} + // GetApp returns the App field. func (c *CheckRun) GetApp() *App { if c == nil { @@ -3438,6 +3446,22 @@ func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedAlertDismissal() s return *c.SecretScanningDelegatedAlertDismissal } +// GetSecretScanningDelegatedBypass returns the SecretScanningDelegatedBypass field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedBypass() string { + if c == nil || c.SecretScanningDelegatedBypass == nil { + return "" + } + return *c.SecretScanningDelegatedBypass +} + +// GetSecretScanningDelegatedBypassOptions returns the SecretScanningDelegatedBypassOptions field. +func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedBypassOptions() *SecretScanningDelegatedBypassOptions { + if c == nil { + return nil + } + return c.SecretScanningDelegatedBypassOptions +} + // GetSecretScanningGenericSecrets returns the SecretScanningGenericSecrets field if it's non-nil, zero value otherwise. func (c *CodeSecurityConfiguration) GetSecretScanningGenericSecrets() string { if c == nil || c.SecretScanningGenericSecrets == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 68e429f6247..8743dd4548c 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -2907,6 +2907,17 @@ func TestBypassActor_GetBypassMode(tt *testing.T) { b.GetBypassMode() } +func TestBypassReviewer_GetSecurityConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + b := &BypassReviewer{SecurityConfigurationID: &zeroValue} + b.GetSecurityConfigurationID() + b = &BypassReviewer{} + b.GetSecurityConfigurationID() + b = nil + b.GetSecurityConfigurationID() +} + func TestCheckRun_GetApp(tt *testing.T) { tt.Parallel() c := &CheckRun{} @@ -4483,6 +4494,25 @@ func TestCodeSecurityConfiguration_GetSecretScanningDelegatedAlertDismissal(tt * c.GetSecretScanningDelegatedAlertDismissal() } +func TestCodeSecurityConfiguration_GetSecretScanningDelegatedBypass(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningDelegatedBypass: &zeroValue} + c.GetSecretScanningDelegatedBypass() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningDelegatedBypass() + c = nil + c.GetSecretScanningDelegatedBypass() +} + +func TestCodeSecurityConfiguration_GetSecretScanningDelegatedBypassOptions(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetSecretScanningDelegatedBypassOptions() + c = nil + c.GetSecretScanningDelegatedBypassOptions() +} + func TestCodeSecurityConfiguration_GetSecretScanningGenericSecrets(tt *testing.T) { tt.Parallel() var zeroValue string diff --git a/github/orgs_codesecurity_configurations.go b/github/orgs_codesecurity_configurations.go index 788f52ed1b9..d1ed6e25793 100644 --- a/github/orgs_codesecurity_configurations.go +++ b/github/orgs_codesecurity_configurations.go @@ -33,6 +33,19 @@ type RepositoryAttachment struct { Repository *Repository `json:"repository"` } +// SecretScanningDelegatedBypassOptions represents the feature options for the secret scanning delegated bypass. +type SecretScanningDelegatedBypassOptions struct { + Reviewers []*BypassReviewer `json:"reviewers,omitzero"` +} + +// BypassReviewer represents the bypass reviewers for the delegated bypass of a code security configuration. +// SecurityConfigurationID is added by GitHub in responses. +type BypassReviewer struct { + ReviewerID int64 `json:"reviewer_id"` + ReviewerType string `json:"reviewer_type"` + SecurityConfigurationID *int64 `json:"security_configuration_id,omitempty"` +} + // CodeSecurityConfiguration represents a code security configuration. type CodeSecurityConfiguration struct { ID *int64 `json:"id,omitempty"` @@ -52,6 +65,8 @@ type CodeSecurityConfiguration struct { CodeSecurity *string `json:"code_security,omitempty"` SecretScanning *string `json:"secret_scanning,omitempty"` SecretScanningPushProtection *string `json:"secret_scanning_push_protection,omitempty"` + SecretScanningDelegatedBypass *string `json:"secret_scanning_delegated_bypass,omitempty"` + SecretScanningDelegatedBypassOptions *SecretScanningDelegatedBypassOptions `json:"secret_scanning_delegated_bypass_options,omitempty"` SecretScanningValidityChecks *string `json:"secret_scanning_validity_checks,omitempty"` SecretScanningNonProviderPatterns *string `json:"secret_scanning_non_provider_patterns,omitempty"` SecretScanningGenericSecrets *string `json:"secret_scanning_generic_secrets,omitempty"` diff --git a/github/orgs_codesecurity_configurations_test.go b/github/orgs_codesecurity_configurations_test.go index 7a9c2a969b2..23a3972108f 100644 --- a/github/orgs_codesecurity_configurations_test.go +++ b/github/orgs_codesecurity_configurations_test.go @@ -156,6 +156,112 @@ func TestOrganizationsService_CreateCodeSecurityConfiguration(t *testing.T) { }) } +func TestOrganizationsService_CreateCodeSecurityConfigurationWithDelegatedBypass(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + SecretProtection: Ptr("enabled"), // required to configure bypass + SecretScanning: Ptr("enabled"), // required to configure bypass + SecretScanningPushProtection: Ptr("enabled"), // required to configure bypass + SecretScanningDelegatedBypass: Ptr("enabled"), + SecretScanningDelegatedBypassOptions: &SecretScanningDelegatedBypassOptions{ + Reviewers: []*BypassReviewer{ + { + ReviewerType: "TEAM", + ReviewerID: 456, + }, + { + ReviewerType: "ROLE", + ReviewerID: 789, + }, + }, + }, + } + + mux.HandleFunc("/orgs/o/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + var v CodeSecurityConfiguration + assertNilError(t, json.NewDecoder(r.Body).Decode(&v)) + + if !cmp.Equal(v, input) { + t.Errorf("Organizations.CreateCodeSecurityConfiguration with Bypass request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{ + "id":123, + "name":"config1", + "description":"desc1", + "secret_protection": "enabled", + "secret_scanning": "enabled", + "secret_scanning_push_protection": "enabled", + "secret_scanning_delegated_bypass": "enabled", + "secret_scanning_delegated_bypass_options": { + "reviewers": [ + { + "security_configuration_id": 123, + "reviewer_type": "TEAM", + "reviewer_id": 456 + }, + { + "security_configuration_id": 123, + "reviewer_type": "ROLE", + "reviewer_id": 789 + } + ] + } + }`) + }) + + configuration, _, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if err != nil { + t.Errorf("Organizations.CreateCodeSecurityConfiguration with Bypass returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ + ID: Ptr(int64(123)), + Name: "config1", + Description: "desc1", + SecretProtection: Ptr("enabled"), + SecretScanning: Ptr("enabled"), + SecretScanningPushProtection: Ptr("enabled"), + SecretScanningDelegatedBypass: Ptr("enabled"), + SecretScanningDelegatedBypassOptions: &SecretScanningDelegatedBypassOptions{ + Reviewers: []*BypassReviewer{ + { + SecurityConfigurationID: Ptr(int64(123)), + ReviewerType: "TEAM", + ReviewerID: 456, + }, + { + SecurityConfigurationID: Ptr(int64(123)), + ReviewerType: "ROLE", + ReviewerID: 789, + }, + }, + }, + } + if !cmp.Equal(configuration, want) { + t.Errorf("Organizations.CreateCodeSecurityConfiguration with Bypass returned %+v, want %+v", configuration, want) + } + + const methodName = "CreateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCodeSecurityConfiguration(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestOrganizationsService_ListDefaultCodeSecurityConfigurations(t *testing.T) { t.Parallel() client, mux, _ := setup(t)