diff --git a/github/github-accessors.go b/github/github-accessors.go index 536ce5a840..7a23acd58a 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -4990,6 +4990,22 @@ func (c *CredentialAuthorization) GetTokenLastEight() string { return *c.TokenLastEight } +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *Credit) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetUser returns the User field. +func (c *Credit) GetUser() *User { + if c == nil { + return nil + } + return c.User +} + // GetDefaultValue returns the DefaultValue field if it's non-nil, zero value otherwise. func (c *CustomProperty) GetDefaultValue() string { if c == nil || c.DefaultValue == nil { @@ -7798,6 +7814,78 @@ func (g *GitObject) GetURL() string { return *g.URL } +// GetGithubReviewedAt returns the GithubReviewedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetGithubReviewedAt() Timestamp { + if g == nil || g.GithubReviewedAt == nil { + return Timestamp{} + } + return *g.GithubReviewedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetNVDPublishedAt returns the NVDPublishedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetNVDPublishedAt() Timestamp { + if g == nil || g.NVDPublishedAt == nil { + return Timestamp{} + } + return *g.NVDPublishedAt +} + +// GetRepositoryAdvisoryURL returns the RepositoryAdvisoryURL field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetRepositoryAdvisoryURL() string { + if g == nil || g.RepositoryAdvisoryURL == nil { + return "" + } + return *g.RepositoryAdvisoryURL +} + +// GetSourceCodeLocation returns the SourceCodeLocation field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetSourceCodeLocation() string { + if g == nil || g.SourceCodeLocation == nil { + return "" + } + return *g.SourceCodeLocation +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetFirstPatchedVersion returns the FirstPatchedVersion field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityVulnerability) GetFirstPatchedVersion() string { + if g == nil || g.FirstPatchedVersion == nil { + return "" + } + return *g.FirstPatchedVersion +} + +// GetPackage returns the Package field. +func (g *GlobalSecurityVulnerability) GetPackage() *VulnerabilityPackage { + if g == nil { + return nil + } + return g.Package +} + +// GetVulnerableVersionRange returns the VulnerableVersionRange field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityVulnerability) GetVulnerableVersionRange() string { + if g == nil || g.VulnerableVersionRange == nil { + return "" + } + return *g.VulnerableVersionRange +} + // GetInstallation returns the Installation field. func (g *GollumEvent) GetInstallation() *Installation { if g == nil { @@ -10766,6 +10854,86 @@ func (l *ListExternalGroupsOptions) GetDisplayName() string { return *l.DisplayName } +// GetAffects returns the Affects field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetAffects() string { + if l == nil || l.Affects == nil { + return "" + } + return *l.Affects +} + +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetCVEID() string { + if l == nil || l.CVEID == nil { + return "" + } + return *l.CVEID +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetEcosystem() string { + if l == nil || l.Ecosystem == nil { + return "" + } + return *l.Ecosystem +} + +// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetGHSAID() string { + if l == nil || l.GHSAID == nil { + return "" + } + return *l.GHSAID +} + +// GetIsWithdrawn returns the IsWithdrawn field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetIsWithdrawn() bool { + if l == nil || l.IsWithdrawn == nil { + return false + } + return *l.IsWithdrawn +} + +// GetModified returns the Modified field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetModified() string { + if l == nil || l.Modified == nil { + return "" + } + return *l.Modified +} + +// GetPublished returns the Published field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetPublished() string { + if l == nil || l.Published == nil { + return "" + } + return *l.Published +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetSeverity() string { + if l == nil || l.Severity == nil { + return "" + } + return *l.Severity +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetType() string { + if l == nil || l.Type == nil { + return "" + } + return *l.Type +} + +// GetUpdated returns the Updated field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetUpdated() string { + if l == nil || l.Updated == nil { + return "" + } + return *l.Updated +} + // GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. func (l *ListOrganizations) GetTotalCount() int { if l == nil || l.TotalCount == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index abe96ecf98..c5cad32ddb 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -5899,6 +5899,23 @@ func TestCredentialAuthorization_GetTokenLastEight(tt *testing.T) { c.GetTokenLastEight() } +func TestCredit_GetType(tt *testing.T) { + var zeroValue string + c := &Credit{Type: &zeroValue} + c.GetType() + c = &Credit{} + c.GetType() + c = nil + c.GetType() +} + +func TestCredit_GetUser(tt *testing.T) { + c := &Credit{} + c.GetUser() + c = nil + c.GetUser() +} + func TestCustomProperty_GetDefaultValue(tt *testing.T) { var zeroValue string c := &CustomProperty{DefaultValue: &zeroValue} @@ -9142,6 +9159,93 @@ func TestGitObject_GetURL(tt *testing.T) { g.GetURL() } +func TestGlobalSecurityAdvisory_GetGithubReviewedAt(tt *testing.T) { + var zeroValue Timestamp + g := &GlobalSecurityAdvisory{GithubReviewedAt: &zeroValue} + g.GetGithubReviewedAt() + g = &GlobalSecurityAdvisory{} + g.GetGithubReviewedAt() + g = nil + g.GetGithubReviewedAt() +} + +func TestGlobalSecurityAdvisory_GetID(tt *testing.T) { + var zeroValue int64 + g := &GlobalSecurityAdvisory{ID: &zeroValue} + g.GetID() + g = &GlobalSecurityAdvisory{} + g.GetID() + g = nil + g.GetID() +} + +func TestGlobalSecurityAdvisory_GetNVDPublishedAt(tt *testing.T) { + var zeroValue Timestamp + g := &GlobalSecurityAdvisory{NVDPublishedAt: &zeroValue} + g.GetNVDPublishedAt() + g = &GlobalSecurityAdvisory{} + g.GetNVDPublishedAt() + g = nil + g.GetNVDPublishedAt() +} + +func TestGlobalSecurityAdvisory_GetRepositoryAdvisoryURL(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{RepositoryAdvisoryURL: &zeroValue} + g.GetRepositoryAdvisoryURL() + g = &GlobalSecurityAdvisory{} + g.GetRepositoryAdvisoryURL() + g = nil + g.GetRepositoryAdvisoryURL() +} + +func TestGlobalSecurityAdvisory_GetSourceCodeLocation(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{SourceCodeLocation: &zeroValue} + g.GetSourceCodeLocation() + g = &GlobalSecurityAdvisory{} + g.GetSourceCodeLocation() + g = nil + g.GetSourceCodeLocation() +} + +func TestGlobalSecurityAdvisory_GetType(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{Type: &zeroValue} + g.GetType() + g = &GlobalSecurityAdvisory{} + g.GetType() + g = nil + g.GetType() +} + +func TestGlobalSecurityVulnerability_GetFirstPatchedVersion(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityVulnerability{FirstPatchedVersion: &zeroValue} + g.GetFirstPatchedVersion() + g = &GlobalSecurityVulnerability{} + g.GetFirstPatchedVersion() + g = nil + g.GetFirstPatchedVersion() +} + +func TestGlobalSecurityVulnerability_GetPackage(tt *testing.T) { + g := &GlobalSecurityVulnerability{} + g.GetPackage() + g = nil + g.GetPackage() +} + +func TestGlobalSecurityVulnerability_GetVulnerableVersionRange(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityVulnerability{VulnerableVersionRange: &zeroValue} + g.GetVulnerableVersionRange() + g = &GlobalSecurityVulnerability{} + g.GetVulnerableVersionRange() + g = nil + g.GetVulnerableVersionRange() +} + func TestGollumEvent_GetInstallation(tt *testing.T) { g := &GollumEvent{} g.GetInstallation() @@ -12612,6 +12716,106 @@ func TestListExternalGroupsOptions_GetDisplayName(tt *testing.T) { l.GetDisplayName() } +func TestListGlobalSecurityAdvisoriesOptions_GetAffects(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Affects: &zeroValue} + l.GetAffects() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetAffects() + l = nil + l.GetAffects() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetCVEID(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{CVEID: &zeroValue} + l.GetCVEID() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetCVEID() + l = nil + l.GetCVEID() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetEcosystem(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Ecosystem: &zeroValue} + l.GetEcosystem() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetEcosystem() + l = nil + l.GetEcosystem() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetGHSAID(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{GHSAID: &zeroValue} + l.GetGHSAID() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetGHSAID() + l = nil + l.GetGHSAID() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetIsWithdrawn(tt *testing.T) { + var zeroValue bool + l := &ListGlobalSecurityAdvisoriesOptions{IsWithdrawn: &zeroValue} + l.GetIsWithdrawn() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetIsWithdrawn() + l = nil + l.GetIsWithdrawn() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetModified(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Modified: &zeroValue} + l.GetModified() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetModified() + l = nil + l.GetModified() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetPublished(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Published: &zeroValue} + l.GetPublished() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetPublished() + l = nil + l.GetPublished() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetSeverity(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Severity: &zeroValue} + l.GetSeverity() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetSeverity() + l = nil + l.GetSeverity() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetType(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Type: &zeroValue} + l.GetType() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetType() + l = nil + l.GetType() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetUpdated(tt *testing.T) { + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Updated: &zeroValue} + l.GetUpdated() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetUpdated() + l = nil + l.GetUpdated() +} + func TestListOrganizations_GetTotalCount(tt *testing.T) { var zeroValue int l := &ListOrganizations{TotalCount: &zeroValue} diff --git a/github/security_advisories.go b/github/security_advisories.go index 66e7dba15a..635263748d 100644 --- a/github/security_advisories.go +++ b/github/security_advisories.go @@ -47,6 +47,81 @@ type ListRepositorySecurityAdvisoriesOptions struct { State string `url:"state,omitempty"` } +// ListGlobalSecurityAdvisoriesOptions specifies the optional parameters to list the global security advisories. +type ListGlobalSecurityAdvisoriesOptions struct { + ListCursorOptions + + // If specified, only advisories with this GHSA (GitHub Security Advisory) identifier will be returned. + GHSAID *string `url:"ghsa_id,omitempty"` + + // If specified, only advisories of this type will be returned. + // By default, a request with no other parameters defined will only return reviewed advisories that are not malware. + // Default: reviewed + // Can be one of: reviewed, malware, unreviewed + Type *string `url:"type,omitempty"` + + // If specified, only advisories with this CVE (Common Vulnerabilities and Exposures) identifier will be returned. + CVEID *string `url:"cve_id,omitempty"` + + // If specified, only advisories for these ecosystems will be returned. + // Can be one of: actions, composer, erlang, go, maven, npm, nuget, other, pip, pub, rubygems, rust + Ecosystem *string `url:"ecosystem,omitempty"` + + // If specified, only advisories with these severities will be returned. + // Can be one of: unknown, low, medium, high, critical + Severity *string `url:"severity,omitempty"` + + // If specified, only advisories with these Common Weakness Enumerations (CWEs) will be returned. + // Example: cwes=79,284,22 or cwes[]=79&cwes[]=284&cwes[]=22 + CWEs []string `url:"cwes,omitempty"` + + // Whether to only return advisories that have been withdrawn. + IsWithdrawn *bool `url:"is_withdrawn,omitempty"` + + // If specified, only return advisories that affect any of package or package@version. + // A maximum of 1000 packages can be specified. If the query parameter causes + // the URL to exceed the maximum URL length supported by your client, you must specify fewer packages. + // Example: affects=package1,package2@1.0.0,package3@^2.0.0 or affects[]=package1&affects[]=package2@1.0.0 + Affects *string `url:"affects,omitempty"` + + // If specified, only return advisories that were published on a date or date range. + Published *string `url:"published,omitempty"` + + // If specified, only return advisories that were updated on a date or date range. + Updated *string `url:"updated,omitempty"` + + // If specified, only show advisories that were updated or published on a date or date range. + Modified *string `url:"modified,omitempty"` +} + +// GlobalSecurityAdvisory represents the global security advisory object response. +type GlobalSecurityAdvisory struct { + SecurityAdvisory + ID *int64 `json:"id,omitempty"` + RepositoryAdvisoryURL *string `json:"repository_advisory_url,omitempty"` + Type *string `json:"type,omitempty"` + SourceCodeLocation *string `json:"source_code_location,omitempty"` + References []string `json:"references,omitempty"` + Vulnerabilities []*GlobalSecurityVulnerability `json:"vulnerabilities,omitempty"` + GithubReviewedAt *Timestamp `json:"github_reviewed_at,omitempty"` + NVDPublishedAt *Timestamp `json:"nvd_published_at,omitempty"` + Credits []*Credit `json:"credits,omitempty"` +} + +// GlobalSecurityVulnerability represents a vulnerability for a global security advisory. +type GlobalSecurityVulnerability struct { + Package *VulnerabilityPackage `json:"package,omitempty"` + FirstPatchedVersion *string `json:"first_patched_version,omitempty"` + VulnerableVersionRange *string `json:"vulnerable_version_range,omitempty"` + VulnerableFunctions []string `json:"vulnerable_functions,omitempty"` +} + +// Credit represents the credit object for a global security advisory. +type Credit struct { + User *User `json:"user,omitempty"` + Type *string `json:"type,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. // @@ -124,3 +199,50 @@ func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisories(ctx context return advisories, resp, nil } + +// ListGlobalSecurityAdvisories lists all global security advisories. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/global-advisories#list-global-security-advisories +// +//meta:operation GET /advisories +func (s *SecurityAdvisoriesService) ListGlobalSecurityAdvisories(ctx context.Context, opts *ListGlobalSecurityAdvisoriesOptions) ([]*GlobalSecurityAdvisory, *Response, error) { + url := "advisories" + url, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*GlobalSecurityAdvisory + resp, err := s.client.Do(ctx, req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} + +// GetGlobalSecurityAdvisories gets a global security advisory using its GitHub Security Advisory (GHSA) identifier. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/global-advisories#get-a-global-security-advisory +// +//meta:operation GET /advisories/{ghsa_id} +func (s *SecurityAdvisoriesService) GetGlobalSecurityAdvisories(ctx context.Context, ghsaID string) (*GlobalSecurityAdvisory, *Response, error) { + url := fmt.Sprintf("advisories/%s", ghsaID) + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisory *GlobalSecurityAdvisory + resp, err := s.client.Do(ctx, req, &advisory) + if err != nil { + return nil, resp, err + } + + return advisory, resp, nil +} diff --git a/github/security_advisories_test.go b/github/security_advisories_test.go index 5476ef6138..918e3e0500 100644 --- a/github/security_advisories_test.go +++ b/github/security_advisories_test.go @@ -7,9 +7,11 @@ package github import ( "context" + "fmt" "net/http" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" ) @@ -315,3 +317,379 @@ func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories(t *testing.T return resp, err }) } + +func TestListGlobalSecurityAdvisories(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"cve_id": "CVE-xoxo-1234"}) + + fmt.Fprint(w, `[{ + "id": 1, + "ghsa_id": "GHSA-xoxo-1234-xoxo", + "cve_id": "CVE-xoxo-1234", + "url": "https://api.github.com/advisories/GHSA-xoxo-1234-xoxo", + "html_url": "https://github.com/advisories/GHSA-xoxo-1234-xoxo", + "repository_advisory_url": "https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo", + "summary": "Heartbleed security advisory", + "description": "This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information.", + "type": "reviewed", + "severity": "high", + "source_code_location": "https://github.com/project/a-package", + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-xoxo-1234-xoxo" + }, + { + "type": "CVE", + "value": "CVE-xoxo-1234" + } + ], + "references": ["https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"], + "published_at": "1996-06-20T00:00:00Z", + "updated_at": "1996-06-20T00:00:00Z", + "github_reviewed_at": "1996-06-20T00:00:00Z", + "nvd_published_at": "1996-06-20T00:00:00Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "a-package" + }, + "first_patched_version": "1.0.3", + "vulnerable_version_range": "<=1.0.2", + "vulnerable_functions": ["a_function"] + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", + "score": 7.6 + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + } + ], + "credits": [ + { + "user": { + "login": "user", + "id": 1, + "node_id": "12=", + "avatar_url": "a", + "gravatar_id": "", + "url": "a", + "html_url": "b", + "followers_url": "b", + "following_url": "c", + "gists_url": "d", + "starred_url": "e", + "subscriptions_url": "f", + "organizations_url": "g", + "repos_url": "h", + "events_url": "i", + "received_events_url": "j", + "type": "User", + "site_admin": false + }, + "type": "analyst" + } + ] + } + ]`) + }) + + ctx := context.Background() + opts := &ListGlobalSecurityAdvisoriesOptions{CVEID: String("CVE-xoxo-1234")} + + advisories, _, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, opts) + if err != nil { + t.Errorf("SecurityAdvisories.ListGlobalSecurityAdvisories returned error: %v", err) + } + + date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)} + want := []*GlobalSecurityAdvisory{ + { + ID: Int64(1), + SecurityAdvisory: SecurityAdvisory{ + GHSAID: String("GHSA-xoxo-1234-xoxo"), + CVEID: String("CVE-xoxo-1234"), + URL: String("https://api.github.com/advisories/GHSA-xoxo-1234-xoxo"), + HTMLURL: String("https://github.com/advisories/GHSA-xoxo-1234-xoxo"), + Severity: String("high"), + Summary: String("Heartbleed security advisory"), + Description: String("This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information."), + Identifiers: []*AdvisoryIdentifier{ + { + Type: String("GHSA"), + Value: String("GHSA-xoxo-1234-xoxo"), + }, + { + Type: String("CVE"), + Value: String("CVE-xoxo-1234"), + }, + }, + PublishedAt: &date, + UpdatedAt: &date, + WithdrawnAt: nil, + CVSS: &AdvisoryCVSS{ + VectorString: String("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Float64(7.6), + }, + CWEs: []*AdvisoryCWEs{ + { + CWEID: String("CWE-400"), + Name: String("Uncontrolled Resource Consumption"), + }, + }, + }, + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + Vulnerabilities: []*GlobalSecurityVulnerability{ + { + Package: &VulnerabilityPackage{ + Ecosystem: String("npm"), + Name: String("a-package"), + }, + FirstPatchedVersion: String("1.0.3"), + VulnerableVersionRange: String("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + RepositoryAdvisoryURL: String("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + Type: String("reviewed"), + SourceCodeLocation: String("https://github.com/project/a-package"), + GithubReviewedAt: &date, + NVDPublishedAt: &date, + Credits: []*Credit{ + { + User: &User{ + Login: String("user"), + ID: Int64(1), + NodeID: String("12="), + AvatarURL: String("a"), + GravatarID: String(""), + URL: String("a"), + HTMLURL: String("b"), + FollowersURL: String("b"), + FollowingURL: String("c"), + GistsURL: String("d"), + StarredURL: String("e"), + SubscriptionsURL: String("f"), + OrganizationsURL: String("g"), + ReposURL: String("h"), + EventsURL: String("i"), + ReceivedEventsURL: String("j"), + Type: String("User"), + SiteAdmin: Bool(false), + }, + Type: String("analyst"), + }, + }, + }, + } + + if !cmp.Equal(advisories, want) { + t.Errorf("SecurityAdvisories.ListGlobalSecurityAdvisories %+v, want %+v", advisories, want) + } + + const methodName = "ListGlobalSecurityAdvisories" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, nil) + return resp, err + }) +} + +func TestGetGlobalSecurityAdvisories(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/advisories/GHSA-xoxo-1234-xoxo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "id": 1, + "ghsa_id": "GHSA-xoxo-1234-xoxo", + "cve_id": "CVE-xoxo-1234", + "url": "https://api.github.com/advisories/GHSA-xoxo-1234-xoxo", + "html_url": "https://github.com/advisories/GHSA-xoxo-1234-xoxo", + "repository_advisory_url": "https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo", + "summary": "Heartbleed security advisory", + "description": "This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information.", + "type": "reviewed", + "severity": "high", + "source_code_location": "https://github.com/project/a-package", + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-xoxo-1234-xoxo" + }, + { + "type": "CVE", + "value": "CVE-xoxo-1234" + } + ], + "references": ["https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"], + "published_at": "1996-06-20T00:00:00Z", + "updated_at": "1996-06-20T00:00:00Z", + "github_reviewed_at": "1996-06-20T00:00:00Z", + "nvd_published_at": "1996-06-20T00:00:00Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "a-package" + }, + "first_patched_version": "1.0.3", + "vulnerable_version_range": "<=1.0.2", + "vulnerable_functions": ["a_function"] + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", + "score": 7.6 + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + } + ], + "credits": [ + { + "user": { + "login": "user", + "id": 1, + "node_id": "12=", + "avatar_url": "a", + "gravatar_id": "", + "url": "a", + "html_url": "b", + "followers_url": "b", + "following_url": "c", + "gists_url": "d", + "starred_url": "e", + "subscriptions_url": "f", + "organizations_url": "g", + "repos_url": "h", + "events_url": "i", + "received_events_url": "j", + "type": "User", + "site_admin": false + }, + "type": "analyst" + } + ] + }`) + }) + + ctx := context.Background() + advisory, _, err := client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "GHSA-xoxo-1234-xoxo") + if err != nil { + t.Errorf("SecurityAdvisories.GetGlobalSecurityAdvisories returned error: %v", err) + } + + date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)} + want := &GlobalSecurityAdvisory{ + ID: Int64(1), + SecurityAdvisory: SecurityAdvisory{ + GHSAID: String("GHSA-xoxo-1234-xoxo"), + CVEID: String("CVE-xoxo-1234"), + URL: String("https://api.github.com/advisories/GHSA-xoxo-1234-xoxo"), + HTMLURL: String("https://github.com/advisories/GHSA-xoxo-1234-xoxo"), + Severity: String("high"), + Summary: String("Heartbleed security advisory"), + Description: String("This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information."), + Identifiers: []*AdvisoryIdentifier{ + { + Type: String("GHSA"), + Value: String("GHSA-xoxo-1234-xoxo"), + }, + { + Type: String("CVE"), + Value: String("CVE-xoxo-1234"), + }, + }, + PublishedAt: &date, + UpdatedAt: &date, + WithdrawnAt: nil, + CVSS: &AdvisoryCVSS{ + VectorString: String("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Float64(7.6), + }, + CWEs: []*AdvisoryCWEs{ + { + CWEID: String("CWE-400"), + Name: String("Uncontrolled Resource Consumption"), + }, + }, + }, + RepositoryAdvisoryURL: String("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + Type: String("reviewed"), + SourceCodeLocation: String("https://github.com/project/a-package"), + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + GithubReviewedAt: &date, + NVDPublishedAt: &date, + + Vulnerabilities: []*GlobalSecurityVulnerability{ + { + Package: &VulnerabilityPackage{ + Ecosystem: String("npm"), + Name: String("a-package"), + }, + FirstPatchedVersion: String("1.0.3"), + VulnerableVersionRange: String("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + Credits: []*Credit{ + { + User: &User{ + Login: String("user"), + ID: Int64(1), + NodeID: String("12="), + AvatarURL: String("a"), + GravatarID: String(""), + URL: String("a"), + HTMLURL: String("b"), + FollowersURL: String("b"), + FollowingURL: String("c"), + GistsURL: String("d"), + StarredURL: String("e"), + SubscriptionsURL: String("f"), + OrganizationsURL: String("g"), + ReposURL: String("h"), + EventsURL: String("i"), + ReceivedEventsURL: String("j"), + Type: String("User"), + SiteAdmin: Bool(false), + }, + Type: String("analyst"), + }, + }, + } + + if !cmp.Equal(advisory, want) { + t.Errorf("SecurityAdvisories.GetGlobalSecurityAdvisories %+v, want %+v", advisory, want) + } + + const methodName = "GetGlobalSecurityAdvisories" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "CVE-\n-1234") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +}