1
0
mirror of synced 2026-10-24 12:59:22 +03:00

Add bundle upgrade command (#4820)

This commit is contained in:
Aaron Lew
2026-05-10 19:59:43 -04:00
committed by GitHub
parent 6a80f22cae
commit a1ec073702
6 changed files with 677 additions and 0 deletions

View File

@@ -31,6 +31,7 @@ func Bundle() *cobra.Command {
}
cmd.AddCommand(bundleCreate())
cmd.AddCommand(bundleUpgrade())
return cmd
}
@@ -68,3 +69,28 @@ func bundleCreate() *cobra.Command {
o.AddFlags(cmd)
return cmd
}
func bundleUpgrade() *cobra.Command {
o := &options.BundleUpgradeOptions{}
cmd := &cobra.Command{
Use: "upgrade <bundle>",
Short: "Upgrade a Sigstore protobuf bundle",
Long: "Upgrade a Sigstore Protobuf bundle to the latest version. This command only supports standardized bundles.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
bundleUpgradeCmd := &bundle.UpgradeCmd{
Out: o.Out,
RekorURL: o.RekorURL,
}
ctx, cancel := context.WithTimeout(cmd.Context(), ro.Timeout)
defer cancel()
return bundleUpgradeCmd.Exec(ctx, args[0])
},
}
o.AddFlags(cmd)
return cmd
}

View File

@@ -0,0 +1,156 @@
//
// Copyright 2026 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bundle
import (
"context"
"crypto/x509"
"fmt"
"os"
protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1"
rekor "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/client/entries"
"github.com/sigstore/rekor/pkg/tle"
"google.golang.org/protobuf/encoding/protojson"
"github.com/sigstore/cosign/v3/internal/ui"
)
type UpgradeCmd struct {
Out string
RekorURL string
}
func (c *UpgradeCmd) Exec(ctx context.Context, bundlePath string) error {
data, err := os.ReadFile(bundlePath)
if err != nil {
return fmt.Errorf("reading input file: %w", err)
}
rekorClient, err := rekor.GetRekorClient(c.RekorURL)
if err != nil {
return fmt.Errorf("creating rekor client: %w", err)
}
upgradedBundle, err := upgradeBundle(ctx, data, rekorClient)
if err != nil {
return fmt.Errorf("upgrading bundle: %w", err)
}
if c.Out != "" {
err = os.WriteFile(c.Out, upgradedBundle, 0600)
if err != nil {
return fmt.Errorf("writing upgraded bundle: %w", err)
}
ui.Infof(ctx, "Successfully upgraded bundle written to %s", c.Out)
} else {
fmt.Println(string(upgradedBundle))
}
return nil
}
func upgradeBundle(ctx context.Context, data []byte, rekorClient *client.Rekor) ([]byte, error) {
var bundle protobundle.Bundle
if err := protojson.Unmarshal(data, &bundle); err != nil {
return nil, fmt.Errorf("unmarshaling bundle: %w", err)
}
if bundle.VerificationMaterial == nil {
return nil, fmt.Errorf("bundle is missing verification material")
}
if bundle.VerificationMaterial.Content == nil {
return nil, fmt.Errorf("bundle verification material is missing content (public key or certificate)")
}
switch bundle.MediaType {
case "application/vnd.dev.sigstore.bundle.v0.3+json", "application/vnd.dev.sigstore.bundle+json;version=0.3":
ui.Infof(ctx, "Bundle is already at v0.3, no upgrade needed.")
return data, nil
case "application/vnd.dev.sigstore.bundle+json;version=0.1":
ui.Infof(ctx, "Upgrading from v0.1 to v0.3...")
case "application/vnd.dev.sigstore.bundle+json;version=0.2":
ui.Infof(ctx, "Upgrading from v0.2 to v0.3...")
default:
return nil, fmt.Errorf("unsupported bundle version: %s", bundle.MediaType)
}
if chainContent, ok := bundle.VerificationMaterial.Content.(*protobundle.VerificationMaterial_X509CertificateChain); ok {
certChain := chainContent.X509CertificateChain.Certificates
if len(certChain) > 0 {
ui.Infof(ctx, "Truncating certificate chain to only the leaf certificate...")
for i, cert := range certChain {
parsedCert, err := x509.ParseCertificate(cert.RawBytes)
if err != nil {
ui.Infof(ctx, " Certificate %d: <unable to parse: %v>", i, err)
continue
}
var identity string
if len(parsedCert.EmailAddresses) > 0 {
identity = parsedCert.EmailAddresses[0]
} else if len(parsedCert.URIs) > 0 {
identity = parsedCert.URIs[0].String()
} else if s := parsedCert.Subject.String(); s != "" {
identity = s
} else {
identity = "<none>"
}
ui.Infof(ctx, " Certificate %d Identity: %s", i, identity)
}
bundle.VerificationMaterial.Content = &protobundle.VerificationMaterial_Certificate{
Certificate: certChain[0],
}
}
}
for i, entry := range bundle.VerificationMaterial.TlogEntries {
if entry.InclusionPromise != nil && entry.InclusionProof == nil {
ui.Infof(ctx, "Fetching missing inclusion proof from Rekor for log index %d...", entry.LogIndex)
params := entries.NewGetLogEntryByIndexParamsWithContext(ctx)
params.SetLogIndex(entry.LogIndex)
resp, err := rekorClient.Entries.GetLogEntryByIndex(params)
if err != nil {
return nil, fmt.Errorf("fetching log entry by index: %w", err)
}
if len(resp.Payload) != 1 {
return nil, fmt.Errorf("expected exactly 1 entry from Rekor for index %d, got %d", entry.LogIndex, len(resp.Payload))
}
for _, e := range resp.Payload {
protoEntry, err := tle.GenerateTransparencyLogEntry(e)
if err != nil {
return nil, fmt.Errorf("generating proto entry: %w", err)
}
bundle.VerificationMaterial.TlogEntries[i] = protoEntry
}
}
}
bundle.MediaType = "application/vnd.dev.sigstore.bundle.v0.3+json"
out, err := protojson.Marshal(&bundle)
if err != nil {
return nil, fmt.Errorf("marshaling bundle: %w", err)
}
return out, nil
}

