From 9b182400827dd7e844b9b080e841fe030ea9b04a Mon Sep 17 00:00:00 2001 From: Andrew Svoboda Date: Tue, 19 Oct 2021 18:54:51 -0400 Subject: [PATCH 1/3] Support Github Advanced Security repository settings Resolves #2132 --- github/github-accessors.go | 40 ++++++++++++++++++++++++++++++++ github/github-accessors_test.go | 41 +++++++++++++++++++++++++++++++++ github/github-stringify_test.go | 34 ++++++++++++++++++++++++++- github/repos.go | 34 +++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index cd802ff1e21..1d2db4ecadb 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -148,6 +148,14 @@ func (a *AdminStats) GetUsers() *UserStats { return a.Users } +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (a *AdvancedSecurity) GetStatus() string { + if a == nil || a.Status == nil { + return "" + } + return *a.Status +} + // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. func (a *Alert) GetClosedAt() Timestamp { if a == nil || a.ClosedAt == nil { @@ -13676,6 +13684,14 @@ func (r *Repository) GetReleasesURL() string { return *r.ReleasesURL } +// GetSecurityAndAnalysis returns the SecurityAndAnalysis field. +func (r *Repository) GetSecurityAndAnalysis() *SecurityAndAnalysis { + if r == nil { + return nil + } + return r.SecurityAndAnalysis +} + // GetSize returns the Size field if it's non-nil, zero value otherwise. func (r *Repository) GetSize() int { if r == nil || r.Size == nil { @@ -15180,6 +15196,30 @@ func (s *SCIMUserName) GetFormatted() string { return *s.Formatted } +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SecretScanning) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + +// GetAdvancedSecurity returns the AdvancedSecurity field. +func (s *SecurityAndAnalysis) GetAdvancedSecurity() *AdvancedSecurity { + if s == nil { + return nil + } + return s.AdvancedSecurity +} + +// GetSecretScanning returns the SecretScanning field. +func (s *SecurityAndAnalysis) GetSecretScanning() *SecretScanning { + if s == nil { + return nil + } + return s.SecretScanning +} + // GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. func (s *SelectedReposList) GetTotalCount() int { if s == nil || s.TotalCount == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 2a77bbf90bd..f6af9991366 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -153,6 +153,16 @@ func TestAdminStats_GetUsers(tt *testing.T) { a.GetUsers() } +func TestAdvancedSecurity_GetStatus(tt *testing.T) { + var zeroValue string + a := &AdvancedSecurity{Status: &zeroValue} + a.GetStatus() + a = &AdvancedSecurity{} + a.GetStatus() + a = nil + a.GetStatus() +} + func TestAlert_GetClosedAt(tt *testing.T) { var zeroValue Timestamp a := &Alert{ClosedAt: &zeroValue} @@ -15965,6 +15975,13 @@ func TestRepository_GetReleasesURL(tt *testing.T) { r.GetReleasesURL() } +func TestRepository_GetSecurityAndAnalysis(tt *testing.T) { + r := &Repository{} + r.GetSecurityAndAnalysis() + r = nil + r.GetSecurityAndAnalysis() +} + func TestRepository_GetSize(tt *testing.T) { var zeroValue int r := &Repository{Size: &zeroValue} @@ -17752,6 +17769,30 @@ func TestSCIMUserName_GetFormatted(tt *testing.T) { s.GetFormatted() } +func TestSecretScanning_GetStatus(tt *testing.T) { + var zeroValue string + s := &SecretScanning{Status: &zeroValue} + s.GetStatus() + s = &SecretScanning{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSecurityAndAnalysis_GetAdvancedSecurity(tt *testing.T) { + s := &SecurityAndAnalysis{} + s.GetAdvancedSecurity() + s = nil + s.GetAdvancedSecurity() +} + +func TestSecurityAndAnalysis_GetSecretScanning(tt *testing.T) { + s := &SecurityAndAnalysis{} + s.GetSecretScanning() + s = nil + s.GetSecretScanning() +} + func TestSelectedReposList_GetTotalCount(tt *testing.T) { var zeroValue int s := &SelectedReposList{TotalCount: &zeroValue} diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go index a5c9b454544..2db35cd64a7 100644 --- a/github/github-stringify_test.go +++ b/github/github-stringify_test.go @@ -55,6 +55,16 @@ func TestAdminStats_String(t *testing.T) { } } +func TestAdvancedSecurity_String(t *testing.T) { + v := AdvancedSecurity{ + Status: String(""), + } + want := `github.AdvancedSecurity{Status:""}` + if got := v.String(); got != want { + t.Errorf("AdvancedSecurity.String = %v, want %v", got, want) + } +} + func TestAuthorization_String(t *testing.T) { v := Authorization{ ID: Int64(0), @@ -1456,6 +1466,7 @@ func TestRepository_String(t *testing.T) { IsTemplate: Bool(false), LicenseTemplate: String(""), GitignoreTemplate: String(""), + SecurityAndAnalysis: &SecurityAndAnalysis{}, TeamID: Int64(0), URL: String(""), ArchiveURL: String(""), @@ -1496,7 +1507,7 @@ func TestRepository_String(t *testing.T) { TeamsURL: String(""), Visibility: String(""), } - want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, DeleteBranchOnMerge:false, Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:""}` + want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, DeleteBranchOnMerge:false, Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", SecurityAndAnalysis:github.SecurityAndAnalysis{}, TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:""}` if got := v.String(); got != want { t.Errorf("Repository.String = %v, want %v", got, want) } @@ -1609,6 +1620,27 @@ func TestRepositoryRelease_String(t *testing.T) { } } +func TestSecretScanning_String(t *testing.T) { + v := SecretScanning{ + Status: String(""), + } + want := `github.SecretScanning{Status:""}` + if got := v.String(); got != want { + t.Errorf("SecretScanning.String = %v, want %v", got, want) + } +} + +func TestSecurityAndAnalysis_String(t *testing.T) { + v := SecurityAndAnalysis{ + AdvancedSecurity: &AdvancedSecurity{}, + SecretScanning: &SecretScanning{}, + } + want := `github.SecurityAndAnalysis{AdvancedSecurity:github.AdvancedSecurity{}, SecretScanning:github.SecretScanning{}}` + if got := v.String(); got != want { + t.Errorf("SecurityAndAnalysis.String = %v, want %v", got, want) + } +} + func TestSourceImportAuthor_String(t *testing.T) { v := SourceImportAuthor{ ID: Int64(0), diff --git a/github/repos.go b/github/repos.go index f6236b432f7..81f457283ee 100644 --- a/github/repos.go +++ b/github/repos.go @@ -80,6 +80,9 @@ type Repository struct { LicenseTemplate *string `json:"license_template,omitempty"` GitignoreTemplate *string `json:"gitignore_template,omitempty"` + // Options for configuring Advanced Security and Secret Scanning + SecurityAndAnalysis *SecurityAndAnalysis `json:"security_and_analysis,omitempty"` + // Creating an organization repository. Required for non-owners. TeamID *int64 `json:"team_id,omitempty"` @@ -183,6 +186,37 @@ type RepositoryListOptions struct { ListOptions } +// SecurityAndAnalysis specifies the optional advanced security features +// that are enabled on a given repository +type SecurityAndAnalysis struct { + AdvancedSecurity *AdvancedSecurity `json:"advanced_security,omitempty"` + SecretScanning *SecretScanning `json:"secret_scanning,omitempty"` +} + +func (s SecurityAndAnalysis) String() string { + return Stringify(s) +} + +// AdvancedSecurity specifies the state of advanced security on a repository +// https://docs.github.com/en/github/getting-started-with-github/learning-about-github/about-github-advanced-security +type AdvancedSecurity struct { + Status *string `json:"status"` +} + +func (a AdvancedSecurity) String() string { + return Stringify(a) +} + +// SecretScanning specifies the state of secret scanning on a repository +// https://docs.github.com/en/code-security/secret-security/about-secret-scanning +type SecretScanning struct { + Status *string `json:"status"` +} + +func (s SecretScanning) String() string { + return Stringify(s) +} + // List the repositories for a user. Passing the empty string will list // repositories for the authenticated user. // From c8d018dd252e87ff2f43843d7a234a6225956e90 Mon Sep 17 00:00:00 2001 From: Andrew Svoboda Date: Tue, 19 Oct 2021 19:15:15 -0400 Subject: [PATCH 2/3] Fix omitempty json tags and documentation notes --- github/repos.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/github/repos.go b/github/repos.go index 81f457283ee..391f3139fcb 100644 --- a/github/repos.go +++ b/github/repos.go @@ -187,7 +187,7 @@ type RepositoryListOptions struct { } // SecurityAndAnalysis specifies the optional advanced security features -// that are enabled on a given repository +// that are enabled on a given repository. type SecurityAndAnalysis struct { AdvancedSecurity *AdvancedSecurity `json:"advanced_security,omitempty"` SecretScanning *SecretScanning `json:"secret_scanning,omitempty"` @@ -197,20 +197,22 @@ func (s SecurityAndAnalysis) String() string { return Stringify(s) } -// AdvancedSecurity specifies the state of advanced security on a repository -// https://docs.github.com/en/github/getting-started-with-github/learning-about-github/about-github-advanced-security +// AdvancedSecurity specifies the state of advanced security on a repository. +// +// GitHub API docs: https://docs.github.com/en/github/getting-started-with-github/learning-about-github/about-github-advanced-security type AdvancedSecurity struct { - Status *string `json:"status"` + Status *string `json:"status,omitempty"` } func (a AdvancedSecurity) String() string { return Stringify(a) } -// SecretScanning specifies the state of secret scanning on a repository -// https://docs.github.com/en/code-security/secret-security/about-secret-scanning +// SecretScanning specifies the state of secret scanning on a repository. +// +// GitHub API docs: https://docs.github.com/en/code-security/secret-security/about-secret-scanning type SecretScanning struct { - Status *string `json:"status"` + Status *string `json:"status,omitempty"` } func (s SecretScanning) String() string { From 6c33d8597907658ea78eeed49c78690715258720 Mon Sep 17 00:00:00 2001 From: Andrew Svoboda Date: Tue, 19 Oct 2021 19:25:15 -0400 Subject: [PATCH 3/3] Update test to validate Advanced Security marshaling --- github/repos_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github/repos_test.go b/github/repos_test.go index 4c799048abb..dee5fdf894b 100644 --- a/github/repos_test.go +++ b/github/repos_test.go @@ -360,7 +360,7 @@ func TestRepositoriesService_Get(t *testing.T) { mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) + fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"},"security_and_analysis":{"advanced_security":{"status":"enabled"},"secret_scanning":{"status":"enabled"}}}`) }) ctx := context.Background() @@ -369,7 +369,7 @@ func TestRepositoriesService_Get(t *testing.T) { t.Errorf("Repositories.Get returned error: %v", err) } - want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} + want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}, SecurityAndAnalysis: &SecurityAndAnalysis{AdvancedSecurity: &AdvancedSecurity{Status: String("enabled")}, SecretScanning: &SecretScanning{String("enabled")}}} if !cmp.Equal(got, want) { t.Errorf("Repositories.Get returned %+v, want %+v", got, want) }