From d68f7d767a47e519fb87dd779dbc976f4fa762be Mon Sep 17 00:00:00 2001 From: tanayarun Date: Sun, 19 Jul 2026 16:28:13 +0530 Subject: [PATCH 1/4] Add repo-level secret scanning custom pattern support --- github/github-accessors.go | 248 ++++++++++++++ github/github-accessors_test.go | 317 ++++++++++++++++++ github/secret_scanning_custom_patterns.go | 185 ++++++++++ .../secret_scanning_custom_patterns_test.go | 236 +++++++++++++ 4 files changed, 986 insertions(+) create mode 100644 github/secret_scanning_custom_patterns.go create mode 100644 github/secret_scanning_custom_patterns_test.go diff --git a/github/github-accessors.go b/github/github-accessors.go index 138f841b5f2..775946cfbee 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -39454,6 +39454,190 @@ func (s *SecretScanningAlertUpdateOptions) GetState() string { return s.State } +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { + return "" + } + return *s.CustomPatternVersion +} + +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" + } + return *s.EndDelimiter +} + +// GetID returns the ID field. +func (s *SecretScanningCustomPattern) GetID() int64 { + if s == nil { + return 0 + } + return s.ID +} + +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPattern) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { + return nil + } + return s.MustMatch +} + +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPattern) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil + } + return s.MustNotMatch +} + +// GetName returns the Name field. +func (s *SecretScanningCustomPattern) GetName() string { + if s == nil { + return "" + } + return s.Name +} + +// GetPattern returns the Pattern field. +func (s *SecretScanningCustomPattern) GetPattern() string { + if s == nil { + return "" + } + return s.Pattern +} + +// GetPushProtectionEnabled returns the PushProtectionEnabled field. +func (s *SecretScanningCustomPattern) GetPushProtectionEnabled() bool { + if s == nil { + return false + } + return s.PushProtectionEnabled +} + +// GetSlug returns the Slug field. +func (s *SecretScanningCustomPattern) GetSlug() string { + if s == nil { + return "" + } + return s.Slug +} + +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { + return "" + } + return *s.StartDelimiter +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetState() string { + if s == nil || s.State == nil { + return "" + } + return *s.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} + } + return *s.UpdatedAt +} + +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternRequest) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" + } + return *s.EndDelimiter +} + +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternRequest) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { + return nil + } + return s.MustMatch +} + +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternRequest) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil + } + return s.MustNotMatch +} + +// GetName returns the Name field. +func (s *SecretScanningCustomPatternRequest) GetName() string { + if s == nil { + return "" + } + return s.Name +} + +// GetPattern returns the Pattern field. +func (s *SecretScanningCustomPatternRequest) GetPattern() string { + if s == nil { + return "" + } + return s.Pattern +} + +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternRequest) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { + return "" + } + return *s.StartDelimiter +} + +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternsCreateRequest) GetPatterns() []*SecretScanningCustomPatternRequest { + if s == nil || s.Patterns == nil { + return nil + } + return s.Patterns +} + +// GetCreatedPatterns returns the CreatedPatterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternsCreateResponse) GetCreatedPatterns() []*SecretScanningCustomPattern { + if s == nil || s.CreatedPatterns == nil { + return nil + } + return s.CreatedPatterns +} + +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternsDeleteRequest) GetPatterns() []*SecretScanningCustomPatternToDelete { + if s == nil || s.Patterns == nil { + return nil + } + return s.Patterns +} + +// GetPostDeleteAction returns the PostDeleteAction field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternsDeleteRequest) GetPostDeleteAction() string { + if s == nil || s.PostDeleteAction == nil { + return "" + } + return *s.PostDeleteAction +} + // GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. func (s *SecretScanningCustomPatternSetting) GetCustomPatternVersion() string { if s == nil || s.CustomPatternVersion == nil { @@ -39478,6 +39662,70 @@ func (s *SecretScanningCustomPatternSetting) GetTokenType() string { return s.TokenType } +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternToDelete) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { + return "" + } + return *s.CustomPatternVersion +} + +// GetPatternID returns the PatternID field. +func (s *SecretScanningCustomPatternToDelete) GetPatternID() int64 { + if s == nil { + return 0 + } + return s.PatternID +} + +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { + return "" + } + return *s.CustomPatternVersion +} + +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" + } + return *s.EndDelimiter +} + +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { + return nil + } + return s.MustMatch +} + +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil + } + return s.MustNotMatch +} + +// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetPattern() string { + if s == nil || s.Pattern == nil { + return "" + } + return *s.Pattern +} + +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternUpdateRequest) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { + return "" + } + return *s.StartDelimiter +} + // GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. func (s *SecretScanningDelegatedBypassOptions) GetReviewers() []*BypassReviewer { if s == nil || s.Reviewers == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 186fc7254db..6e56f06c485 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -49394,6 +49394,238 @@ func TestSecretScanningAlertUpdateOptions_GetState(tt *testing.T) { s.GetState() } +func TestSecretScanningCustomPattern_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningCustomPattern{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SecretScanningCustomPattern{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecretScanningCustomPattern_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPattern{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPattern_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningCustomPattern{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningCustomPattern_GetID(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetID() + s = nil + s.GetID() +} + +func TestSecretScanningCustomPattern_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPattern{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningCustomPattern{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningCustomPattern_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPattern{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningCustomPattern{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningCustomPattern_GetName(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetName() + s = nil + s.GetName() +} + +func TestSecretScanningCustomPattern_GetPattern(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningCustomPattern_GetPushProtectionEnabled(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetPushProtectionEnabled() + s = nil + s.GetPushProtectionEnabled() +} + +func TestSecretScanningCustomPattern_GetSlug(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetSlug() + s = nil + s.GetSlug() +} + +func TestSecretScanningCustomPattern_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningCustomPattern{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + +func TestSecretScanningCustomPattern_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{State: &zeroValue} + s.GetState() + s = &SecretScanningCustomPattern{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningCustomPattern_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningCustomPattern{UpdatedAt: &zeroValue} + s.GetUpdatedAt() + s = &SecretScanningCustomPattern{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestSecretScanningCustomPatternRequest_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternRequest{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningCustomPatternRequest{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningCustomPatternRequest_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternRequest{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningCustomPatternRequest{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningCustomPatternRequest_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternRequest{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningCustomPatternRequest{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningCustomPatternRequest_GetName(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternRequest{} + s.GetName() + s = nil + s.GetName() +} + +func TestSecretScanningCustomPatternRequest_GetPattern(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternRequest{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningCustomPatternRequest_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternRequest{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningCustomPatternRequest{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + +func TestSecretScanningCustomPatternsCreateRequest_GetPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternRequest{} + s := &SecretScanningCustomPatternsCreateRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningCustomPatternsCreateRequest{} + s.GetPatterns() + s = nil + s.GetPatterns() +} + +func TestSecretScanningCustomPatternsCreateResponse_GetCreatedPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPattern{} + s := &SecretScanningCustomPatternsCreateResponse{CreatedPatterns: zeroValue} + s.GetCreatedPatterns() + s = &SecretScanningCustomPatternsCreateResponse{} + s.GetCreatedPatterns() + s = nil + s.GetCreatedPatterns() +} + +func TestSecretScanningCustomPatternsDeleteRequest_GetPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternToDelete{} + s := &SecretScanningCustomPatternsDeleteRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningCustomPatternsDeleteRequest{} + s.GetPatterns() + s = nil + s.GetPatterns() +} + +func TestSecretScanningCustomPatternsDeleteRequest_GetPostDeleteAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternsDeleteRequest{PostDeleteAction: &zeroValue} + s.GetPostDeleteAction() + s = &SecretScanningCustomPatternsDeleteRequest{} + s.GetPostDeleteAction() + s = nil + s.GetPostDeleteAction() +} + func TestSecretScanningCustomPatternSetting_GetCustomPatternVersion(tt *testing.T) { tt.Parallel() var zeroValue string @@ -49421,6 +49653,91 @@ func TestSecretScanningCustomPatternSetting_GetTokenType(tt *testing.T) { s.GetTokenType() } +func TestSecretScanningCustomPatternToDelete_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternToDelete{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPatternToDelete{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPatternToDelete_GetPatternID(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternToDelete{} + s.GetPatternID() + s = nil + s.GetPatternID() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternUpdateRequest{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternUpdateRequest{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternUpdateRequest{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternUpdateRequest{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetPattern(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternUpdateRequest{Pattern: &zeroValue} + s.GetPattern() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningCustomPatternUpdateRequest_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternUpdateRequest{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningCustomPatternUpdateRequest{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + func TestSecretScanningDelegatedBypassOptions_GetReviewers(tt *testing.T) { tt.Parallel() zeroValue := []*BypassReviewer{} diff --git a/github/secret_scanning_custom_patterns.go b/github/secret_scanning_custom_patterns.go new file mode 100644 index 00000000000..83e87e6e840 --- /dev/null +++ b/github/secret_scanning_custom_patterns.go @@ -0,0 +1,185 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SecretScanningCustomPattern represents a custom pattern for secret scanning, +// as returned by the GitHub API. +// +// GitHub API docs: https://docs.github.com/en/rest/secret-scanning/secret-scanning +type SecretScanningCustomPattern struct { + ID int64 `json:"id"` + Name string `json:"name"` + Pattern string `json:"pattern"` + Slug string `json:"slug"` + + // State is the publish state of the pattern. Possible values are: + // "published" or "unpublished". + State *string `json:"state,omitempty"` + + PushProtectionEnabled bool `json:"push_protection_enabled"` + StartDelimiter *string `json:"start_delimiter,omitempty"` + EndDelimiter *string `json:"end_delimiter,omitempty"` + MustMatch []string `json:"must_match,omitempty"` + MustNotMatch []string `json:"must_not_match,omitempty"` + + // CustomPatternVersion is used to confirm you're updating the current + // version of the pattern and to avoid unintentionally overriding + // someone else's update. + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// SecretScanningCustomPatternRequest represents a custom pattern to be +// created for secret scanning. +type SecretScanningCustomPatternRequest struct { + Name string `json:"name"` + Pattern string `json:"pattern"` + + StartDelimiter *string `json:"start_delimiter,omitempty"` + EndDelimiter *string `json:"end_delimiter,omitempty"` + MustMatch []string `json:"must_match,omitempty"` + MustNotMatch []string `json:"must_not_match,omitempty"` +} + +// SecretScanningCustomPatternsCreateRequest represents the bulk request body +// used to create one or more custom patterns. +type SecretScanningCustomPatternsCreateRequest struct { + Patterns []*SecretScanningCustomPatternRequest `json:"patterns"` +} + +// SecretScanningCustomPatternsCreateResponse represents the bulk response +// returned after creating one or more custom patterns. +type SecretScanningCustomPatternsCreateResponse struct { + CreatedPatterns []*SecretScanningCustomPattern `json:"created_patterns"` +} + +// SecretScanningCustomPatternToDelete identifies a single custom pattern to +// remove in a bulk delete operation. +type SecretScanningCustomPatternToDelete struct { + PatternID int64 `json:"pattern_id"` + + // CustomPatternVersion is used to confirm you're deleting the current + // version of the pattern. + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` +} + +// SecretScanningCustomPatternsDeleteRequest represents the bulk request body +// used to delete one or more custom patterns. +type SecretScanningCustomPatternsDeleteRequest struct { + Patterns []*SecretScanningCustomPatternToDelete `json:"patterns"` + + // PostDeleteAction controls what happens to alerts associated with the + // deleted patterns. Possible values are: "delete_alerts" (default) or + // "resolve_alerts". + PostDeleteAction *string `json:"post_delete_action,omitempty"` +} + +// SecretScanningCustomPatternUpdateRequest represents the fields that can be +// updated on an existing custom pattern. At least one of Pattern, +// StartDelimiter, EndDelimiter, MustMatch, or MustNotMatch must be set. +type SecretScanningCustomPatternUpdateRequest struct { + Pattern *string `json:"pattern,omitempty"` + StartDelimiter *string `json:"start_delimiter,omitempty"` + EndDelimiter *string `json:"end_delimiter,omitempty"` + MustMatch []string `json:"must_match,omitempty"` + MustNotMatch []string `json:"must_not_match,omitempty"` + + // CustomPatternVersion is required and is used to confirm you're + // updating the current version of the pattern. + CustomPatternVersion *string `json:"custom_pattern_version"` +} + +// ListCustomPatternsForRepo lists the secret scanning custom patterns +// defined at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#list-repository-custom-patterns +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) ListCustomPatternsForRepo(ctx context.Context, owner, repo string) ([]*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var patterns []*SecretScanningCustomPattern + resp, err := s.client.Do(req, &patterns) + if err != nil { + return nil, resp, err + } + + return patterns, resp, nil +} + +// CreateCustomPatternsForRepo creates one or more secret scanning custom +// patterns at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-create-repository-custom-patterns +// +//meta:operation POST /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) CreateCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningCustomPatternsCreateRequest) (*SecretScanningCustomPatternsCreateResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *SecretScanningCustomPatternsCreateResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// UpdateCustomPatternForRepo updates a single secret scanning custom pattern +// at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#update-a-repository-custom-pattern +// +//meta:operation PATCH /repos/{owner}/{repo}/secret-scanning/custom-patterns/{pattern_id} +func (s *SecretScanningService) UpdateCustomPatternForRepo(ctx context.Context, owner, repo string, patternID int64, body SecretScanningCustomPatternUpdateRequest) (*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns/%v", owner, repo, patternID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var pattern *SecretScanningCustomPattern + resp, err := s.client.Do(req, &pattern) + if err != nil { + return nil, resp, err + } + + return pattern, resp, nil +} + +// DeleteCustomPatternsForRepo deletes one or more secret scanning custom +// patterns at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-delete-repository-custom-patterns +// +//meta:operation DELETE /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) DeleteCustomPatternsForRepo(ctx context.Context, owner, repo string, patterns *SecretScanningCustomPatternsDeleteRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + + req, err := s.client.NewRequest(ctx, "DELETE", u, patterns) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/secret_scanning_custom_patterns_test.go b/github/secret_scanning_custom_patterns_test.go new file mode 100644 index 00000000000..a54fd4c8301 --- /dev/null +++ b/github/secret_scanning_custom_patterns_test.go @@ -0,0 +1,236 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": true, + "start_delimiter": "\\b", + "end_delimiter": "\\b", + "must_match": ["ID-.*"], + "must_not_match": ["TEST-.*"], + "custom_pattern_version": "v1", + "created_at": "2026-07-01T00:00:00Z", + "updated_at": "2026-07-02T00:00:00Z" + } + ]`) + }) + + ctx := t.Context() + patterns, _, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r") + if err != nil { + t.Errorf("SecretScanning.ListCustomPatternsForRepo returned error: %v", err) + } + + createdAt, _ := time.Parse(time.RFC3339, "2026-07-01T00:00:00Z") + updatedAt, _ := time.Parse(time.RFC3339, "2026-07-02T00:00:00Z") + + want := []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: Ptr("published"), + PushProtectionEnabled: true, + StartDelimiter: Ptr(`\b`), + EndDelimiter: Ptr(`\b`), + MustMatch: []string{"ID-.*"}, + MustNotMatch: []string{"TEST-.*"}, + CustomPatternVersion: Ptr("v1"), + CreatedAt: &Timestamp{createdAt}, + UpdatedAt: &Timestamp{updatedAt}, + }, + } + if !cmp.Equal(patterns, want) { + t.Errorf("SecretScanning.ListCustomPatternsForRepo returned %+v, want %+v", patterns, want) + } + + const methodName = "ListCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListCustomPatternsForRepo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r") + return resp, err + }) +} + +func TestSecretScanningService_CreateCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SecretScanningCustomPatternsCreateRequest{ + Patterns: []*SecretScanningCustomPatternRequest{ + { + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + }, + }, + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "created_patterns": [ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false + } + ] + }`) + }) + + ctx := t.Context() + result, _, err := client.SecretScanning.CreateCustomPatternsForRepo(ctx, "o", "r", input) + if err != nil { + t.Errorf("SecretScanning.CreateCustomPatternsForRepo returned error: %v", err) + } + + want := &SecretScanningCustomPatternsCreateResponse{ + CreatedPatterns: []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: Ptr("published"), + PushProtectionEnabled: false, + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("SecretScanning.CreateCustomPatternsForRepo returned %+v, want %+v", result, want) + } + + const methodName = "CreateCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.CreateCustomPatternsForRepo(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.CreateCustomPatternsForRepo(ctx, "o", "r", input) + return resp, err + }) +} + +func TestSecretScanningService_UpdateCustomPatternForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SecretScanningCustomPatternUpdateRequest{ + Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), + CustomPatternVersion: Ptr("v1"), + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{3}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false, + "custom_pattern_version": "v2" + }`) + }) + + ctx := t.Context() + pattern, _, err := client.SecretScanning.UpdateCustomPatternForRepo(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("SecretScanning.UpdateCustomPatternForRepo returned error: %v", err) + } + + want := &SecretScanningCustomPattern{ + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{3}-[0-9]{4}", + Slug: "custom-pattern", + State: Ptr("published"), + PushProtectionEnabled: false, + CustomPatternVersion: Ptr("v2"), + } + if !cmp.Equal(pattern, want) { + t.Errorf("SecretScanning.UpdateCustomPatternForRepo returned %+v, want %+v", pattern, want) + } + + const methodName = "UpdateCustomPatternForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdateCustomPatternForRepo(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdateCustomPatternForRepo(ctx, "o", "r", 1, input) + return resp, err + }) +} + +func TestSecretScanningService_DeleteCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SecretScanningCustomPatternsDeleteRequest{ + Patterns: []*SecretScanningCustomPatternToDelete{ + {PatternID: 1}, + }, + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "o", "r", input) + if err != nil { + t.Errorf("SecretScanning.DeleteCustomPatternsForRepo returned error: %v", err) + } + + const methodName = "DeleteCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "o", "r", input) + }) +} From 8db584ce4d87b9fd5ddfe5e95a4faf13a2923d17 Mon Sep 17 00:00:00 2001 From: tanayarun Date: Sun, 19 Jul 2026 16:49:00 +0530 Subject: [PATCH 2/4] Add tests for repo-level secret scanning custom patterns --- github/secret_scanning_custom_patterns_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github/secret_scanning_custom_patterns_test.go b/github/secret_scanning_custom_patterns_test.go index a54fd4c8301..76da8f64452 100644 --- a/github/secret_scanning_custom_patterns_test.go +++ b/github/secret_scanning_custom_patterns_test.go @@ -86,7 +86,7 @@ func TestSecretScanningService_CreateCustomPatternsForRepo(t *testing.T) { t.Parallel() client, mux, _ := setup(t) - input := &SecretScanningCustomPatternsCreateRequest{ + input := SecretScanningCustomPatternsCreateRequest{ Patterns: []*SecretScanningCustomPatternRequest{ { Name: "Custom pattern", @@ -151,7 +151,7 @@ func TestSecretScanningService_UpdateCustomPatternForRepo(t *testing.T) { t.Parallel() client, mux, _ := setup(t) - input := &SecretScanningCustomPatternUpdateRequest{ + input := SecretScanningCustomPatternUpdateRequest{ Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), CustomPatternVersion: Ptr("v1"), } @@ -212,7 +212,7 @@ func TestSecretScanningService_DeleteCustomPatternsForRepo(t *testing.T) { }, } - mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") testJSONBody(t, r, input) }) From f2dacf4af4d2558185b0d8411b2028b186253d54 Mon Sep 17 00:00:00 2001 From: tanayarun Date: Wed, 22 Jul 2026 15:56:50 +0530 Subject: [PATCH 3/4] Address review: rename request types, add List opts, doc all fields, use referenceTime in tests --- github/github-accessors.go | 168 +++++++------ github/github-accessors_test.go | 226 ++++++++++-------- github/github-iterators.go | 31 +++ github/github-iterators_test.go | 72 ++++++ github/secret_scanning_custom_patterns.go | 146 ++++++++--- .../secret_scanning_custom_patterns_test.go | 38 +-- 6 files changed, 468 insertions(+), 213 deletions(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index 775946cfbee..4319504a569 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -39454,6 +39454,14 @@ func (s *SecretScanningAlertUpdateOptions) GetState() string { return s.State } +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCreateCustomPatternsRequest) GetPatterns() []*SecretScanningCustomPatternRequest { + if s == nil || s.Patterns == nil { + return nil + } + return s.Patterns +} + // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. func (s *SecretScanningCustomPattern) GetCreatedAt() Timestamp { if s == nil || s.CreatedAt == nil { @@ -39558,6 +39566,38 @@ func (s *SecretScanningCustomPattern) GetUpdatedAt() Timestamp { return *s.UpdatedAt } +// GetDirection returns the Direction field. +func (s *SecretScanningCustomPatternListOptions) GetDirection() string { + if s == nil { + return "" + } + return s.Direction +} + +// GetPushProtection returns the PushProtection field. +func (s *SecretScanningCustomPatternListOptions) GetPushProtection() string { + if s == nil { + return "" + } + return s.PushProtection +} + +// GetSort returns the Sort field. +func (s *SecretScanningCustomPatternListOptions) GetSort() string { + if s == nil { + return "" + } + return s.Sort +} + +// GetState returns the State field. +func (s *SecretScanningCustomPatternListOptions) GetState() string { + if s == nil { + return "" + } + return s.State +} + // GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. func (s *SecretScanningCustomPatternRequest) GetEndDelimiter() string { if s == nil || s.EndDelimiter == nil { @@ -39606,14 +39646,6 @@ func (s *SecretScanningCustomPatternRequest) GetStartDelimiter() string { return *s.StartDelimiter } -// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. -func (s *SecretScanningCustomPatternsCreateRequest) GetPatterns() []*SecretScanningCustomPatternRequest { - if s == nil || s.Patterns == nil { - return nil - } - return s.Patterns -} - // GetCreatedPatterns returns the CreatedPatterns slice if it's non-nil, nil otherwise. func (s *SecretScanningCustomPatternsCreateResponse) GetCreatedPatterns() []*SecretScanningCustomPattern { if s == nil || s.CreatedPatterns == nil { @@ -39622,22 +39654,6 @@ func (s *SecretScanningCustomPatternsCreateResponse) GetCreatedPatterns() []*Sec return s.CreatedPatterns } -// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. -func (s *SecretScanningCustomPatternsDeleteRequest) GetPatterns() []*SecretScanningCustomPatternToDelete { - if s == nil || s.Patterns == nil { - return nil - } - return s.Patterns -} - -// GetPostDeleteAction returns the PostDeleteAction field if it's non-nil, zero value otherwise. -func (s *SecretScanningCustomPatternsDeleteRequest) GetPostDeleteAction() string { - if s == nil || s.PostDeleteAction == nil { - return "" - } - return *s.PostDeleteAction -} - // GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. func (s *SecretScanningCustomPatternSetting) GetCustomPatternVersion() string { if s == nil || s.CustomPatternVersion == nil { @@ -39678,60 +39694,28 @@ func (s *SecretScanningCustomPatternToDelete) GetPatternID() int64 { return s.PatternID } -// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetCustomPatternVersion() string { - if s == nil || s.CustomPatternVersion == nil { - return "" - } - return *s.CustomPatternVersion -} - -// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetEndDelimiter() string { - if s == nil || s.EndDelimiter == nil { - return "" - } - return *s.EndDelimiter -} - -// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetMustMatch() []string { - if s == nil || s.MustMatch == nil { +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (s *SecretScanningDelegatedBypassOptions) GetReviewers() []*BypassReviewer { + if s == nil || s.Reviewers == nil { return nil } - return s.MustMatch + return s.Reviewers } -// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetMustNotMatch() []string { - if s == nil || s.MustNotMatch == nil { +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningDeleteCustomPatternsRequest) GetPatterns() []*SecretScanningCustomPatternToDelete { + if s == nil || s.Patterns == nil { return nil } - return s.MustNotMatch -} - -// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetPattern() string { - if s == nil || s.Pattern == nil { - return "" - } - return *s.Pattern + return s.Patterns } -// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. -func (s *SecretScanningCustomPatternUpdateRequest) GetStartDelimiter() string { - if s == nil || s.StartDelimiter == nil { +// GetPostDeleteAction returns the PostDeleteAction field if it's non-nil, zero value otherwise. +func (s *SecretScanningDeleteCustomPatternsRequest) GetPostDeleteAction() string { + if s == nil || s.PostDeleteAction == nil { return "" } - return *s.StartDelimiter -} - -// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. -func (s *SecretScanningDelegatedBypassOptions) GetReviewers() []*BypassReviewer { - if s == nil || s.Reviewers == nil { - return nil - } - return s.Reviewers + return *s.PostDeleteAction } // GetCustomPatternOverrides returns the CustomPatternOverrides slice if it's non-nil, nil otherwise. @@ -39942,6 +39926,54 @@ func (s *SecretScanningScanHistory) GetPatternUpdateScans() []*SecretsScan { return s.PatternUpdateScans } +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { + return "" + } + return *s.CustomPatternVersion +} + +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" + } + return *s.EndDelimiter +} + +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { + return nil + } + return s.MustMatch +} + +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil + } + return s.MustNotMatch +} + +// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetPattern() string { + if s == nil || s.Pattern == nil { + return "" + } + return *s.Pattern +} + +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { + return "" + } + return *s.StartDelimiter +} + // GetStatus returns the Status field if it's non-nil, zero value otherwise. func (s *SecretScanningValidityChecks) GetStatus() string { if s == nil || s.Status == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 6e56f06c485..e4514eeb419 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -49394,6 +49394,17 @@ func TestSecretScanningAlertUpdateOptions_GetState(tt *testing.T) { s.GetState() } +func TestSecretScanningCreateCustomPatternsRequest_GetPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternRequest{} + s := &SecretScanningCreateCustomPatternsRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningCreateCustomPatternsRequest{} + s.GetPatterns() + s = nil + s.GetPatterns() +} + func TestSecretScanningCustomPattern_GetCreatedAt(tt *testing.T) { tt.Parallel() var zeroValue Timestamp @@ -49522,6 +49533,38 @@ func TestSecretScanningCustomPattern_GetUpdatedAt(tt *testing.T) { s.GetUpdatedAt() } +func TestSecretScanningCustomPatternListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetDirection() + s = nil + s.GetDirection() +} + +func TestSecretScanningCustomPatternListOptions_GetPushProtection(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetPushProtection() + s = nil + s.GetPushProtection() +} + +func TestSecretScanningCustomPatternListOptions_GetSort(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetSort() + s = nil + s.GetSort() +} + +func TestSecretScanningCustomPatternListOptions_GetState(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetState() + s = nil + s.GetState() +} + func TestSecretScanningCustomPatternRequest_GetEndDelimiter(tt *testing.T) { tt.Parallel() var zeroValue string @@ -49582,17 +49625,6 @@ func TestSecretScanningCustomPatternRequest_GetStartDelimiter(tt *testing.T) { s.GetStartDelimiter() } -func TestSecretScanningCustomPatternsCreateRequest_GetPatterns(tt *testing.T) { - tt.Parallel() - zeroValue := []*SecretScanningCustomPatternRequest{} - s := &SecretScanningCustomPatternsCreateRequest{Patterns: zeroValue} - s.GetPatterns() - s = &SecretScanningCustomPatternsCreateRequest{} - s.GetPatterns() - s = nil - s.GetPatterns() -} - func TestSecretScanningCustomPatternsCreateResponse_GetCreatedPatterns(tt *testing.T) { tt.Parallel() zeroValue := []*SecretScanningCustomPattern{} @@ -49604,28 +49636,6 @@ func TestSecretScanningCustomPatternsCreateResponse_GetCreatedPatterns(tt *testi s.GetCreatedPatterns() } -func TestSecretScanningCustomPatternsDeleteRequest_GetPatterns(tt *testing.T) { - tt.Parallel() - zeroValue := []*SecretScanningCustomPatternToDelete{} - s := &SecretScanningCustomPatternsDeleteRequest{Patterns: zeroValue} - s.GetPatterns() - s = &SecretScanningCustomPatternsDeleteRequest{} - s.GetPatterns() - s = nil - s.GetPatterns() -} - -func TestSecretScanningCustomPatternsDeleteRequest_GetPostDeleteAction(tt *testing.T) { - tt.Parallel() - var zeroValue string - s := &SecretScanningCustomPatternsDeleteRequest{PostDeleteAction: &zeroValue} - s.GetPostDeleteAction() - s = &SecretScanningCustomPatternsDeleteRequest{} - s.GetPostDeleteAction() - s = nil - s.GetPostDeleteAction() -} - func TestSecretScanningCustomPatternSetting_GetCustomPatternVersion(tt *testing.T) { tt.Parallel() var zeroValue string @@ -49672,81 +49682,37 @@ func TestSecretScanningCustomPatternToDelete_GetPatternID(tt *testing.T) { s.GetPatternID() } -func TestSecretScanningCustomPatternUpdateRequest_GetCustomPatternVersion(tt *testing.T) { - tt.Parallel() - var zeroValue string - s := &SecretScanningCustomPatternUpdateRequest{CustomPatternVersion: &zeroValue} - s.GetCustomPatternVersion() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetCustomPatternVersion() - s = nil - s.GetCustomPatternVersion() -} - -func TestSecretScanningCustomPatternUpdateRequest_GetEndDelimiter(tt *testing.T) { - tt.Parallel() - var zeroValue string - s := &SecretScanningCustomPatternUpdateRequest{EndDelimiter: &zeroValue} - s.GetEndDelimiter() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetEndDelimiter() - s = nil - s.GetEndDelimiter() -} - -func TestSecretScanningCustomPatternUpdateRequest_GetMustMatch(tt *testing.T) { - tt.Parallel() - zeroValue := []string{} - s := &SecretScanningCustomPatternUpdateRequest{MustMatch: zeroValue} - s.GetMustMatch() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetMustMatch() - s = nil - s.GetMustMatch() -} - -func TestSecretScanningCustomPatternUpdateRequest_GetMustNotMatch(tt *testing.T) { +func TestSecretScanningDelegatedBypassOptions_GetReviewers(tt *testing.T) { tt.Parallel() - zeroValue := []string{} - s := &SecretScanningCustomPatternUpdateRequest{MustNotMatch: zeroValue} - s.GetMustNotMatch() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetMustNotMatch() + zeroValue := []*BypassReviewer{} + s := &SecretScanningDelegatedBypassOptions{Reviewers: zeroValue} + s.GetReviewers() + s = &SecretScanningDelegatedBypassOptions{} + s.GetReviewers() s = nil - s.GetMustNotMatch() + s.GetReviewers() } -func TestSecretScanningCustomPatternUpdateRequest_GetPattern(tt *testing.T) { +func TestSecretScanningDeleteCustomPatternsRequest_GetPatterns(tt *testing.T) { tt.Parallel() - var zeroValue string - s := &SecretScanningCustomPatternUpdateRequest{Pattern: &zeroValue} - s.GetPattern() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetPattern() + zeroValue := []*SecretScanningCustomPatternToDelete{} + s := &SecretScanningDeleteCustomPatternsRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningDeleteCustomPatternsRequest{} + s.GetPatterns() s = nil - s.GetPattern() + s.GetPatterns() } -func TestSecretScanningCustomPatternUpdateRequest_GetStartDelimiter(tt *testing.T) { +func TestSecretScanningDeleteCustomPatternsRequest_GetPostDeleteAction(tt *testing.T) { tt.Parallel() var zeroValue string - s := &SecretScanningCustomPatternUpdateRequest{StartDelimiter: &zeroValue} - s.GetStartDelimiter() - s = &SecretScanningCustomPatternUpdateRequest{} - s.GetStartDelimiter() - s = nil - s.GetStartDelimiter() -} - -func TestSecretScanningDelegatedBypassOptions_GetReviewers(tt *testing.T) { - tt.Parallel() - zeroValue := []*BypassReviewer{} - s := &SecretScanningDelegatedBypassOptions{Reviewers: zeroValue} - s.GetReviewers() - s = &SecretScanningDelegatedBypassOptions{} - s.GetReviewers() + s := &SecretScanningDeleteCustomPatternsRequest{PostDeleteAction: &zeroValue} + s.GetPostDeleteAction() + s = &SecretScanningDeleteCustomPatternsRequest{} + s.GetPostDeleteAction() s = nil - s.GetReviewers() + s.GetPostDeleteAction() } func TestSecretScanningPatternConfigs_GetCustomPatternOverrides(tt *testing.T) { @@ -50029,6 +49995,72 @@ func TestSecretScanningScanHistory_GetPatternUpdateScans(tt *testing.T) { s.GetPatternUpdateScans() } +func TestSecretScanningUpdateCustomPatternRequest_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningUpdateCustomPatternRequest{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningUpdateCustomPatternRequest{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetPattern(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{Pattern: &zeroValue} + s.GetPattern() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + func TestSecretScanningValidityChecks_GetStatus(tt *testing.T) { tt.Parallel() var zeroValue string diff --git a/github/github-iterators.go b/github/github-iterators.go index 8248f6b5edb..701004531d7 100644 --- a/github/github-iterators.go +++ b/github/github-iterators.go @@ -6719,6 +6719,37 @@ func (s *SecretScanningService) ListAlertsForRepoIter(ctx context.Context, owner } } +// ListCustomPatternsForRepoIter returns an iterator that paginates through all results of ListCustomPatternsForRepo. +func (s *SecretScanningService) ListCustomPatternsForRepoIter(ctx context.Context, owner string, repo string, opts *SecretScanningCustomPatternListOptions) iter.Seq2[*SecretScanningCustomPattern, error] { + return func(yield func(*SecretScanningCustomPattern, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningCustomPatternListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomPatternsForRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + // ListLocationsForAlertIter returns an iterator that paginates through all results of ListLocationsForAlert. func (s *SecretScanningService) ListLocationsForAlertIter(ctx context.Context, owner string, repo string, number int64, opts *ListOptions) iter.Seq2[*SecretScanningAlertLocation, error] { return func(yield func(*SecretScanningAlertLocation, error) bool) { diff --git a/github/github-iterators_test.go b/github/github-iterators_test.go index 9a3ffc31fd4..ea51452061f 100644 --- a/github/github-iterators_test.go +++ b/github/github-iterators_test.go @@ -14919,6 +14919,78 @@ func TestSecretScanningService_ListAlertsForRepoIter(t *testing.T) { } } +func TestSecretScanningService_ListCustomPatternsForRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningCustomPatternListOptions{} + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *SecretScanningCustomPattern, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + func TestSecretScanningService_ListLocationsForAlertIter(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/secret_scanning_custom_patterns.go b/github/secret_scanning_custom_patterns.go index 83e87e6e840..f7c9ac46e2f 100644 --- a/github/secret_scanning_custom_patterns.go +++ b/github/secret_scanning_custom_patterns.go @@ -15,56 +15,92 @@ import ( // // GitHub API docs: https://docs.github.com/en/rest/secret-scanning/secret-scanning type SecretScanningCustomPattern struct { - ID int64 `json:"id"` - Name string `json:"name"` + // ID is the unique identifier of the custom pattern. + ID int64 `json:"id"` + + // Name is the name of the custom pattern. + Name string `json:"name"` + + // Pattern is the regular expression that defines the custom pattern. Pattern string `json:"pattern"` - Slug string `json:"slug"` + + // Slug is the URL-friendly identifier derived from the pattern name. + Slug string `json:"slug"` // State is the publish state of the pattern. Possible values are: // "published" or "unpublished". State *string `json:"state,omitempty"` - PushProtectionEnabled bool `json:"push_protection_enabled"` - StartDelimiter *string `json:"start_delimiter,omitempty"` - EndDelimiter *string `json:"end_delimiter,omitempty"` - MustMatch []string `json:"must_match,omitempty"` - MustNotMatch []string `json:"must_not_match,omitempty"` + // PushProtectionEnabled reports whether push protection is enabled for + // this custom pattern. + PushProtectionEnabled bool `json:"push_protection_enabled"` + + // StartDelimiter is the start delimiter regex for the custom pattern. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the end delimiter regex for the custom pattern. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the list of regexes that the secret must not match. + MustNotMatch []string `json:"must_not_match,omitempty"` // CustomPatternVersion is used to confirm you're updating the current // version of the pattern and to avoid unintentionally overriding // someone else's update. - CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` + + // CreatedAt is the time the custom pattern was created. + CreatedAt *Timestamp `json:"created_at,omitempty"` + + // UpdatedAt is the time the custom pattern was last updated. + UpdatedAt *Timestamp `json:"updated_at,omitempty"` } -// SecretScanningCustomPatternRequest represents a custom pattern to be -// created for secret scanning. +// SecretScanningCustomPatternRequest represents a single custom pattern to +// be created for secret scanning. type SecretScanningCustomPatternRequest struct { - Name string `json:"name"` + // Name is the name of the custom pattern. + Name string `json:"name"` + + // Pattern is the regular expression that defines the custom pattern. Pattern string `json:"pattern"` - StartDelimiter *string `json:"start_delimiter,omitempty"` - EndDelimiter *string `json:"end_delimiter,omitempty"` - MustMatch []string `json:"must_match,omitempty"` - MustNotMatch []string `json:"must_not_match,omitempty"` + // StartDelimiter is the start delimiter regex for the custom pattern. + // Defaults to a sensible boundary regex when not specified. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the end delimiter regex for the custom pattern. + // Defaults to a sensible boundary regex when not specified. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the list of regexes that the secret must not match. + MustNotMatch []string `json:"must_not_match,omitempty"` } -// SecretScanningCustomPatternsCreateRequest represents the bulk request body +// SecretScanningCreateCustomPatternsRequest represents the bulk request body // used to create one or more custom patterns. -type SecretScanningCustomPatternsCreateRequest struct { +type SecretScanningCreateCustomPatternsRequest struct { + // Patterns is the list of custom patterns to create. Patterns []*SecretScanningCustomPatternRequest `json:"patterns"` } // SecretScanningCustomPatternsCreateResponse represents the bulk response // returned after creating one or more custom patterns. type SecretScanningCustomPatternsCreateResponse struct { + // CreatedPatterns is the list of successfully created custom patterns. CreatedPatterns []*SecretScanningCustomPattern `json:"created_patterns"` } // SecretScanningCustomPatternToDelete identifies a single custom pattern to // remove in a bulk delete operation. type SecretScanningCustomPatternToDelete struct { + // PatternID is the ID of the custom pattern to delete. PatternID int64 `json:"pattern_id"` // CustomPatternVersion is used to confirm you're deleting the current @@ -72,9 +108,10 @@ type SecretScanningCustomPatternToDelete struct { CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` } -// SecretScanningCustomPatternsDeleteRequest represents the bulk request body +// SecretScanningDeleteCustomPatternsRequest represents the bulk request body // used to delete one or more custom patterns. -type SecretScanningCustomPatternsDeleteRequest struct { +type SecretScanningDeleteCustomPatternsRequest struct { + // Patterns is the list of custom patterns to delete. Patterns []*SecretScanningCustomPatternToDelete `json:"patterns"` // PostDeleteAction controls what happens to alerts associated with the @@ -83,29 +120,70 @@ type SecretScanningCustomPatternsDeleteRequest struct { PostDeleteAction *string `json:"post_delete_action,omitempty"` } -// SecretScanningCustomPatternUpdateRequest represents the fields that can be +// SecretScanningUpdateCustomPatternRequest represents the fields that can be // updated on an existing custom pattern. At least one of Pattern, // StartDelimiter, EndDelimiter, MustMatch, or MustNotMatch must be set. -type SecretScanningCustomPatternUpdateRequest struct { - Pattern *string `json:"pattern,omitempty"` - StartDelimiter *string `json:"start_delimiter,omitempty"` - EndDelimiter *string `json:"end_delimiter,omitempty"` - MustMatch []string `json:"must_match,omitempty"` - MustNotMatch []string `json:"must_not_match,omitempty"` +type SecretScanningUpdateCustomPatternRequest struct { + // Pattern is the updated regular expression of the custom pattern. + Pattern *string `json:"pattern,omitempty"` + + // StartDelimiter is the updated start delimiter regex for the custom + // pattern. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the updated end delimiter regex for the custom + // pattern. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the updated list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the updated list of regexes that the secret must not + // match. + MustNotMatch []string `json:"must_not_match,omitempty"` // CustomPatternVersion is required and is used to confirm you're // updating the current version of the pattern. CustomPatternVersion *string `json:"custom_pattern_version"` } +// SecretScanningCustomPatternListOptions specifies the optional parameters +// to the SecretScanningService.ListCustomPatternsForRepo method. +type SecretScanningCustomPatternListOptions struct { + // State filters custom patterns by state. Possible values are: + // "published" or "unpublished". When absent, returns patterns in all + // states. + State string `url:"state,omitempty"` + + // PushProtection filters custom patterns by whether push protection is + // enabled. Possible values are: "enabled" or "disabled". When absent, + // returns patterns regardless of push protection status. + PushProtection string `url:"push_protection,omitempty"` + + // Sort is the property to sort the results by. Possible values are: + // "created", "updated", or "name". Default: "created". + Sort string `url:"sort,omitempty"` + + // Direction is the direction to sort the results by. Possible values + // are: "asc" or "desc". Default: "desc". + Direction string `url:"direction,omitempty"` + + // ListOptions controls pagination via Page and PerPage. + ListOptions +} + // ListCustomPatternsForRepo lists the secret scanning custom patterns // defined at the repository level. // // GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#list-repository-custom-patterns // //meta:operation GET /repos/{owner}/{repo}/secret-scanning/custom-patterns -func (s *SecretScanningService) ListCustomPatternsForRepo(ctx context.Context, owner, repo string) ([]*SecretScanningCustomPattern, *Response, error) { +func (s *SecretScanningService) ListCustomPatternsForRepo(ctx context.Context, owner, repo string, opts *SecretScanningCustomPatternListOptions) ([]*SecretScanningCustomPattern, *Response, error) { u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { @@ -127,7 +205,7 @@ func (s *SecretScanningService) ListCustomPatternsForRepo(ctx context.Context, o // GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-create-repository-custom-patterns // //meta:operation POST /repos/{owner}/{repo}/secret-scanning/custom-patterns -func (s *SecretScanningService) CreateCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningCustomPatternsCreateRequest) (*SecretScanningCustomPatternsCreateResponse, *Response, error) { +func (s *SecretScanningService) CreateCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningCreateCustomPatternsRequest) (*SecretScanningCustomPatternsCreateResponse, *Response, error) { u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) req, err := s.client.NewRequest(ctx, "POST", u, body) @@ -150,7 +228,7 @@ func (s *SecretScanningService) CreateCustomPatternsForRepo(ctx context.Context, // GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#update-a-repository-custom-pattern // //meta:operation PATCH /repos/{owner}/{repo}/secret-scanning/custom-patterns/{pattern_id} -func (s *SecretScanningService) UpdateCustomPatternForRepo(ctx context.Context, owner, repo string, patternID int64, body SecretScanningCustomPatternUpdateRequest) (*SecretScanningCustomPattern, *Response, error) { +func (s *SecretScanningService) UpdateCustomPatternForRepo(ctx context.Context, owner, repo string, patternID int64, body SecretScanningUpdateCustomPatternRequest) (*SecretScanningCustomPattern, *Response, error) { u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns/%v", owner, repo, patternID) req, err := s.client.NewRequest(ctx, "PATCH", u, body) @@ -173,10 +251,10 @@ func (s *SecretScanningService) UpdateCustomPatternForRepo(ctx context.Context, // GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-delete-repository-custom-patterns // //meta:operation DELETE /repos/{owner}/{repo}/secret-scanning/custom-patterns -func (s *SecretScanningService) DeleteCustomPatternsForRepo(ctx context.Context, owner, repo string, patterns *SecretScanningCustomPatternsDeleteRequest) (*Response, error) { +func (s *SecretScanningService) DeleteCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningDeleteCustomPatternsRequest) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) - req, err := s.client.NewRequest(ctx, "DELETE", u, patterns) + req, err := s.client.NewRequest(ctx, "DELETE", u, body) if err != nil { return nil, err } diff --git a/github/secret_scanning_custom_patterns_test.go b/github/secret_scanning_custom_patterns_test.go index 76da8f64452..12bf14178fb 100644 --- a/github/secret_scanning_custom_patterns_test.go +++ b/github/secret_scanning_custom_patterns_test.go @@ -9,7 +9,6 @@ import ( "fmt" "net/http" "testing" - "time" "github.com/google/go-cmp/cmp" ) @@ -20,6 +19,13 @@ func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "published", + "push_protection": "enabled", + "sort": "created", + "direction": "desc", + "page": "2", + }) fmt.Fprint(w, `[ { "id": 1, @@ -33,21 +39,25 @@ func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { "must_match": ["ID-.*"], "must_not_match": ["TEST-.*"], "custom_pattern_version": "v1", - "created_at": "2026-07-01T00:00:00Z", - "updated_at": "2026-07-02T00:00:00Z" + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+` } ]`) }) ctx := t.Context() - patterns, _, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r") + opts := &SecretScanningCustomPatternListOptions{ + State: "published", + PushProtection: "enabled", + Sort: "created", + Direction: "desc", + ListOptions: ListOptions{Page: 2}, + } + patterns, _, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r", opts) if err != nil { t.Errorf("SecretScanning.ListCustomPatternsForRepo returned error: %v", err) } - createdAt, _ := time.Parse(time.RFC3339, "2026-07-01T00:00:00Z") - updatedAt, _ := time.Parse(time.RFC3339, "2026-07-02T00:00:00Z") - want := []*SecretScanningCustomPattern{ { ID: 1, @@ -61,8 +71,8 @@ func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { MustMatch: []string{"ID-.*"}, MustNotMatch: []string{"TEST-.*"}, CustomPatternVersion: Ptr("v1"), - CreatedAt: &Timestamp{createdAt}, - UpdatedAt: &Timestamp{updatedAt}, + CreatedAt: &referenceTimestamp, + UpdatedAt: &referenceTimestamp, }, } if !cmp.Equal(patterns, want) { @@ -72,12 +82,12 @@ func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { const methodName = "ListCustomPatternsForRepo" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.SecretScanning.ListCustomPatternsForRepo(ctx, "\n", "\n") + _, _, err = client.SecretScanning.ListCustomPatternsForRepo(ctx, "\n", "\n", nil) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - _, resp, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r") + _, resp, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r", nil) return resp, err }) } @@ -86,7 +96,7 @@ func TestSecretScanningService_CreateCustomPatternsForRepo(t *testing.T) { t.Parallel() client, mux, _ := setup(t) - input := SecretScanningCustomPatternsCreateRequest{ + input := SecretScanningCreateCustomPatternsRequest{ Patterns: []*SecretScanningCustomPatternRequest{ { Name: "Custom pattern", @@ -151,7 +161,7 @@ func TestSecretScanningService_UpdateCustomPatternForRepo(t *testing.T) { t.Parallel() client, mux, _ := setup(t) - input := SecretScanningCustomPatternUpdateRequest{ + input := SecretScanningUpdateCustomPatternRequest{ Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), CustomPatternVersion: Ptr("v1"), } @@ -206,7 +216,7 @@ func TestSecretScanningService_DeleteCustomPatternsForRepo(t *testing.T) { t.Parallel() client, mux, _ := setup(t) - input := &SecretScanningCustomPatternsDeleteRequest{ + input := SecretScanningDeleteCustomPatternsRequest{ Patterns: []*SecretScanningCustomPatternToDelete{ {PatternID: 1}, }, From c3f140356eb402ead3881325dba112b88484dba3 Mon Sep 17 00:00:00 2001 From: tanayarun Date: Fri, 24 Jul 2026 14:08:48 +0530 Subject: [PATCH 4/4] Make State a required field per response schema --- github/github-accessors.go | 6 +++--- github/github-accessors_test.go | 5 +---- github/secret_scanning_custom_patterns.go | 2 +- github/secret_scanning_custom_patterns_test.go | 6 +++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index 4319504a569..aef5494ccc3 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -39550,12 +39550,12 @@ func (s *SecretScanningCustomPattern) GetStartDelimiter() string { return *s.StartDelimiter } -// GetState returns the State field if it's non-nil, zero value otherwise. +// GetState returns the State field. func (s *SecretScanningCustomPattern) GetState() string { - if s == nil || s.State == nil { + if s == nil { return "" } - return *s.State + return s.State } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index e4514eeb419..96d7a0afffb 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -49513,10 +49513,7 @@ func TestSecretScanningCustomPattern_GetStartDelimiter(tt *testing.T) { func TestSecretScanningCustomPattern_GetState(tt *testing.T) { tt.Parallel() - var zeroValue string - s := &SecretScanningCustomPattern{State: &zeroValue} - s.GetState() - s = &SecretScanningCustomPattern{} + s := &SecretScanningCustomPattern{} s.GetState() s = nil s.GetState() diff --git a/github/secret_scanning_custom_patterns.go b/github/secret_scanning_custom_patterns.go index f7c9ac46e2f..a806494c85c 100644 --- a/github/secret_scanning_custom_patterns.go +++ b/github/secret_scanning_custom_patterns.go @@ -29,7 +29,7 @@ type SecretScanningCustomPattern struct { // State is the publish state of the pattern. Possible values are: // "published" or "unpublished". - State *string `json:"state,omitempty"` + State string `json:"state"` // PushProtectionEnabled reports whether push protection is enabled for // this custom pattern. diff --git a/github/secret_scanning_custom_patterns_test.go b/github/secret_scanning_custom_patterns_test.go index 12bf14178fb..8b7d229fe2e 100644 --- a/github/secret_scanning_custom_patterns_test.go +++ b/github/secret_scanning_custom_patterns_test.go @@ -64,7 +64,7 @@ func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { Name: "Custom pattern", Pattern: "[A-Z]{2}-[0-9]{4}", Slug: "custom-pattern", - State: Ptr("published"), + State: "published", PushProtectionEnabled: true, StartDelimiter: Ptr(`\b`), EndDelimiter: Ptr(`\b`), @@ -135,7 +135,7 @@ func TestSecretScanningService_CreateCustomPatternsForRepo(t *testing.T) { Name: "Custom pattern", Pattern: "[A-Z]{2}-[0-9]{4}", Slug: "custom-pattern", - State: Ptr("published"), + State: "published", PushProtectionEnabled: false, }, }, @@ -191,7 +191,7 @@ func TestSecretScanningService_UpdateCustomPatternForRepo(t *testing.T) { Name: "Custom pattern", Pattern: "[A-Z]{3}-[0-9]{4}", Slug: "custom-pattern", - State: Ptr("published"), + State: "published", PushProtectionEnabled: false, CustomPatternVersion: Ptr("v2"), }