diff --git a/example/go.mod b/example/go.mod index f44e4246166..44daa815481 100644 --- a/example/go.mod +++ b/example/go.mod @@ -6,6 +6,7 @@ require ( github.com/ProtonMail/go-crypto v1.5.2 github.com/gofri/go-github-pagination v1.0.1 github.com/gofri/go-github-ratelimit/v2 v2.0.2 + github.com/golang/snappy v1.0.0 github.com/google/go-github/otel/v93 v93.0.0 github.com/google/go-github/v93 v93.0.0 github.com/jferrl/go-githubauth v1.9.1 diff --git a/example/go.sum b/example/go.sum index 7e3a7bbc046..8acbf03e292 100644 --- a/example/go.sum +++ b/example/go.sum @@ -164,6 +164,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo= github.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= diff --git a/example/verifyartifact/main.go b/example/verifyartifact/main.go index 2a41553c589..0d2120ded4d 100644 --- a/example/verifyartifact/main.go +++ b/example/verifyartifact/main.go @@ -15,9 +15,12 @@ import ( "encoding/json" "flag" "fmt" + "io" "log" + "net/http" "os" + "github.com/golang/snappy" "github.com/google/go-github/v93/github" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" @@ -107,17 +110,55 @@ 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) } } } +// getAttestationBundle returns the Sigstore bundle of the attestation. +// GitHub returns a bundle_url pointing to a snappy-compressed bundle instead of +// embedding the bundle in the response. +func getAttestationBundle(ctx context.Context, attestation *github.Attestation) ([]byte, error) { + bundleURL := attestation.GetBundleURL() + if bundleURL == "" { + return attestation.Bundle, nil //nolint:staticcheck // Fall back to the embedded bundle of older responses. + } + + // The bundle URL does not need the GitHub token, so it is fetched with the default client. + req, err := http.NewRequestWithContext(ctx, "GET", bundleURL, nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch attestation bundle: %v", resp.Status) + } + + compressed, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return snappy.Decode(nil, compressed) +} + func getTrustedMaterial() (root.TrustedMaterialCollection, error) { trustedRootJSON, err := os.ReadFile(*trustedRootJSONPath) if err != nil { diff --git a/github/attestations.go b/github/attestations.go index d2212c9caa3..4c1a80ab943 100644 --- a/github/attestations.go +++ b/github/attestations.go @@ -17,6 +17,8 @@ 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 removes this field from attestation list responses. Use BundleURL instead. Bundle json.RawMessage `json:"bundle,omitempty"` // The download URL for the attestation's Sigstore Bundle. BundleURL *string `json:"bundle_url,omitempty"`