diff --git a/example/verifyartifact/main.go b/example/verifyartifact/main.go index 4a5d6bac50a..0629f94d114 100644 --- a/example/verifyartifact/main.go +++ b/example/verifyartifact/main.go @@ -15,7 +15,9 @@ import ( "encoding/json" "flag" "fmt" + "io" "log" + "net/http" "os" "github.com/google/go-github/v91/github" @@ -107,17 +109,44 @@ func main() { var b *bundle.Bundle for _, attestation := range attestations.Attestations { - if err := json.Unmarshal(attestation.Bundle, &b); err != nil { + bundleJSON, err := getAttestationBundle(ctx, attestation) + if err != nil { + log.Fatal(err) + } + if err := json.Unmarshal(bundleJSON, &b); err != nil { log.Fatal(err) } - err := runVerification(sev, pb, b) + err = runVerification(sev, pb, b) if err != nil { log.Fatal(err) } } } +func getAttestationBundle(ctx context.Context, attestation *github.Attestation) ([]byte, error) { + if attestation.BundleURL != nil { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, *attestation.BundleURL, nil) + if err != nil { + return nil, fmt.Errorf("failed to create bundle request: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to fetch attestation bundle: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch attestation bundle: %s", resp.Status) + } + return io.ReadAll(resp.Body) + } + + // Bundle is retained for responses from older GitHub API versions and GHES. + return attestation.Bundle, nil //nolint:staticcheck +} + func getTrustedMaterial() (root.TrustedMaterialCollection, error) { trustedRootJSON, err := os.ReadFile(*trustedRootJSONPath) if err != nil { diff --git a/github/attestations.go b/github/attestations.go index 618d5d73f6c..1d404e0b765 100644 --- a/github/attestations.go +++ b/github/attestations.go @@ -17,7 +17,10 @@ type Attestation struct { // The attestation's Sigstore Bundle. // Refer to the sigstore bundle specification for more info: // https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto + // + // Deprecated: GitHub REST API version 2026-03-10 returns BundleURL instead. Bundle json.RawMessage `json:"bundle"` + BundleURL *string `json:"bundle_url,omitempty"` RepositoryID int64 `json:"repository_id"` } diff --git a/github/github-accessors.go b/github/github-accessors.go index b9ce167b413..08a8fe32728 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -2734,6 +2734,14 @@ func (a *Attestation) GetBundle() json.RawMessage { return a.Bundle } +// GetBundleURL returns the BundleURL field if it's non-nil, zero value otherwise. +func (a *Attestation) GetBundleURL() string { + if a == nil || a.BundleURL == nil { + return "" + } + return *a.BundleURL +} + // GetRepositoryID returns the RepositoryID field. func (a *Attestation) GetRepositoryID() int64 { if a == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index bc822744add..f1e27c1ff9f 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -3452,6 +3452,17 @@ func TestAttestation_GetBundle(tt *testing.T) { a.GetBundle() } +func TestAttestation_GetBundleURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Attestation{BundleURL: &zeroValue} + a.GetBundleURL() + a = &Attestation{} + a.GetBundleURL() + a = nil + a.GetBundleURL() +} + func TestAttestation_GetRepositoryID(tt *testing.T) { tt.Parallel() a := &Attestation{} diff --git a/github/repos_attestations_test.go b/github/repos_attestations_test.go index cc522c3ce86..271b651375c 100644 --- a/github/repos_attestations_test.go +++ b/github/repos_attestations_test.go @@ -27,7 +27,7 @@ func TestRepositoriesService_ListAttestations(t *testing.T) { }, { "repository_id": 2, - "bundle": {} + "bundle_url": "https://example.com/attestations/2" } ] }`) @@ -46,7 +46,7 @@ func TestRepositoriesService_ListAttestations(t *testing.T) { }, { RepositoryID: 2, - Bundle: []byte(`{}`), + BundleURL: new("https://example.com/attestations/2"), }, }, }