View File

@@ -0,0 +1,447 @@
//
// Copyright 2026 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bundle
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/json"
"io"
"math/big"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/client/entries"
"github.com/sigstore/rekor/pkg/generated/models"
)
type mockEntriesClient struct {
entries.ClientService
getLogEntryByIndexFunc func(params *entries.GetLogEntryByIndexParams, opts ...entries.ClientOption) (*entries.GetLogEntryByIndexOK, error)
}
func (m *mockEntriesClient) GetLogEntryByIndex(params *entries.GetLogEntryByIndexParams, opts ...entries.ClientOption) (*entries.GetLogEntryByIndexOK, error) {
if m.getLogEntryByIndexFunc != nil {
return m.getLogEntryByIndexFunc(params, opts...)
}
return nil, nil
}
func TestUpgradeBundle(t *testing.T) {
checkV03 := func(expectedCertB64 string) func(t *testing.T, output []byte) {
return func(t *testing.T, output []byte) {
var m map[string]interface{}
err := json.Unmarshal(output, &m)
if err != nil {
t.Fatalf("unmarshaling output: %v", err)
}
if m["mediaType"] != "application/vnd.dev.sigstore.bundle.v0.3+json" {
t.Fatalf("expected mediaType to be 'application/vnd.dev.sigstore.bundle.v0.3+json', got %v", m["mediaType"])
}
vm, ok := m["verificationMaterial"].(map[string]interface{})
if !ok {
t.Fatal("missing verificationMaterial")
}
cert, ok := vm["certificate"].(map[string]interface{})
if !ok {
t.Fatal("missing certificate field in verificationMaterial")
}
if cert["rawBytes"] != expectedCertB64 {
t.Fatalf("expected leaf certificate rawBytes to be %v, got %v", expectedCertB64, cert["rawBytes"])
}
}
}
genCert := func(cn string, emails []string, uris []string) string {
cert, _, err := selfSignedCertificate(cn, emails, uris)
if err != nil {
t.Fatal(err)
}
return base64.StdEncoding.EncodeToString(cert.Raw)
}
certEmailB64 := genCert("cert", []string{"foo@bar.com"}, nil)
certURIB64 := genCert("cert", nil, []string{"https://example.com/workflow"})
certSubjectB64 := genCert("my-identity", nil, nil)
certNoneB64 := genCert("", nil, nil)
tests := []struct {
name string
input string
expectError bool
checkOutput func(t *testing.T, output []byte)
rekorClient *client.Rekor
}{
{
name: "Already v0.3",
input: `{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"publicKey":{}}}`,
checkOutput: func(t *testing.T, output []byte) {
expected := `{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"publicKey":{}}}`
if string(output) != expected {
t.Fatal("expected output to match input")
}
},
},
{
name: "Upgrade v0.1 to v0.3",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "bGVhZg=="},
{"rawBytes": "aW50ZXJtZWRpYXRl"}
]
}
}
}`,
checkOutput: checkV03("bGVhZg=="),
},
{
name: "Upgrade v0.2 to v0.3",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.2",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "bGVhZg=="},
{"rawBytes": "aW50ZXJtZWRpYXRl"}
]
}
}
}`,
checkOutput: checkV03("bGVhZg=="),
},
{
name: "Upgrade with Email SAN",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "` + certEmailB64 + `"}
]
}
}
}`,
checkOutput: checkV03(certEmailB64),
},
{
name: "Upgrade with URI SAN",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "` + certURIB64 + `"}
]
}
}
}`,
checkOutput: checkV03(certURIB64),
},
{
name: "Upgrade with Subject",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "` + certSubjectB64 + `"}
]
}
}
}`,
checkOutput: checkV03(certSubjectB64),
},
{
name: "Upgrade with no identity",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "` + certNoneB64 + `"}
]
}
}
}`,
checkOutput: checkV03(certNoneB64),
},
{
name: "Missing VerificationMaterial",
input: `{"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1"}`,
expectError: true,
},
{
name: "Missing Content in VerificationMaterial",
input: `{"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": {}}`,
expectError: true,
},
{
name: "Unsupported version",
input: `{"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.4", "verificationMaterial": {"publicKey": {}}}`,
expectError: true,
},
{
name: "Upgrade with missing inclusion proof",
input: `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "bGVhZg=="}
]
},
"tlogEntries": [
{
"logIndex": 42,
"inclusionPromise": {
"signedEntryTimestamp": "c2ln"
}
}
]
}
}`,
rekorClient: &client.Rekor{
Entries: &mockEntriesClient{
getLogEntryByIndexFunc: func(_ *entries.GetLogEntryByIndexParams, _ ...entries.ClientOption) (*entries.GetLogEntryByIndexOK, error) {
return &entries.GetLogEntryByIndexOK{
Payload: models.LogEntry{
"uuid": createMockLogEntryAnon(),
},
}, nil
},
},
},
checkOutput: func(t *testing.T, output []byte) {
var m map[string]interface{}
if err := json.Unmarshal(output, &m); err != nil {
t.Fatalf("unmarshaling output: %v", err)
}
vm := m["verificationMaterial"].(map[string]interface{})
tlogEntries := vm["tlogEntries"].([]interface{})
entry := tlogEntries[0].(map[string]interface{})
proof := entry["inclusionProof"].(map[string]interface{})
if proof["logIndex"].(string) != "42" {
t.Fatalf("expected logIndex 42, got %v", proof["logIndex"])
}
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
out, err := upgradeBundle(context.Background(), []byte(tc.input), tc.rekorClient)
if tc.expectError {
if err == nil {
t.Fatal("expected error, got nil")
}
} else {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tc.checkOutput != nil {
tc.checkOutput(t, out)
}
}
})
}
}
func TestUpgradeCmd(t *testing.T) {
ctx := context.Background()
t.Run("File Output", func(t *testing.T) {
td := t.TempDir()
inputPath := filepath.Join(td, "input.json")
v01Bundle := `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "bGVhZg=="}
]
}
}
}`
err := os.WriteFile(inputPath, []byte(v01Bundle), 0600)
if err != nil {
t.Fatal(err)
}
outputPath := filepath.Join(td, "output.json")
cmd := UpgradeCmd{
Out: outputPath,
}
err = cmd.Exec(ctx, inputPath)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
data, err := os.ReadFile(outputPath)
if err != nil {
t.Fatal(err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatal(err)
}
if m["mediaType"] != "application/vnd.dev.sigstore.bundle.v0.3+json" {
t.Fatalf("expected mediaType to be 'application/vnd.dev.sigstore.bundle.v0.3+json', got %v", m["mediaType"])
}
})
t.Run("Stdout Fallback", func(t *testing.T) {
td := t.TempDir()
inputPath := filepath.Join(td, "input.json")
v01Bundle := `{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"x509CertificateChain": {
"certificates": [
{"rawBytes": "bGVhZg=="}
]
}
}
}`
err := os.WriteFile(inputPath, []byte(v01Bundle), 0600)
if err != nil {
t.Fatal(err)
}
cmd := UpgradeCmd{
Out: "",
}
reader, writer, err := os.Pipe()
if err != nil {
t.Fatal("failed to create a pipe for testing os.Stdout")
}
stdout := os.Stdout
os.Stdout = writer
err = cmd.Exec(ctx, inputPath)
os.Stdout = stdout
writer.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var buffer bytes.Buffer
_, err = io.Copy(&buffer, reader)
if err != nil {
t.Fatal(err)
}
output := buffer.Bytes()
var m map[string]interface{}
if err := json.Unmarshal(output, &m); err != nil {
t.Fatalf("unmarshaling stdout output: %v", err)
}
if m["mediaType"] != "application/vnd.dev.sigstore.bundle.v0.3+json" {
t.Fatalf("expected mediaType to be 'application/vnd.dev.sigstore.bundle.v0.3+json', got %v", m["mediaType"])
}
})
}
func createMockLogEntryAnon() models.LogEntryAnon {
body := "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyMzBkODM1OGRjOGU4ODkwYjRjNThkZWViNjI5MTJlZTJmMjAzNTdhZTkyYTVjYzg2MWI5OGU2OGZlMzFhY2I1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURHcXFXL1Q4YzZqZTltSVFPaUNhZXdldWZpUmJXMC9YZVBJUmMxWVdXUVZBSWdNWjVlbmNvWFdNdjdUK3AxU0k1YUUzdzZOb3Zyb3RYdGVoMlV3V040SUJvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSnJha05EUVZScFowRjNTVUpCWjBsQ1FWUkJTMEpuWjNGb2EycFBVRkZSUkVGcVFYVk5VbFYzUlhkWlJGWlJVVXRGZDNoNllWZGtlbVJIT1hrS1dsTTFhMXBZV1hoR1ZFRlVRbWRPVmtKQlRWUkVTRTV3V2pOT01HSXpTbXhNV0U0eFdXcEJaVVozTUhsT2FrRXdUVlJWZUU1RVRUUk9SR2hoUm5jd2VRcE9ha0V3VFZSVmVFNVVUVFZPUkdoaFRVRkJkMWRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVMM0JKUVdaWldXNUxDazlHYkUxS00zYzBMMnBxUmxoNFRHNHhTRlZOWkVzMGJteDJiU3RLV0c1WmNUbFlPRlZpTkVWRFJFcFFjRUp1VW1sd1pGQkRUWGxYWTBsRGRIRkVPV1lLVlVGdFpIVnZiMUZLWmpReWJ6TlZkMk42UVU5Q1owNVdTRkU0UWtGbU9FVkNRVTFEUWpSQmQwVjNXVVJXVWpCc1FrRjNkME5uV1VsTGQxbENRbEZWU0FwQmQwMTNTSGRaUkZaU01HcENRbWQzUm05QlZXbHFPVEJ5VFZSeVVGaHlkM2hHYm5kTk9FOUdla3MxUW5Ock1IZEdVVmxFVmxJd1VrRlJTQzlDUVhOM0NrTlpSVWhqTTFacFlXMVdhbVJFUVZWQ1oyOXlRbWRGUlVGWlR5OU5RVVZDUWtGYWNHTXpUakZhV0VsM1EyZFpTVXR2V2tsNmFqQkZRWGRKUkZOQlFYY0tVbEZKWjFSSk4ya3pUWEYwVVRBMlp6QnlNbTlEWXpWb1QwZFBWRVZLYlVKSlZUSTVhRXhqVGxKNFJXeHBTbTlEU1ZGRWNuWk9TSE56U21vd1JqWlVNd293UmpaTU4wRkhiWEpDVlhGQllWSllSV0owWVhOaE5IcFJkWHA0YUdjOVBRb3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0ifX19fQ=="
integratedTime := int64(1234567890)
logIndex := int64(42)
treeSize := int64(100)
rootHash := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
logID := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
checkpoint := "checkpoint"
return models.LogEntryAnon{
Body: body,
IntegratedTime: &integratedTime,
LogIndex: &logIndex,
LogID: &logID,
Verification: &models.LogEntryAnonVerification{
SignedEntryTimestamp: []byte("sig"),
InclusionProof: &models.InclusionProof{
LogIndex: &logIndex,
TreeSize: &treeSize,
RootHash: &rootHash,
Hashes: []string{},
Checkpoint: &checkpoint,
},
},
}
}
func selfSignedCertificate(commonName string, emails []string, uris []string) (*x509.Certificate, *ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
var parsedURIs []*url.URL
for _, u := range uris {
parsed, _ := url.Parse(u)
parsedURIs = append(parsedURIs, parsed)
}
var subject pkix.Name
if commonName != "" {
subject.CommonName = commonName
subject.Organization = []string{"dev"}
}
ct := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: subject,
EmailAddresses: emails,
URIs: parsedURIs,
NotBefore: time.Now().Add(-1 * time.Minute),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
}
certBytes, err := x509.CreateCertificate(rand.Reader, ct, ct, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, err
}
return cert, priv, nil
}

