From 0a04270376e80bee6fd957fdebc931c6aadbbab0 Mon Sep 17 00:00:00 2001 From: cpanato Date: Fri, 17 Nov 2023 17:46:41 +0100 Subject: [PATCH] implement global security advisories API Signed-off-by: cpanato --- github/github-accessors.go | 272 +++++++++++++++++++++ github/github-accessors_test.go | 328 +++++++++++++++++++++++++ github/security_advisories.go | 152 ++++++++++++ github/security_advisories_test.go | 373 +++++++++++++++++++++++++++++ 4 files changed, 1125 insertions(+) diff --git a/github/github-accessors.go b/github/github-accessors.go index 536ce5a8407..c24ed7132db 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 *Credits) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetUser returns the User field. +func (c *Credits) 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 { @@ -5062,6 +5078,38 @@ func (c *CustomRepoRoles) GetName() string { return *c.Name } +// GetScore returns the Score field. +func (c *CVSS) GetScore() *float64 { + if c == nil { + return nil + } + return c.Score +} + +// GetVectorString returns the VectorString field if it's non-nil, zero value otherwise. +func (c *CVSS) GetVectorString() string { + if c == nil || c.VectorString == nil { + return "" + } + return *c.VectorString +} + +// GetCWEID returns the CWEID field if it's non-nil, zero value otherwise. +func (c *CWEs) GetCWEID() string { + if c == nil || c.CWEID == nil { + return "" + } + return *c.CWEID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CWEs) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + // GetQuerySuite returns the QuerySuite field if it's non-nil, zero value otherwise. func (d *DefaultSetupConfiguration) GetQuerySuite() string { if d == nil || d.QuerySuite == nil { @@ -7798,6 +7846,174 @@ func (g *GitObject) GetURL() string { return *g.URL } +// GetCredits returns the Credits field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetCredits() []Credits { + if g == nil || g.Credits == nil { + return nil + } + return *g.Credits +} + +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetCVEID() string { + if g == nil || g.CVEID == nil { + return "" + } + return *g.CVEID +} + +// GetCVSS returns the CVSS field. +func (g *GlobalSecurityAdvisory) GetCVSS() *CVSS { + if g == nil { + return nil + } + return g.CVSS +} + +// GetCWEs returns the CWEs field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetCWEs() []CWEs { + if g == nil || g.CWEs == nil { + return nil + } + return *g.CWEs +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetDescription() string { + if g == nil || g.Description == nil { + return "" + } + return *g.Description +} + +// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetGHSAID() string { + if g == nil || g.GHSAID == nil { + return "" + } + return *g.GHSAID +} + +// GetGitHubReviewedAt returns the GitHubReviewedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetGitHubReviewedAt() time.Time { + if g == nil || g.GitHubReviewedAt == nil { + return time.Time{} + } + return *g.GitHubReviewedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetHTMLURL() string { + if g == nil || g.HTMLURL == nil { + return "" + } + return *g.HTMLURL +} + +// 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 +} + +// GetIdentifiers returns the Identifiers field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetIdentifiers() []Identifiers { + if g == nil || g.Identifiers == nil { + return nil + } + return *g.Identifiers +} + +// GetNVDPublishedAt returns the NVDPublishedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetNVDPublishedAt() time.Time { + if g == nil || g.NVDPublishedAt == nil { + return time.Time{} + } + return *g.NVDPublishedAt +} + +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetPublishedAt() time.Time { + if g == nil || g.PublishedAt == nil { + return time.Time{} + } + return *g.PublishedAt +} + +// 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 +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetSeverity() string { + if g == nil || g.Severity == nil { + return "" + } + return *g.Severity +} + +// 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 +} + +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetSummary() string { + if g == nil || g.Summary == nil { + return "" + } + return *g.Summary +} + +// 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 +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetUpdatedAt() time.Time { + if g == nil || g.UpdatedAt == nil { + return time.Time{} + } + return *g.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetVulnerabilities returns the Vulnerabilities field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetVulnerabilities() []Vulnerabilities { + if g == nil || g.Vulnerabilities == nil { + return nil + } + return *g.Vulnerabilities +} + +// GetWithdrawnAt returns the WithdrawnAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetWithdrawnAt() time.Time { + if g == nil || g.WithdrawnAt == nil { + return time.Time{} + } + return *g.WithdrawnAt +} + // GetInstallation returns the Installation field. func (g *GollumEvent) GetInstallation() *Installation { if g == nil { @@ -8310,6 +8526,22 @@ func (h *HookStats) GetTotalHooks() int { return *h.TotalHooks } +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (i *Identifiers) GetType() string { + if i == nil || i.Type == nil { + return "" + } + return *i.Type +} + +// GetValue returns the Value field if it's non-nil, zero value otherwise. +func (i *Identifiers) GetValue() string { + if i == nil || i.Value == nil { + return "" + } + return *i.Value +} + // GetGroupDescription returns the GroupDescription field if it's non-nil, zero value otherwise. func (i *IDPGroup) GetGroupDescription() string { if i == nil || i.GroupDescription == nil { @@ -24334,6 +24566,46 @@ func (u *UserSuspendOptions) GetReason() string { return *u.Reason } +// GetFirstPatchedVersion returns the FirstPatchedVersion field if it's non-nil, zero value otherwise. +func (v *Vulnerabilities) GetFirstPatchedVersion() string { + if v == nil || v.FirstPatchedVersion == nil { + return "" + } + return *v.FirstPatchedVersion +} + +// GetPackage returns the Package field. +func (v *Vulnerabilities) GetPackage() *VulnerabilitiesPackage { + if v == nil { + return nil + } + return v.Package +} + +// GetVulnerableVersionRange returns the VulnerableVersionRange field if it's non-nil, zero value otherwise. +func (v *Vulnerabilities) GetVulnerableVersionRange() string { + if v == nil || v.VulnerableVersionRange == nil { + return "" + } + return *v.VulnerableVersionRange +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (v *VulnerabilitiesPackage) GetEcosystem() string { + if v == nil || v.Ecosystem == nil { + return "" + } + return *v.Ecosystem +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (v *VulnerabilitiesPackage) GetName() string { + if v == nil || v.Name == nil { + return "" + } + return *v.Name +} + // GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. func (v *VulnerabilityPackage) GetEcosystem() string { if v == nil || v.Ecosystem == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index abe96ecf98f..86f63d01d91 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 TestCredits_GetType(tt *testing.T) { + var zeroValue string + c := &Credits{Type: &zeroValue} + c.GetType() + c = &Credits{} + c.GetType() + c = nil + c.GetType() +} + +func TestCredits_GetUser(tt *testing.T) { + c := &Credits{} + c.GetUser() + c = nil + c.GetUser() +} + func TestCustomProperty_GetDefaultValue(tt *testing.T) { var zeroValue string c := &CustomProperty{DefaultValue: &zeroValue} @@ -5989,6 +6006,43 @@ func TestCustomRepoRoles_GetName(tt *testing.T) { c.GetName() } +func TestCVSS_GetScore(tt *testing.T) { + c := &CVSS{} + c.GetScore() + c = nil + c.GetScore() +} + +func TestCVSS_GetVectorString(tt *testing.T) { + var zeroValue string + c := &CVSS{VectorString: &zeroValue} + c.GetVectorString() + c = &CVSS{} + c.GetVectorString() + c = nil + c.GetVectorString() +} + +func TestCWEs_GetCWEID(tt *testing.T) { + var zeroValue string + c := &CWEs{CWEID: &zeroValue} + c.GetCWEID() + c = &CWEs{} + c.GetCWEID() + c = nil + c.GetCWEID() +} + +func TestCWEs_GetName(tt *testing.T) { + var zeroValue string + c := &CWEs{Name: &zeroValue} + c.GetName() + c = &CWEs{} + c.GetName() + c = nil + c.GetName() +} + func TestDefaultSetupConfiguration_GetQuerySuite(tt *testing.T) { var zeroValue string d := &DefaultSetupConfiguration{QuerySuite: &zeroValue} @@ -9142,6 +9196,213 @@ func TestGitObject_GetURL(tt *testing.T) { g.GetURL() } +func TestGlobalSecurityAdvisory_GetCredits(tt *testing.T) { + var zeroValue []Credits + g := &GlobalSecurityAdvisory{Credits: &zeroValue} + g.GetCredits() + g = &GlobalSecurityAdvisory{} + g.GetCredits() + g = nil + g.GetCredits() +} + +func TestGlobalSecurityAdvisory_GetCVEID(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{CVEID: &zeroValue} + g.GetCVEID() + g = &GlobalSecurityAdvisory{} + g.GetCVEID() + g = nil + g.GetCVEID() +} + +func TestGlobalSecurityAdvisory_GetCVSS(tt *testing.T) { + g := &GlobalSecurityAdvisory{} + g.GetCVSS() + g = nil + g.GetCVSS() +} + +func TestGlobalSecurityAdvisory_GetCWEs(tt *testing.T) { + var zeroValue []CWEs + g := &GlobalSecurityAdvisory{CWEs: &zeroValue} + g.GetCWEs() + g = &GlobalSecurityAdvisory{} + g.GetCWEs() + g = nil + g.GetCWEs() +} + +func TestGlobalSecurityAdvisory_GetDescription(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{Description: &zeroValue} + g.GetDescription() + g = &GlobalSecurityAdvisory{} + g.GetDescription() + g = nil + g.GetDescription() +} + +func TestGlobalSecurityAdvisory_GetGHSAID(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{GHSAID: &zeroValue} + g.GetGHSAID() + g = &GlobalSecurityAdvisory{} + g.GetGHSAID() + g = nil + g.GetGHSAID() +} + +func TestGlobalSecurityAdvisory_GetGitHubReviewedAt(tt *testing.T) { + var zeroValue time.Time + g := &GlobalSecurityAdvisory{GitHubReviewedAt: &zeroValue} + g.GetGitHubReviewedAt() + g = &GlobalSecurityAdvisory{} + g.GetGitHubReviewedAt() + g = nil + g.GetGitHubReviewedAt() +} + +func TestGlobalSecurityAdvisory_GetHTMLURL(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{HTMLURL: &zeroValue} + g.GetHTMLURL() + g = &GlobalSecurityAdvisory{} + g.GetHTMLURL() + g = nil + g.GetHTMLURL() +} + +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_GetIdentifiers(tt *testing.T) { + var zeroValue []Identifiers + g := &GlobalSecurityAdvisory{Identifiers: &zeroValue} + g.GetIdentifiers() + g = &GlobalSecurityAdvisory{} + g.GetIdentifiers() + g = nil + g.GetIdentifiers() +} + +func TestGlobalSecurityAdvisory_GetNVDPublishedAt(tt *testing.T) { + var zeroValue time.Time + g := &GlobalSecurityAdvisory{NVDPublishedAt: &zeroValue} + g.GetNVDPublishedAt() + g = &GlobalSecurityAdvisory{} + g.GetNVDPublishedAt() + g = nil + g.GetNVDPublishedAt() +} + +func TestGlobalSecurityAdvisory_GetPublishedAt(tt *testing.T) { + var zeroValue time.Time + g := &GlobalSecurityAdvisory{PublishedAt: &zeroValue} + g.GetPublishedAt() + g = &GlobalSecurityAdvisory{} + g.GetPublishedAt() + g = nil + g.GetPublishedAt() +} + +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_GetSeverity(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{Severity: &zeroValue} + g.GetSeverity() + g = &GlobalSecurityAdvisory{} + g.GetSeverity() + g = nil + g.GetSeverity() +} + +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_GetSummary(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{Summary: &zeroValue} + g.GetSummary() + g = &GlobalSecurityAdvisory{} + g.GetSummary() + g = nil + g.GetSummary() +} + +func TestGlobalSecurityAdvisory_GetType(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{Type: &zeroValue} + g.GetType() + g = &GlobalSecurityAdvisory{} + g.GetType() + g = nil + g.GetType() +} + +func TestGlobalSecurityAdvisory_GetUpdatedAt(tt *testing.T) { + var zeroValue time.Time + g := &GlobalSecurityAdvisory{UpdatedAt: &zeroValue} + g.GetUpdatedAt() + g = &GlobalSecurityAdvisory{} + g.GetUpdatedAt() + g = nil + g.GetUpdatedAt() +} + +func TestGlobalSecurityAdvisory_GetURL(tt *testing.T) { + var zeroValue string + g := &GlobalSecurityAdvisory{URL: &zeroValue} + g.GetURL() + g = &GlobalSecurityAdvisory{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestGlobalSecurityAdvisory_GetVulnerabilities(tt *testing.T) { + var zeroValue []Vulnerabilities + g := &GlobalSecurityAdvisory{Vulnerabilities: &zeroValue} + g.GetVulnerabilities() + g = &GlobalSecurityAdvisory{} + g.GetVulnerabilities() + g = nil + g.GetVulnerabilities() +} + +func TestGlobalSecurityAdvisory_GetWithdrawnAt(tt *testing.T) { + var zeroValue time.Time + g := &GlobalSecurityAdvisory{WithdrawnAt: &zeroValue} + g.GetWithdrawnAt() + g = &GlobalSecurityAdvisory{} + g.GetWithdrawnAt() + g = nil + g.GetWithdrawnAt() +} + func TestGollumEvent_GetInstallation(tt *testing.T) { g := &GollumEvent{} g.GetInstallation() @@ -9752,6 +10013,26 @@ func TestHookStats_GetTotalHooks(tt *testing.T) { h.GetTotalHooks() } +func TestIdentifiers_GetType(tt *testing.T) { + var zeroValue string + i := &Identifiers{Type: &zeroValue} + i.GetType() + i = &Identifiers{} + i.GetType() + i = nil + i.GetType() +} + +func TestIdentifiers_GetValue(tt *testing.T) { + var zeroValue string + i := &Identifiers{Value: &zeroValue} + i.GetValue() + i = &Identifiers{} + i.GetValue() + i = nil + i.GetValue() +} + func TestIDPGroup_GetGroupDescription(tt *testing.T) { var zeroValue string i := &IDPGroup{GroupDescription: &zeroValue} @@ -28339,6 +28620,53 @@ func TestUserSuspendOptions_GetReason(tt *testing.T) { u.GetReason() } +func TestVulnerabilities_GetFirstPatchedVersion(tt *testing.T) { + var zeroValue string + v := &Vulnerabilities{FirstPatchedVersion: &zeroValue} + v.GetFirstPatchedVersion() + v = &Vulnerabilities{} + v.GetFirstPatchedVersion() + v = nil + v.GetFirstPatchedVersion() +} + +func TestVulnerabilities_GetPackage(tt *testing.T) { + v := &Vulnerabilities{} + v.GetPackage() + v = nil + v.GetPackage() +} + +func TestVulnerabilities_GetVulnerableVersionRange(tt *testing.T) { + var zeroValue string + v := &Vulnerabilities{VulnerableVersionRange: &zeroValue} + v.GetVulnerableVersionRange() + v = &Vulnerabilities{} + v.GetVulnerableVersionRange() + v = nil + v.GetVulnerableVersionRange() +} + +func TestVulnerabilitiesPackage_GetEcosystem(tt *testing.T) { + var zeroValue string + v := &VulnerabilitiesPackage{Ecosystem: &zeroValue} + v.GetEcosystem() + v = &VulnerabilitiesPackage{} + v.GetEcosystem() + v = nil + v.GetEcosystem() +} + +func TestVulnerabilitiesPackage_GetName(tt *testing.T) { + var zeroValue string + v := &VulnerabilitiesPackage{Name: &zeroValue} + v.GetName() + v = &VulnerabilitiesPackage{} + v.GetName() + v = nil + v.GetName() +} + func TestVulnerabilityPackage_GetEcosystem(tt *testing.T) { var zeroValue string v := &VulnerabilityPackage{Ecosystem: &zeroValue} diff --git a/github/security_advisories.go b/github/security_advisories.go index 66e7dba15ac..18f39181bd2 100644 --- a/github/security_advisories.go +++ b/github/security_advisories.go @@ -8,6 +8,7 @@ package github import ( "context" "fmt" + "time" ) type SecurityAdvisoriesService service @@ -47,6 +48,110 @@ 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"` +} + +type GlobalSecurityAdvisory struct { + ID *int64 `json:"id,omitempty"` + GHSAID *string `json:"ghsa_id,omitempty"` + CVEID *string `json:"cve_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + RepositoryAdvisoryURL *string `json:"repository_advisory_url,omitempty"` + Summary *string `json:"summary,omitempty"` + Description *string `json:"description,omitempty"` + Type *string `json:"type,omitempty"` + Severity *string `json:"severity,omitempty"` + SourceCodeLocation *string `json:"source_code_location,omitempty"` + Identifiers *[]Identifiers `json:"identifiers,omitempty"` + References []string `json:"references,omitempty"` + PublishedAt *time.Time `json:"published_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + GitHubReviewedAt *time.Time `json:"github_reviewed_at,omitempty"` + NVDPublishedAt *time.Time `json:"nvd_published_at,omitempty"` + WithdrawnAt *time.Time `json:"withdrawn_at,omitempty"` + Vulnerabilities *[]Vulnerabilities `json:"vulnerabilities,omitempty"` + CVSS *CVSS `json:"cvss,omitempty"` + CWEs *[]CWEs `json:"cwes,omitempty"` + Credits *[]Credits `json:"credits,omitempty"` +} + +type Identifiers struct { + Type *string `json:"type,omitempty"` + Value *string `json:"value,omitempty"` +} + +type Vulnerabilities struct { + Package *VulnerabilitiesPackage `json:"package,omitempty"` + FirstPatchedVersion *string `json:"first_patched_version,omitempty"` + VulnerableVersionRange *string `json:"vulnerable_version_range,omitempty"` + VulnerableFunctions []string `json:"vulnerable_functions,omitempty"` +} + +type VulnerabilitiesPackage struct { + Ecosystem *string `json:"ecosystem,omitempty"` + Name *string `json:"name,omitempty"` +} + +type CVSS struct { + VectorString *string `json:"vector_string,omitempty"` + Score *float64 `json:"score,omitempty"` +} + +type CWEs struct { + CWEID *string `json:"cwe_id,omitempty"` + Name *string `json:"name,omitempty"` +} + +type Credits struct { + User *User + 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 +229,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, opt *ListGlobalSecurityAdvisoriesOptions) ([]*GlobalSecurityAdvisory, *Response, error) { + url := "advisories" + 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 []*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 5476ef6138f..422de4a34a8 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,374 @@ func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories(t *testing.T return resp, err }) } + +func TestGlobalSecurityAdvisories(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: "CVE-xoxo-1234"} + + advisories, _, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, opts) + if err != nil { + t.Errorf("SecurityAdvisories.ListGlobalSecurityAdvisories returned error: %v", err) + } + + date := time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC) + want := []*GlobalSecurityAdvisory{ + { + ID: Int64(1), + 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"), + RepositoryAdvisoryURL: String("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + 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."), + Type: String("reviewed"), + Severity: String("high"), + SourceCodeLocation: String("https://github.com/project/a-package"), + Identifiers: &[]Identifiers{ + { + Type: String("GHSA"), + Value: String("GHSA-xoxo-1234-xoxo"), + }, + { + Type: String("CVE"), + Value: String("CVE-xoxo-1234"), + }, + }, + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + PublishedAt: &date, + UpdatedAt: &date, + GitHubReviewedAt: &date, + NVDPublishedAt: &date, + WithdrawnAt: nil, + Vulnerabilities: &[]Vulnerabilities{ + { + Package: &VulnerabilitiesPackage{ + Ecosystem: String("npm"), + Name: String("a-package"), + }, + FirstPatchedVersion: String("1.0.3"), + VulnerableVersionRange: String("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + CVSS: &CVSS{ + 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: &[]CWEs{ + { + CWEID: String("CWE-400"), + Name: String("Uncontrolled Resource Consumption"), + }, + }, + Credits: &[]Credits{ + { + 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 := time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC) + want := &GlobalSecurityAdvisory{ + ID: Int64(1), + 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"), + RepositoryAdvisoryURL: String("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + 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."), + Type: String("reviewed"), + Severity: String("high"), + SourceCodeLocation: String("https://github.com/project/a-package"), + Identifiers: &[]Identifiers{ + { + Type: String("GHSA"), + Value: String("GHSA-xoxo-1234-xoxo"), + }, + { + Type: String("CVE"), + Value: String("CVE-xoxo-1234"), + }, + }, + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + PublishedAt: &date, + UpdatedAt: &date, + GitHubReviewedAt: &date, + NVDPublishedAt: &date, + WithdrawnAt: nil, + Vulnerabilities: &[]Vulnerabilities{ + { + Package: &VulnerabilitiesPackage{ + Ecosystem: String("npm"), + Name: String("a-package"), + }, + FirstPatchedVersion: String("1.0.3"), + VulnerableVersionRange: String("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + CVSS: &CVSS{ + 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: &[]CWEs{ + { + CWEID: String("CWE-400"), + Name: String("Uncontrolled Resource Consumption"), + }, + }, + Credits: &[]Credits{ + { + 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, "\n") + 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 + }) +}