Add support for fetching SBOMs (#2869)

Fixes: #2864.
This commit is contained in:
vandan rohatgi authored and GitHub committed 2023-08-16 13:27:49 -04:00
1 parent 1a4b1067cc
commit 0c562f6a6c
6 files changed
+435

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
// Copyright 2023 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
type DependencyGraphService service
// SBOM represents a software bill of materials, which describes the
// packages/libraries that a repository depends on.
type SBOM struct {
SBOM *SBOMInfo `json:"sbom,omitempty"`
}
// CreationInfo represents when the SBOM was created and who created it.
type CreationInfo struct {
Created *Timestamp `json:"created,omitempty"`
Creators []string `json:"creators,omitempty"`
}
// RepoDependencies represents the dependencies of a repo.
type RepoDependencies struct {
SPDXID *string `json:"SPDXID,omitempty"`
// Package name
Name *string `json:"name,omitempty"`
VersionInfo *string `json:"versionInfo,omitempty"`
DownloadLocation *string `json:"downloadLocation,omitempty"`
FilesAnalyzed *bool `json:"filesAnalyzed,omitempty"`
LicenseConcluded *string `json:"licenseConcluded,omitempty"`
LicenseDeclared *string `json:"licenseDeclared,omitempty"`
}
// SBOMInfo represents a software bill of materials (SBOM) using SPDX.
// SPDX is an open standard for SBOMs that
// identifies and catalogs components, licenses, copyrights, security
// references, and other metadata relating to software.
type SBOMInfo struct {
SPDXID *string `json:"SPDXID,omitempty"`
SPDXVersion *string `json:"spdxVersion,omitempty"`
CreationInfo *CreationInfo `json:"creationInfo,omitempty"`
// Repo name
Name *string `json:"name,omitempty"`
DataLicense *string `json:"dataLicense,omitempty"`
DocumentDescribes []string `json:"documentDescribes,omitempty"`
DocumentNamespace *string `json:"documentNamespace,omitempty"`
// List of packages dependencies
Packages []*RepoDependencies `json:"packages,omitempty"`
}
func (s SBOM) String() string {
return Stringify(s)
}
// GetSBOM fetches the software bill of materials for a repository.
//
// GitHub API docs: https://docs.github.com/en/rest/dependency-graph/sboms
func (s *DependencyGraphService) GetSBOM(ctx context.Context, owner, repo string) (*SBOM, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/dependency-graph/sbom", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var sbom *SBOM
resp, err := s.client.Do(ctx, req, &sbom)
if err != nil {
return nil, resp, err
}
return sbom, resp, nil
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2023 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"net/http"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func TestDependencyGraphService_GetSBOM(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{
"sbom":{
"creationInfo":{
"created":"2021-09-01T00:00:00Z"
},
"name":"owner/repo",
"packages":[
{
"name":"rubygems:rails",
"versionInfo":"1.0.0"
}
]
}
}`)
})
ctx := context.Background()
sbom, _, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo")
if err != nil {
t.Errorf("DependencyGraph.GetSBOM returned error: %v", err)
}
testTime := time.Date(2021, 9, 1, 0, 0, 0, 0, time.UTC)
want := &SBOM{
&SBOMInfo{
CreationInfo: &CreationInfo{
Created: &Timestamp{testTime},
},
Name: String("owner/repo"),
Packages: []*RepoDependencies{
{
Name: String("rubygems:rails"),
VersionInfo: String("1.0.0"),
},
},
},
}
if !cmp.Equal(sbom, want) {
t.Errorf("DependencyGraph.GetSBOM returned %+v, want %+v", sbom, want)
}
const methodName = "GetSBOM"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.DependencyGraph.GetSBOM(ctx, "\n", "\n")
return err
})
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo")
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}
+120
View File
@@ -4726,6 +4726,14 @@ func (c *CreateUserProjectOptions) GetBody() string {
return *c.Body
}
// GetCreated returns the Created field if it's non-nil, zero value otherwise.
func (c *CreationInfo) GetCreated() Timestamp {
if c == nil || c.Created == nil {
return Timestamp{}
}
return *c.Created
}
// GetAuthorizedCredentialExpiresAt returns the AuthorizedCredentialExpiresAt field if it's non-nil, zero value otherwise.
func (c *CredentialAuthorization) GetAuthorizedCredentialExpiresAt() Timestamp {
if c == nil || c.AuthorizedCredentialExpiresAt == nil {
@@ -17342,6 +17350,62 @@ func (r *RenameOrgResponse) GetURL() string {
return *r.URL
}
// GetDownloadLocation returns the DownloadLocation field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetDownloadLocation() string {
if r == nil || r.DownloadLocation == nil {
return ""
}
return *r.DownloadLocation
}
// GetFilesAnalyzed returns the FilesAnalyzed field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetFilesAnalyzed() bool {
if r == nil || r.FilesAnalyzed == nil {
return false
}
return *r.FilesAnalyzed
}
// GetLicenseConcluded returns the LicenseConcluded field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetLicenseConcluded() string {
if r == nil || r.LicenseConcluded == nil {
return ""
}
return *r.LicenseConcluded
}
// GetLicenseDeclared returns the LicenseDeclared field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetLicenseDeclared() string {
if r == nil || r.LicenseDeclared == nil {
return ""
}
return *r.LicenseDeclared
}
// GetName returns the Name field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetName() string {
if r == nil || r.Name == nil {
return ""
}
return *r.Name
}
// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetSPDXID() string {
if r == nil || r.SPDXID == nil {
return ""
}
return *r.SPDXID
}
// GetVersionInfo returns the VersionInfo field if it's non-nil, zero value otherwise.
func (r *RepoDependencies) GetVersionInfo() string {
if r == nil || r.VersionInfo == nil {
return ""
}
return *r.VersionInfo
}
// GetBranch returns the Branch field if it's non-nil, zero value otherwise.
func (r *RepoMergeUpstreamRequest) GetBranch() string {
if r == nil || r.Branch == nil {
@@ -19974,6 +20038,62 @@ func (s *SarifID) GetURL() string {
return *s.URL
}
// GetSBOM returns the SBOM field.
func (s *SBOM) GetSBOM() *SBOMInfo {
if s == nil {
return nil
}
return s.SBOM
}
// GetCreationInfo returns the CreationInfo field.
func (s *SBOMInfo) GetCreationInfo() *CreationInfo {
if s == nil {
return nil
}
return s.CreationInfo
}
// GetDataLicense returns the DataLicense field if it's non-nil, zero value otherwise.
func (s *SBOMInfo) GetDataLicense() string {
if s == nil || s.DataLicense == nil {
return ""
}
return *s.DataLicense
}
// GetDocumentNamespace returns the DocumentNamespace field if it's non-nil, zero value otherwise.
func (s *SBOMInfo) GetDocumentNamespace() string {
if s == nil || s.DocumentNamespace == nil {
return ""
}
return *s.DocumentNamespace
}
// GetName returns the Name field if it's non-nil, zero value otherwise.
func (s *SBOMInfo) GetName() string {
if s == nil || s.Name == nil {
return ""
}
return *s.Name
}
// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise.
func (s *SBOMInfo) GetSPDXID() string {
if s == nil || s.SPDXID == nil {
return ""
}
return *s.SPDXID
}
// GetSPDXVersion returns the SPDXVersion field if it's non-nil, zero value otherwise.
func (s *SBOMInfo) GetSPDXVersion() string {
if s == nil || s.SPDXVersion == nil {
return ""
}
return *s.SPDXVersion
}
// GetAnalysisKey returns the AnalysisKey field if it's non-nil, zero value otherwise.
func (s *ScanningAnalysis) GetAnalysisKey() string {
if s == nil || s.AnalysisKey == nil {
+144
View File
@@ -5575,6 +5575,16 @@ func TestCreateUserProjectOptions_GetBody(tt *testing.T) {
c.GetBody()
}
func TestCreationInfo_GetCreated(tt *testing.T) {
var zeroValue Timestamp
c := &CreationInfo{Created: &zeroValue}
c.GetCreated()
c = &CreationInfo{}
c.GetCreated()
c = nil
c.GetCreated()
}
func TestCredentialAuthorization_GetAuthorizedCredentialExpiresAt(tt *testing.T) {
var zeroValue Timestamp
c := &CredentialAuthorization{AuthorizedCredentialExpiresAt: &zeroValue}
@@ -20130,6 +20140,76 @@ func TestRenameOrgResponse_GetURL(tt *testing.T) {
r.GetURL()
}
func TestRepoDependencies_GetDownloadLocation(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{DownloadLocation: &zeroValue}
r.GetDownloadLocation()
r = &RepoDependencies{}
r.GetDownloadLocation()
r = nil
r.GetDownloadLocation()
}
func TestRepoDependencies_GetFilesAnalyzed(tt *testing.T) {
var zeroValue bool
r := &RepoDependencies{FilesAnalyzed: &zeroValue}
r.GetFilesAnalyzed()
r = &RepoDependencies{}
r.GetFilesAnalyzed()
r = nil
r.GetFilesAnalyzed()
}
func TestRepoDependencies_GetLicenseConcluded(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{LicenseConcluded: &zeroValue}
r.GetLicenseConcluded()
r = &RepoDependencies{}
r.GetLicenseConcluded()
r = nil
r.GetLicenseConcluded()
}
func TestRepoDependencies_GetLicenseDeclared(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{LicenseDeclared: &zeroValue}
r.GetLicenseDeclared()
r = &RepoDependencies{}
r.GetLicenseDeclared()
r = nil
r.GetLicenseDeclared()
}
func TestRepoDependencies_GetName(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{Name: &zeroValue}
r.GetName()
r = &RepoDependencies{}
r.GetName()
r = nil
r.GetName()
}
func TestRepoDependencies_GetSPDXID(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{SPDXID: &zeroValue}
r.GetSPDXID()
r = &RepoDependencies{}
r.GetSPDXID()
r = nil
r.GetSPDXID()
}
func TestRepoDependencies_GetVersionInfo(tt *testing.T) {
var zeroValue string
r := &RepoDependencies{VersionInfo: &zeroValue}
r.GetVersionInfo()
r = &RepoDependencies{}
r.GetVersionInfo()
r = nil
r.GetVersionInfo()
}
func TestRepoMergeUpstreamRequest_GetBranch(tt *testing.T) {
var zeroValue string
r := &RepoMergeUpstreamRequest{Branch: &zeroValue}
@@ -23273,6 +23353,70 @@ func TestSarifID_GetURL(tt *testing.T) {
s.GetURL()
}
func TestSBOM_GetSBOM(tt *testing.T) {
s := &SBOM{}
s.GetSBOM()
s = nil
s.GetSBOM()
}
func TestSBOMInfo_GetCreationInfo(tt *testing.T) {
s := &SBOMInfo{}
s.GetCreationInfo()
s = nil
s.GetCreationInfo()
}
func TestSBOMInfo_GetDataLicense(tt *testing.T) {
var zeroValue string
s := &SBOMInfo{DataLicense: &zeroValue}
s.GetDataLicense()
s = &SBOMInfo{}
s.GetDataLicense()
s = nil
s.GetDataLicense()
}
func TestSBOMInfo_GetDocumentNamespace(tt *testing.T) {
var zeroValue string
s := &SBOMInfo{DocumentNamespace: &zeroValue}
s.GetDocumentNamespace()
s = &SBOMInfo{}
s.GetDocumentNamespace()
s = nil
s.GetDocumentNamespace()
}
func TestSBOMInfo_GetName(tt *testing.T) {
var zeroValue string
s := &SBOMInfo{Name: &zeroValue}
s.GetName()
s = &SBOMInfo{}
s.GetName()
s = nil
s.GetName()
}
func TestSBOMInfo_GetSPDXID(tt *testing.T) {
var zeroValue string
s := &SBOMInfo{SPDXID: &zeroValue}
s.GetSPDXID()
s = &SBOMInfo{}
s.GetSPDXID()
s = nil
s.GetSPDXID()
}
func TestSBOMInfo_GetSPDXVersion(tt *testing.T) {
var zeroValue string
s := &SBOMInfo{SPDXVersion: &zeroValue}
s.GetSPDXVersion()
s = &SBOMInfo{}
s.GetSPDXVersion()
s = nil
s.GetSPDXVersion()
}
func TestScanningAnalysis_GetAnalysisKey(tt *testing.T) {
var zeroValue string
s := &ScanningAnalysis{AnalysisKey: &zeroValue}
+10
View File
@@ -1763,6 +1763,16 @@ func TestRepositoryRelease_String(t *testing.T) {
}
}
func TestSBOM_String(t *testing.T) {
v := SBOM{
SBOM: &SBOMInfo{},
}
want := `github.SBOM{SBOM:github.SBOMInfo{}}`
if got := v.String(); got != want {
t.Errorf("SBOM.String = %v, want %v", got, want)
}
}
func TestSSHSigningKey_String(t *testing.T) {
v := SSHSigningKey{
ID: Int64(0),
+2
View File
@@ -186,6 +186,7 @@ type Client struct {
CodeScanning *CodeScanningService
Codespaces *CodespacesService
Dependabot *DependabotService
DependencyGraph *DependencyGraphService
Enterprise *EnterpriseService
Gists *GistsService
Git *GitService
@@ -326,6 +327,7 @@ func NewClient(httpClient *http.Client) *Client {
c.CodeScanning = (*CodeScanningService)(&c.common)
c.Codespaces = (*CodespacesService)(&c.common)
c.Dependabot = (*DependabotService)(&c.common)
c.DependencyGraph = (*DependencyGraphService)(&c.common)
c.Enterprise = (*EnterpriseService)(&c.common)
c.Gists = (*GistsService)(&c.common)
c.Git = (*GitService)(&c.common)