Add bundle upgrade command (#4820)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
156
cmd/cosign/cli/bundle/upgrade.go
Normal file
156
cmd/cosign/cli/bundle/upgrade.go
Normal 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
|
||||
}
|
||||
447
cmd/cosign/cli/bundle/upgrade_test.go
Normal file
447
cmd/cosign/cli/bundle/upgrade_test.go
Normal 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
|
||||
}
|
||||
@@ -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
1
doc/cosign_bundle.md
generated
@@ -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
32
doc/cosign_bundle_upgrade.md
generated
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user