View File

@@ -91,3 +91,18 @@ func (o *BundleCreateOptions) AddFlags(cmd *cobra.Command) {
cmd.MarkFlagsMutuallyExclusive("bundle", "certificate")
cmd.MarkFlagsMutuallyExclusive("bundle", "signature")
}
type BundleUpgradeOptions struct {
Out string
RekorURL string
}
var _ Interface = (*BundleUpgradeOptions)(nil)
func (o *BundleUpgradeOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.Out, "out", "", "path to the output upgraded bundle file")
_ = cmd.MarkFlagFilename("out", bundleExts...)
cmd.Flags().StringVar(&o.RekorURL, "rekor-url", "https://rekor.sigstore.dev", "URL of the transparency log")
_ = cmd.RegisterFlagCompletionFunc("rekor-url", cobra.NoFileCompletions)
}

1
doc/cosign_bundle.md generated
View File

@@ -24,4 +24,5 @@ Tools for interacting with a Sigstore protobuf bundle
* [cosign](cosign.md) - A tool for Container Signing, Verification and Storage in an OCI registry.
* [cosign bundle create](cosign_bundle_create.md) - Create a Sigstore protobuf bundle
* [cosign bundle upgrade](cosign_bundle_upgrade.md) - Upgrade a Sigstore protobuf bundle

32
doc/cosign_bundle_upgrade.md generated Normal file
View File

@@ -0,0 +1,32 @@
## cosign bundle upgrade
Upgrade a Sigstore protobuf bundle
### Synopsis
Upgrade a Sigstore Protobuf bundle to the latest version. This command only supports standardized bundles.
```
cosign bundle upgrade <bundle> [flags]
```
### Options
```
-h, --help help for upgrade
--out string path to the output upgraded bundle file
--rekor-url string URL of the transparency log (default "https://rekor.sigstore.dev")
```
### Options inherited from parent commands
```
--output-file string log output to a file
-t, --timeout duration timeout for commands (default 3m0s)
-d, --verbose log debug output
```
### SEE ALSO
* [cosign bundle](cosign_bundle.md) - Interact with a Sigstore protobuf bundle