diff --git a/github/event_types.go b/github/event_types.go index 3161384f15..cad123b251 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -1616,18 +1616,33 @@ type WorkflowRunEvent struct { // // GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#security_advisory type SecurityAdvisory struct { - CVSS *AdvisoryCVSS `json:"cvss,omitempty"` - CWEs []*AdvisoryCWEs `json:"cwes,omitempty"` - GHSAID *string `json:"ghsa_id,omitempty"` - Summary *string `json:"summary,omitempty"` - Description *string `json:"description,omitempty"` - Severity *string `json:"severity,omitempty"` - Identifiers []*AdvisoryIdentifier `json:"identifiers,omitempty"` - References []*AdvisoryReference `json:"references,omitempty"` - PublishedAt *Timestamp `json:"published_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - WithdrawnAt *Timestamp `json:"withdrawn_at,omitempty"` - Vulnerabilities []*AdvisoryVulnerability `json:"vulnerabilities,omitempty"` + CVSS *AdvisoryCVSS `json:"cvss,omitempty"` + CWEs []*AdvisoryCWEs `json:"cwes,omitempty"` + GHSAID *string `json:"ghsa_id,omitempty"` + Summary *string `json:"summary,omitempty"` + Description *string `json:"description,omitempty"` + Severity *string `json:"severity,omitempty"` + Identifiers []*AdvisoryIdentifier `json:"identifiers,omitempty"` + References []*AdvisoryReference `json:"references,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + WithdrawnAt *Timestamp `json:"withdrawn_at,omitempty"` + Vulnerabilities []*AdvisoryVulnerability `json:"vulnerabilities,omitempty"` + CVEID *string `json:"cve_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Author *User `json:"author,omitempty"` + Publisher *User `json:"publisher,omitempty"` + State *string `json:"state,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + Submission *SecurityAdvisorySubmission `json:"submission,omitempty"` + CWEIDs []string `json:"cwe_ids,omitempty"` + Credits []*RepoAdvisoryCredit `json:"credits,omitempty"` + CreditsDetailed []*RepoAdvisoryCreditDetailed `json:"credits_detailed,omitempty"` + CollaboratingUsers []*User `json:"collaborating_users,omitempty"` + CollaboratingTeams []*Team `json:"collaborating_teams,omitempty"` + PrivateFork *Repository `json:"private_fork,omitempty"` } // AdvisoryIdentifier represents the identifier for a Security Advisory. @@ -1647,6 +1662,12 @@ type AdvisoryVulnerability struct { Severity *string `json:"severity,omitempty"` VulnerableVersionRange *string `json:"vulnerable_version_range,omitempty"` FirstPatchedVersion *FirstPatchedVersion `json:"first_patched_version,omitempty"` + + // PatchedVersions and VulnerableFunctions are used in the following APIs: + // - https://docs.github.com/en/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories-for-an-organization + // - https://docs.github.com/en/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories + PatchedVersions *string `json:"patched_versions,omitempty"` + VulnerableFunctions []string `json:"vulnerable_functions,omitempty"` } // VulnerabilityPackage represents the package object for an Advisory Vulnerability. diff --git a/github/github-accessors.go b/github/github-accessors.go index 7317081668..36618adc36 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -438,6 +438,14 @@ func (a *AdvisoryVulnerability) GetPackage() *VulnerabilityPackage { return a.Package } +// GetPatchedVersions returns the PatchedVersions field if it's non-nil, zero value otherwise. +func (a *AdvisoryVulnerability) GetPatchedVersions() string { + if a == nil || a.PatchedVersions == nil { + return "" + } + return *a.PatchedVersions +} + // GetSeverity returns the Severity field if it's non-nil, zero value otherwise. func (a *AdvisoryVulnerability) GetSeverity() string { if a == nil || a.Severity == nil { @@ -17758,6 +17766,46 @@ func (r *RenameOrgResponse) GetURL() string { return *r.URL } +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCredit) GetLogin() string { + if r == nil || r.Login == nil { + return "" + } + return *r.Login +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCredit) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCreditDetailed) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCreditDetailed) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetUser returns the User field. +func (r *RepoAdvisoryCreditDetailed) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + // GetDownloadLocation returns the DownloadLocation field if it's non-nil, zero value otherwise. func (r *RepoDependencies) GetDownloadLocation() string { if r == nil || r.DownloadLocation == nil { @@ -21078,6 +21126,38 @@ func (s *SecretScanningPushProtection) GetStatus() string { return *s.Status } +// GetAuthor returns the Author field. +func (s *SecurityAdvisory) GetAuthor() *User { + if s == nil { + return nil + } + return s.Author +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetClosedAt() Timestamp { + if s == nil || s.ClosedAt == nil { + return Timestamp{} + } + return *s.ClosedAt +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetCVEID() string { + if s == nil || s.CVEID == nil { + return "" + } + return *s.CVEID +} + // GetCVSS returns the CVSS field. func (s *SecurityAdvisory) GetCVSS() *AdvisoryCVSS { if s == nil { @@ -21102,6 +21182,22 @@ func (s *SecurityAdvisory) GetGHSAID() string { return *s.GHSAID } +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetHTMLURL() string { + if s == nil || s.HTMLURL == nil { + return "" + } + return *s.HTMLURL +} + +// GetPrivateFork returns the PrivateFork field. +func (s *SecurityAdvisory) GetPrivateFork() *Repository { + if s == nil { + return nil + } + return s.PrivateFork +} + // GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. func (s *SecurityAdvisory) GetPublishedAt() Timestamp { if s == nil || s.PublishedAt == nil { @@ -21110,6 +21206,14 @@ func (s *SecurityAdvisory) GetPublishedAt() Timestamp { return *s.PublishedAt } +// GetPublisher returns the Publisher field. +func (s *SecurityAdvisory) GetPublisher() *User { + if s == nil { + return nil + } + return s.Publisher +} + // GetSeverity returns the Severity field if it's non-nil, zero value otherwise. func (s *SecurityAdvisory) GetSeverity() string { if s == nil || s.Severity == nil { @@ -21118,6 +21222,22 @@ func (s *SecurityAdvisory) GetSeverity() string { return *s.Severity } +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetState() string { + if s == nil || s.State == nil { + return "" + } + return *s.State +} + +// GetSubmission returns the Submission field. +func (s *SecurityAdvisory) GetSubmission() *SecurityAdvisorySubmission { + if s == nil { + return nil + } + return s.Submission +} + // GetSummary returns the Summary field if it's non-nil, zero value otherwise. func (s *SecurityAdvisory) GetSummary() string { if s == nil || s.Summary == nil { @@ -21134,6 +21254,14 @@ func (s *SecurityAdvisory) GetUpdatedAt() Timestamp { return *s.UpdatedAt } +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + // GetWithdrawnAt returns the WithdrawnAt field if it's non-nil, zero value otherwise. func (s *SecurityAdvisory) GetWithdrawnAt() Timestamp { if s == nil || s.WithdrawnAt == nil { @@ -21198,6 +21326,14 @@ func (s *SecurityAdvisoryEvent) GetSender() *User { return s.Sender } +// GetAccepted returns the Accepted field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisorySubmission) GetAccepted() bool { + if s == nil || s.Accepted == nil { + return false + } + return *s.Accepted +} + // GetAdvancedSecurity returns the AdvancedSecurity field. func (s *SecurityAndAnalysis) GetAdvancedSecurity() *AdvancedSecurity { if s == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index bdb4209a27..48ca4137b9 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -503,6 +503,16 @@ func TestAdvisoryVulnerability_GetPackage(tt *testing.T) { a.GetPackage() } +func TestAdvisoryVulnerability_GetPatchedVersions(tt *testing.T) { + var zeroValue string + a := &AdvisoryVulnerability{PatchedVersions: &zeroValue} + a.GetPatchedVersions() + a = &AdvisoryVulnerability{} + a.GetPatchedVersions() + a = nil + a.GetPatchedVersions() +} + func TestAdvisoryVulnerability_GetSeverity(tt *testing.T) { var zeroValue string a := &AdvisoryVulnerability{Severity: &zeroValue} @@ -20611,6 +20621,53 @@ func TestRenameOrgResponse_GetURL(tt *testing.T) { r.GetURL() } +func TestRepoAdvisoryCredit_GetLogin(tt *testing.T) { + var zeroValue string + r := &RepoAdvisoryCredit{Login: &zeroValue} + r.GetLogin() + r = &RepoAdvisoryCredit{} + r.GetLogin() + r = nil + r.GetLogin() +} + +func TestRepoAdvisoryCredit_GetType(tt *testing.T) { + var zeroValue string + r := &RepoAdvisoryCredit{Type: &zeroValue} + r.GetType() + r = &RepoAdvisoryCredit{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepoAdvisoryCreditDetailed_GetState(tt *testing.T) { + var zeroValue string + r := &RepoAdvisoryCreditDetailed{State: &zeroValue} + r.GetState() + r = &RepoAdvisoryCreditDetailed{} + r.GetState() + r = nil + r.GetState() +} + +func TestRepoAdvisoryCreditDetailed_GetType(tt *testing.T) { + var zeroValue string + r := &RepoAdvisoryCreditDetailed{Type: &zeroValue} + r.GetType() + r = &RepoAdvisoryCreditDetailed{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepoAdvisoryCreditDetailed_GetUser(tt *testing.T) { + r := &RepoAdvisoryCreditDetailed{} + r.GetUser() + r = nil + r.GetUser() +} + func TestRepoDependencies_GetDownloadLocation(tt *testing.T) { var zeroValue string r := &RepoDependencies{DownloadLocation: &zeroValue} @@ -24572,6 +24629,43 @@ func TestSecretScanningPushProtection_GetStatus(tt *testing.T) { s.GetStatus() } +func TestSecurityAdvisory_GetAuthor(tt *testing.T) { + s := &SecurityAdvisory{} + s.GetAuthor() + s = nil + s.GetAuthor() +} + +func TestSecurityAdvisory_GetClosedAt(tt *testing.T) { + var zeroValue Timestamp + s := &SecurityAdvisory{ClosedAt: &zeroValue} + s.GetClosedAt() + s = &SecurityAdvisory{} + s.GetClosedAt() + s = nil + s.GetClosedAt() +} + +func TestSecurityAdvisory_GetCreatedAt(tt *testing.T) { + var zeroValue Timestamp + s := &SecurityAdvisory{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SecurityAdvisory{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecurityAdvisory_GetCVEID(tt *testing.T) { + var zeroValue string + s := &SecurityAdvisory{CVEID: &zeroValue} + s.GetCVEID() + s = &SecurityAdvisory{} + s.GetCVEID() + s = nil + s.GetCVEID() +} + func TestSecurityAdvisory_GetCVSS(tt *testing.T) { s := &SecurityAdvisory{} s.GetCVSS() @@ -24599,6 +24693,23 @@ func TestSecurityAdvisory_GetGHSAID(tt *testing.T) { s.GetGHSAID() } +func TestSecurityAdvisory_GetHTMLURL(tt *testing.T) { + var zeroValue string + s := &SecurityAdvisory{HTMLURL: &zeroValue} + s.GetHTMLURL() + s = &SecurityAdvisory{} + s.GetHTMLURL() + s = nil + s.GetHTMLURL() +} + +func TestSecurityAdvisory_GetPrivateFork(tt *testing.T) { + s := &SecurityAdvisory{} + s.GetPrivateFork() + s = nil + s.GetPrivateFork() +} + func TestSecurityAdvisory_GetPublishedAt(tt *testing.T) { var zeroValue Timestamp s := &SecurityAdvisory{PublishedAt: &zeroValue} @@ -24609,6 +24720,13 @@ func TestSecurityAdvisory_GetPublishedAt(tt *testing.T) { s.GetPublishedAt() } +func TestSecurityAdvisory_GetPublisher(tt *testing.T) { + s := &SecurityAdvisory{} + s.GetPublisher() + s = nil + s.GetPublisher() +} + func TestSecurityAdvisory_GetSeverity(tt *testing.T) { var zeroValue string s := &SecurityAdvisory{Severity: &zeroValue} @@ -24619,6 +24737,23 @@ func TestSecurityAdvisory_GetSeverity(tt *testing.T) { s.GetSeverity() } +func TestSecurityAdvisory_GetState(tt *testing.T) { + var zeroValue string + s := &SecurityAdvisory{State: &zeroValue} + s.GetState() + s = &SecurityAdvisory{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecurityAdvisory_GetSubmission(tt *testing.T) { + s := &SecurityAdvisory{} + s.GetSubmission() + s = nil + s.GetSubmission() +} + func TestSecurityAdvisory_GetSummary(tt *testing.T) { var zeroValue string s := &SecurityAdvisory{Summary: &zeroValue} @@ -24639,6 +24774,16 @@ func TestSecurityAdvisory_GetUpdatedAt(tt *testing.T) { s.GetUpdatedAt() } +func TestSecurityAdvisory_GetURL(tt *testing.T) { + var zeroValue string + s := &SecurityAdvisory{URL: &zeroValue} + s.GetURL() + s = &SecurityAdvisory{} + s.GetURL() + s = nil + s.GetURL() +} + func TestSecurityAdvisory_GetWithdrawnAt(tt *testing.T) { var zeroValue Timestamp s := &SecurityAdvisory{WithdrawnAt: &zeroValue} @@ -24701,6 +24846,16 @@ func TestSecurityAdvisoryEvent_GetSender(tt *testing.T) { s.GetSender() } +func TestSecurityAdvisorySubmission_GetAccepted(tt *testing.T) { + var zeroValue bool + s := &SecurityAdvisorySubmission{Accepted: &zeroValue} + s.GetAccepted() + s = &SecurityAdvisorySubmission{} + s.GetAccepted() + s = nil + s.GetAccepted() +} + func TestSecurityAndAnalysis_GetAdvancedSecurity(tt *testing.T) { s := &SecurityAndAnalysis{} s.GetAdvancedSecurity() diff --git a/github/security_advisories.go b/github/security_advisories.go index a75fce54d9..681d0cd4bd 100644 --- a/github/security_advisories.go +++ b/github/security_advisories.go @@ -12,6 +12,41 @@ import ( type SecurityAdvisoriesService service +// SecurityAdvisorySubmission represents the Security Advisory Submission. +type SecurityAdvisorySubmission struct { + // Accepted represents whether a private vulnerability report was accepted by the repository's administrators. + Accepted *bool `json:"accepted,omitempty"` +} + +// RepoAdvisoryCredit represents the credit object for a repository Security Advisory. +type RepoAdvisoryCredit struct { + Login *string `json:"login,omitempty"` + Type *string `json:"type,omitempty"` +} + +// RepoAdvisoryCreditDetailed represents a credit given to a user for a repository Security Advisory. +type RepoAdvisoryCreditDetailed struct { + User *User `json:"user,omitempty"` + Type *string `json:"type,omitempty"` + State *string `json:"state,omitempty"` +} + +// ListRepositorySecurityAdvisoriesOptions specifies the optional parameters to list the repository security advisories. +type ListRepositorySecurityAdvisoriesOptions struct { + ListCursorOptions + + // Direction in which to sort advisories. Possible values are: asc, desc. + // Default is "asc". + Direction string `url:"direction,omitempty"` + + // Sort specifies how to sort advisories. Possible values are: created, updated, + // and published. Default value is "created". + Sort string `url:"sort,omitempty"` + + // State filters advisories based on their state. Possible values are: triage, draft, published, closed. + State string `url:"state,omitempty"` +} + // RequestCVE requests a Common Vulnerabilities and Exposures (CVE) for a repository security advisory. // The ghsaID is the GitHub Security Advisory identifier of the advisory. // @@ -35,3 +70,51 @@ func (s *SecurityAdvisoriesService) RequestCVE(ctx context.Context, owner, repo, return resp, nil } + +// ListRepositorySecurityAdvisoriesForOrg lists the repository security advisories for an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories-for-an-organization +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisoriesForOrg(ctx context.Context, org string, opt *ListRepositorySecurityAdvisoriesOptions) ([]*SecurityAdvisory, *Response, error) { + url := fmt.Sprintf("orgs/%v/security-advisories", org) + url, err := addOptions(url, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*SecurityAdvisory + resp, err := s.client.Do(ctx, req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} + +// ListRepositorySecurityAdvisories lists the security advisories in a repository. +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisories(ctx context.Context, owner, repo string, opt *ListRepositorySecurityAdvisoriesOptions) ([]*SecurityAdvisory, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/security-advisories", owner, repo) + url, err := addOptions(url, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*SecurityAdvisory + resp, err := s.client.Do(ctx, req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} diff --git a/github/security_advisories_test.go b/github/security_advisories_test.go index e4a6fbd7c1..5476ef6138 100644 --- a/github/security_advisories_test.go +++ b/github/security_advisories_test.go @@ -8,7 +8,10 @@ package github import ( "context" "net/http" + "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestSecurityAdvisoriesService_RequestCVE(t *testing.T) { @@ -50,3 +53,265 @@ func TestSecurityAdvisoriesService_RequestCVE(t *testing.T) { return resp, err }) } + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg_BadRequest(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + http.Error(w, "Bad Request", 400) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg_NotFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + query := r.URL.Query() + if query.Get("state") != "draft" { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned %+v, want %+v", query.Get("state"), "draft") + } + + http.NotFound(w, r) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", &ListRepositorySecurityAdvisoriesOptions{ + State: "draft", + }) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg_UnmarshalError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[{"ghsa_id": 12334354}]`)) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if err == nil { + t.Errorf("Expected unmarshal error") + } else if !strings.Contains(err.Error(), "json: cannot unmarshal number into Go struct field SecurityAdvisory.ghsa_id of type string") { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned unexpected error: %v", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[ + { + "ghsa_id": "GHSA-abcd-1234-efgh", + "cve_id": "CVE-2050-00000" + } + ]`)) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if err != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned error: %v, want nil", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %d, want %d", got, want) + } + + want := []*SecurityAdvisory{ + { + GHSAID: String("GHSA-abcd-1234-efgh"), + CVEID: String("CVE-2050-00000"), + }, + } + if !cmp.Equal(advisories, want) { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned %+v, want %+v", advisories, want) + } + + methodName := "ListRepositorySecurityAdvisoriesForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "\n", &ListRepositorySecurityAdvisoriesOptions{ + Sort: "\n", + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories_BadRequest(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + http.Error(w, "Bad Request", 400) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisories return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories_NotFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + query := r.URL.Query() + if query.Get("state") != "draft" { + t.Errorf("ListRepositorySecurityAdvisories returned %+v, want %+v", query.Get("state"), "draft") + } + + http.NotFound(w, r) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", &ListRepositorySecurityAdvisoriesOptions{ + State: "draft", + }) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisories return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories_UnmarshalError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[{"ghsa_id": 12334354}]`)) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if err == nil { + t.Errorf("Expected unmarshal error") + } else if !strings.Contains(err.Error(), "json: cannot unmarshal number into Go struct field SecurityAdvisory.ghsa_id of type string") { + t.Errorf("ListRepositorySecurityAdvisories returned unexpected error: %v", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %d, want %d", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisories return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[ + { + "ghsa_id": "GHSA-abcd-1234-efgh", + "cve_id": "CVE-2050-00000" + } + ]`)) + }) + + ctx := context.Background() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if err != nil { + t.Errorf("ListRepositorySecurityAdvisories returned error: %v, want nil", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %d, want %d", got, want) + } + + want := []*SecurityAdvisory{ + { + GHSAID: String("GHSA-abcd-1234-efgh"), + CVEID: String("CVE-2050-00000"), + }, + } + if !cmp.Equal(advisories, want) { + t.Errorf("ListRepositorySecurityAdvisories returned %+v, want %+v", advisories, want) + } + + methodName := "ListRepositorySecurityAdvisories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "\n", "\n", &ListRepositorySecurityAdvisoriesOptions{ + Sort: "\n", + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +}