diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index 9f8b95adc29..e133c695b62 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -26,4 +26,4 @@ jobs: permissions: contents: read - uses: sigstore/community/.github/workflows/reusable-dependency-review.yml@9b1b5aca605f92ec5b1bf3681b1e61b3dbc420cc + uses: sigstore/community/.github/workflows/reusable-dependency-review.yml@main diff --git a/.github/workflows/donotsubmit.yaml b/.github/workflows/donotsubmit.yaml index 0b01302d47e..adca5003cc7 100644 --- a/.github/workflows/donotsubmit.yaml +++ b/.github/workflows/donotsubmit.yaml @@ -40,4 +40,4 @@ jobs: persist-credentials: false - name: Do Not Submit - uses: chainguard-dev/actions/donotsubmit@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + uses: chainguard-dev/actions/donotsubmit@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 524c4ce13de..8dd48a724f5 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -220,4 +220,4 @@ jobs: - name: Collect diagnostics if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + uses: chainguard-dev/actions/kind-diag@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 diff --git a/.github/workflows/kind-verify-attestation.yaml b/.github/workflows/kind-verify-attestation.yaml index bf641fab2f3..6a939b46b33 100644 --- a/.github/workflows/kind-verify-attestation.yaml +++ b/.github/workflows/kind-verify-attestation.yaml @@ -156,7 +156,7 @@ jobs: - name: Collect diagnostics if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + uses: chainguard-dev/actions/kind-diag@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 - name: Create vuln attestation for it run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index dffd80bf49a..e7e65be5b69 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -169,7 +169,7 @@ jobs: - name: Collect diagnostics if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + uses: chainguard-dev/actions/kind-diag@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 e2e-windows-powershell-tests: name: Run PowerShell E2E tests diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml index 23e170974cd..da24b2c50bd 100644 --- a/.github/workflows/validate-release.yml +++ b/.github/workflows/validate-release.yml @@ -31,9 +31,9 @@ jobs: steps: - name: Check Signature run: | - cosign verify ghcr.io/gythialy/golang-cross:v1.24.3-0@sha256:b0e66440a1dc4216c45d9df95ac9c34b9cb2e7de1d9e55a94914eb38c2ec2249 \ + cosign verify ghcr.io/gythialy/golang-cross:v1.24.4-0@sha256:0b29abd58891e1b3dc915efbfec697f93151118e20c13860ac1c8667ef14fb24 \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.24.3-0" + --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.24.4-0" env: TUF_ROOT: /tmp @@ -43,7 +43,7 @@ jobs: - check-signature container: - image: ghcr.io/gythialy/golang-cross:v1.24.3-0@sha256:b0e66440a1dc4216c45d9df95ac9c34b9cb2e7de1d9e55a94914eb38c2ec2249 + image: ghcr.io/gythialy/golang-cross:v1.24.4-0@sha256:0b29abd58891e1b3dc915efbfec697f93151118e20c13860ac1c8667ef14fb24 volumes: - /usr:/host_usr - /opt:/host_opt diff --git a/.github/workflows/whitespace.yaml b/.github/workflows/whitespace.yaml index e755705c790..8ba3b8c2046 100644 --- a/.github/workflows/whitespace.yaml +++ b/.github/workflows/whitespace.yaml @@ -38,8 +38,8 @@ jobs: with: persist-credentials: false - - uses: chainguard-dev/actions/trailing-space@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + - uses: chainguard-dev/actions/trailing-space@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 if: ${{ always() }} - - uses: chainguard-dev/actions/eof-newline@5363dd9eb48083bbf7674a4bbe62d71c3b230edd # v1.1.2 + - uses: chainguard-dev/actions/eof-newline@939ece6bc39459fd24dde56e63ca93adf840031e # v1.2.1 if: ${{ always() }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b5ee04287..1d78299fca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# v2.5.1 + +## Features + +* Add Rekor v2 support for trusted-root create (#4242) +* Add baseUrl and Uri to trusted-root create command +* Upgrade to TUF v2 client with trusted root +* Don't verify SCT for a private PKI cert (#4225) +* Bump TSA library to relax EKU chain validation rules (#4219) + +## Bug Fixes + +* Bump sigstore-go to pick up log index=0 fix (#4162) +* remove unused recursive flag on attest command (#4187) + +## Docs + +* Fix indentation in `verify-blob` cmd examples (#4160) + +## Releases + +* ensure we copy the latest tags on each release (#4157) + +## Contributors + +* arthurus-rex +* Babak K. Shandiz +* Bob Callaway +* Carlos Tadeu Panato Junior +* Colleen Murphy +* Dmitry Savintsev +* Emmanuel Ferdman +* Hayden B +* Ville Skyttä + # v2.5.0 v2.5.0 includes an implementation of the new bundle specification, diff --git a/cmd/conformance/main.go b/cmd/conformance/main.go index 4d43702aa5e..d4916df6df6 100644 --- a/cmd/conformance/main.go +++ b/cmd/conformance/main.go @@ -124,11 +124,16 @@ func main() { args = append(args, os.Args[len(os.Args)-1]) dir := filepath.Dir(os.Args[0]) + initCmd := exec.Command(filepath.Join(dir, "cosign"), "initialize") // #nosec G204 + err := initCmd.Run() + if err != nil { + log.Fatal(err) + } cmd := exec.Command(filepath.Join(dir, "cosign"), args...) // #nosec G204 var out strings.Builder cmd.Stdout = &out cmd.Stderr = &out - err := cmd.Run() + err = cmd.Run() fmt.Println(out.String()) diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go index 20400ad6c58..f9ac6a85c55 100644 --- a/cmd/cosign/cli/attest.go +++ b/cmd/cosign/cli/attest.go @@ -16,11 +16,15 @@ package cli import ( + "context" "fmt" "github.com/sigstore/cosign/v2/cmd/cosign/cli/attest" "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/internal/ui" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/spf13/cobra" ) @@ -59,6 +63,9 @@ func Attest() *cobra.Command { # supply attestation via stdin echo | cosign attest --predicate - + # write attestation to stdout + cosign attest --predicate --type --key cosign.key --no-upload true + # attach an attestation to a container image and honor the creation timestamp of the signature cosign attest --predicate --type --key cosign.key --record-creation-timestamp `, @@ -69,6 +76,7 @@ func Attest() *cobra.Command { if err != nil { return err } + ko := options.KeyOpts{ KeyRef: o.Key, PassFunc: generate.GetPass, @@ -92,6 +100,13 @@ func Attest() *cobra.Command { TSAServerURL: o.TSAServerURL, NewBundleFormat: o.NewBundleFormat, } + if o.Key == "" && env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" { // Get the trusted root if using fulcio for signing + trustedMaterial, err := cosign.TrustedRoot() + if err != nil { + ui.Warnf(context.Background(), "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + ko.TrustedMaterial = trustedMaterial + } attestCommand := attest.AttestCommand{ KeyOpts: ko, RegistryOptions: o.Registry, diff --git a/cmd/cosign/cli/attest_blob.go b/cmd/cosign/cli/attest_blob.go index 2340b792e08..f63d3d37c72 100644 --- a/cmd/cosign/cli/attest_blob.go +++ b/cmd/cosign/cli/attest_blob.go @@ -15,9 +15,14 @@ package cli import ( + "context" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/attest" "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/internal/ui" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/spf13/cobra" ) @@ -54,6 +59,7 @@ func AttestBlob() *cobra.Command { if err != nil { return err } + ko := options.KeyOpts{ KeyRef: o.Key, PassFunc: generate.GetPass, @@ -79,6 +85,13 @@ func AttestBlob() *cobra.Command { BundlePath: o.BundlePath, NewBundleFormat: o.NewBundleFormat, } + if o.Key == "" && env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" { // Get the trusted root if using fulcio for signing + trustedMaterial, err := cosign.TrustedRoot() + if err != nil { + ui.Warnf(context.Background(), "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + ko.TrustedMaterial = trustedMaterial + } v := attest.AttestBlobCommand{ KeyOpts: ko, CertPath: o.Cert, diff --git a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go index 8646bb298bd..076a763c536 100644 --- a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go +++ b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go @@ -17,12 +17,15 @@ package fulcioverifier import ( "context" + "crypto/x509" "fmt" "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/sigstore-go/pkg/verify" + "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" ) @@ -32,12 +35,31 @@ func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerV return nil, err } - // Grab the PublicKeys for the CTFE, either from tuf or env. + if ko.TrustedMaterial != nil && len(fs.SCT) == 0 { + // Detached SCTs cannot be verified with this function. + chain, err := cryptoutils.UnmarshalCertificatesFromPEM(fs.Chain) + if err != nil { + return nil, fmt.Errorf("unmarshalling cert chain from PEM for SCT verification: %w", err) + } + certs, err := cryptoutils.UnmarshalCertificatesFromPEM(fs.Cert) + if err != nil || len(certs) < 1 { + return nil, fmt.Errorf("unmarshalling cert from PEM for SCT verification: %w", err) + } + chain = append(certs, chain...) + chains := make([][]*x509.Certificate, 1) + chains[0] = chain + if err := verify.VerifySignedCertificateTimestamp(chains, 1, ko.TrustedMaterial); err != nil { + return nil, fmt.Errorf("verifying SCT using trusted root: %w", err) + } + ui.Infof(ctx, "Successfully verified SCT...") + return fs, nil + } + + // There was no trusted_root.json or we need to verify a detached SCT, so grab the PublicKeys for the CTFE, either from tuf or env. pubKeys, err := cosign.GetCTLogPubs(ctx) if err != nil { return nil, fmt.Errorf("getting CTFE public keys: %w", err) } - // verify the sct if err := cosign.VerifySCT(ctx, fs.Cert, fs.Chain, fs.SCT, pubKeys); err != nil { return nil, fmt.Errorf("verifying SCT: %w", err) diff --git a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier_test.go b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier_test.go new file mode 100644 index 00000000000..c0b11006ec1 --- /dev/null +++ b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier_test.go @@ -0,0 +1,419 @@ +// Copyright 2025 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 fulcioverifier + +import ( + "context" + "crypto" + "crypto/ed25519" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "math/big" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + ct "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/tls" + "github.com/google/certificate-transparency-go/trillian/ctfe" + ctx509 "github.com/google/certificate-transparency-go/x509" + ctx509util "github.com/google/certificate-transparency-go/x509util" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/initialize" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/fulcio/pkg/ctl" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/v2/metadata" +) + +func TestNewSigner(t *testing.T) { + td := t.TempDir() + t.Setenv("TUF_ROOT", td) + tufRepo := t.TempDir() + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + skid, err := cryptoutils.SKID(&privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } + caCert := createBaseCert(t, privateKey, skid, big.NewInt(1)) + logID, err := ctfe.GetCTLogID(&privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } + sct := ct.SignedCertificateTimestamp{ + SCTVersion: ct.V1, + Timestamp: 12345, + LogID: ct.LogID{KeyID: logID}, + } + preCert := createBaseCert(t, privateKey, skid, big.NewInt(1)) + if err != nil { + t.Fatal(err) + } + pubBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + t.Fatal(err) + } + pubPEM := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: pubBytes, + }) + err = newTUF(tufRepo, map[string][]byte{"ctfe.pub": pubPEM}) + if err != nil { + t.Fatal(err) + } + tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir(tufRepo)).ServeHTTP(w, r) + })) + err = initialize.DoInitialize(context.Background(), filepath.Join(tufRepo, "1.root.json"), tufServer.URL) + if err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + embeddedSCT bool + trustedMaterial root.TrustedMaterial + }{ + { + name: "detached SCT", + embeddedSCT: false, + trustedMaterial: nil, + }, + { + name: "embedded SCT with legacy TUF metadata", + embeddedSCT: true, + trustedMaterial: nil, + }, + { + name: "embedded SCT with trusted root", + embeddedSCT: true, + trustedMaterial: &fakeTrustedMaterial{ + transparencyLog: map[string]*root.TransparencyLog{ + hex.EncodeToString(logID[:]): { + PublicKey: &privateKey.PublicKey, + }, + }, + cas: []root.CertificateAuthority{ + &root.FulcioCertificateAuthority{ + Root: caCert, + }, + }, + }, + }, + { + name: "detached SCT with trusted root uses legacy TUF client", + embeddedSCT: false, + trustedMaterial: &fakeTrustedMaterial{ + transparencyLog: map[string]*root.TransparencyLog{ + hex.EncodeToString(logID[:]): { + PublicKey: &privateKey.PublicKey, + }, + }, + cas: []root.CertificateAuthority{ + &root.FulcioCertificateAuthority{ + Root: caCert, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + leafCert := preCert + sctHeader := "" + if test.embeddedSCT { + leafCert = embedSCT(t, privateKey, skid, preCert, sct) + } else { + sctHeader = detachedSCT(t, privateKey, preCert, sct) + } + pemChain, _ := cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, caCert}) + testServer := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, _ *http.Request) { + if sctHeader != "" { + w.Header().Set("SCT", sctHeader) + } + w.WriteHeader(http.StatusCreated) + _, _ = w.Write(pemChain) + })) + defer testServer.Close() + + ctx := context.Background() + ko := options.KeyOpts{ + OIDCDisableProviders: true, + // random test token + IDToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + FulcioURL: testServer.URL, + FulcioAuthFlow: "token", + } + privKey, err := cosign.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + } + sv, err := signature.LoadECDSASignerVerifier(privKey, crypto.SHA256) + if err != nil { + t.Fatal(err) + } + + fs, err := NewSigner(ctx, ko, sv) + if test.embeddedSCT { + assert.Empty(t, fs.SCT) + } else { + assert.NotEmpty(t, fs.SCT) + } + assert.NoError(t, err) + }) + } +} + +func getSCT(t *testing.T, privateKey *rsa.PrivateKey, preCert *x509.Certificate, sctInput ct.SignedCertificateTimestamp, embedded bool) ct.SignedCertificateTimestamp { + logEntry := ct.LogEntry{ + Leaf: ct.MerkleTreeLeaf{ + Version: ct.V1, + LeafType: ct.TimestampedEntryLeafType, + TimestampedEntry: &ct.TimestampedEntry{ + Timestamp: sctInput.Timestamp, + }, + }, + } + if embedded { + logEntry.Leaf.TimestampedEntry.EntryType = ct.PrecertLogEntryType + logEntry.Leaf.TimestampedEntry.PrecertEntry = &ct.PreCert{ + IssuerKeyHash: sha256.Sum256(preCert.RawSubjectPublicKeyInfo), + TBSCertificate: preCert.RawTBSCertificate, + } + } else { + logEntry.Leaf.TimestampedEntry.EntryType = ct.X509LogEntryType + logEntry.Leaf.TimestampedEntry.X509Entry = &ct.ASN1Cert{Data: preCert.Raw} + } + data, err := ct.SerializeSCTSignatureInput(sctInput, logEntry) + if err != nil { + t.Fatal(err) + } + h := sha256.Sum256(data) + signature, err := privateKey.Sign(rand.Reader, h[:], crypto.SHA256) + if err != nil { + t.Fatal(err) + } + sct := ct.SignedCertificateTimestamp{ + SCTVersion: sctInput.SCTVersion, + LogID: sctInput.LogID, + Timestamp: sctInput.Timestamp, + Signature: ct.DigitallySigned{ + Algorithm: tls.SignatureAndHashAlgorithm{ + Hash: tls.SHA256, + Signature: tls.RSA, + }, + Signature: signature, + }, + } + return sct +} + +func detachedSCT(t *testing.T, privateKey *rsa.PrivateKey, preCert *x509.Certificate, sctInput ct.SignedCertificateTimestamp) string { + sct := getSCT(t, privateKey, preCert, sctInput, false) + addChainResp, err := ctl.ToAddChainResponse(&sct) + if err != nil { + t.Fatal(err) + } + sctBytes, err := json.Marshal(addChainResp) + if err != nil { + t.Fatal(err) + } + + return base64.StdEncoding.EncodeToString(sctBytes) +} + +func embedSCT(t *testing.T, privateKey *rsa.PrivateKey, skid []byte, preCert *x509.Certificate, sctInput ct.SignedCertificateTimestamp) *x509.Certificate { + sct := getSCT(t, privateKey, preCert, sctInput, true) + sctList, err := ctx509util.MarshalSCTsIntoSCTList([]*ct.SignedCertificateTimestamp{&sct}) + if err != nil { + t.Fatal(err) + } + sctBytes, err := tls.Marshal(*sctList) + if err != nil { + t.Fatal(err) + } + asnSCT, err := asn1.Marshal(sctBytes) + if err != nil { + t.Fatal(err) + } + cert := &x509.Certificate{ + SerialNumber: preCert.SerialNumber, + SubjectKeyId: skid, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier(ctx509.OIDExtensionCTSCT), + Value: asnSCT, + }, + }, + } + certDERBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey) + if err != nil { + t.Fatal(err) + } + parsedCert, err := x509.ParseCertificate(certDERBytes) + if err != nil { + t.Fatal(err) + } + return parsedCert +} + +func newKey() (*metadata.Key, signature.Signer, error) { + pub, private, err := ed25519.GenerateKey(nil) + if err != nil { + return nil, nil, err + } + public, err := metadata.KeyFromPublicKey(pub) + if err != nil { + return nil, nil, err + } + signer, err := signature.LoadSigner(private, crypto.Hash(0)) + if err != nil { + return nil, nil, err + } + return public, signer, nil +} + +func newTUF(td string, targetList map[string][]byte) error { + expiration := time.Now().AddDate(0, 0, 1).UTC() + targets := metadata.Targets(expiration) + targetsDir := filepath.Join(td, "targets") + err := os.Mkdir(targetsDir, 0700) + if err != nil { + return err + } + for name, content := range targetList { + targetPath := filepath.Join(targetsDir, name) + err := os.WriteFile(targetPath, content, 0600) + if err != nil { + return err + } + targetFileInfo, err := metadata.TargetFile().FromFile(targetPath, "sha256") + if err != nil { + return err + } + targets.Signed.Targets[name] = targetFileInfo + } + snapshot := metadata.Snapshot(expiration) + timestamp := metadata.Timestamp(expiration) + root := metadata.Root(expiration) + root.Signed.ConsistentSnapshot = false + public, signer, err := newKey() + if err != nil { + return err + } + for _, name := range []string{"targets", "snapshot", "timestamp", "root"} { + err := root.Signed.AddKey(public, name) + if err != nil { + return err + } + switch name { + case "targets": + _, err = targets.Sign(signer) + case "snapshot": + _, err = snapshot.Sign(signer) + case "timestamp": + _, err = timestamp.Sign(signer) + case "root": + _, err = root.Sign(signer) + } + if err != nil { + return err + } + } + err = targets.ToFile(filepath.Join(td, "targets.json"), false) + if err != nil { + return err + } + err = snapshot.ToFile(filepath.Join(td, "snapshot.json"), false) + if err != nil { + return err + } + err = timestamp.ToFile(filepath.Join(td, "timestamp.json"), false) + if err != nil { + return err + } + err = root.ToFile(filepath.Join(td, "1.root.json"), false) + if err != nil { + return err + } + err = root.VerifyDelegate("root", root) + if err != nil { + return err + } + err = root.VerifyDelegate("targets", targets) + if err != nil { + return err + } + err = root.VerifyDelegate("snapshot", snapshot) + if err != nil { + return err + } + err = root.VerifyDelegate("timestamp", timestamp) + return err +} + +func createBaseCert(t *testing.T, privateKey *rsa.PrivateKey, skid []byte, serialNumber *big.Int) *x509.Certificate { + cert := &x509.Certificate{ + SerialNumber: serialNumber, + SubjectKeyId: skid, + } + certDERBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey) + if err != nil { + t.Fatal(err) + } + parsedCert, err := x509.ParseCertificate(certDERBytes) + if err != nil { + t.Fatal(err) + } + return parsedCert +} + +type fakeTrustedMaterial struct { + transparencyLog map[string]*root.TransparencyLog + cas []root.CertificateAuthority +} + +func (t *fakeTrustedMaterial) CTLogs() map[string]*root.TransparencyLog { + return t.transparencyLog +} + +func (t *fakeTrustedMaterial) FulcioCertificateAuthorities() []root.CertificateAuthority { + return t.cas +} + +func (t *fakeTrustedMaterial) TimestampingAuthorities() []root.TimestampingAuthority { + panic("not implemented") +} +func (t *fakeTrustedMaterial) RekorLogs() map[string]*root.TransparencyLog { panic("not implemented") } +func (t *fakeTrustedMaterial) PublicKeyVerifier(string) (root.TimeConstrainedVerifier, error) { + panic("not implemented") +} diff --git a/cmd/cosign/cli/initialize/init.go b/cmd/cosign/cli/initialize/init.go index eca80ea15ea..fc726cd0d59 100644 --- a/cmd/cosign/cli/initialize/init.go +++ b/cmd/cosign/cli/initialize/init.go @@ -21,11 +21,16 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strings" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/blob" - "github.com/sigstore/sigstore/pkg/tuf" + "github.com/sigstore/cosign/v2/pkg/cosign/env" + tufroot "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/tuf" + tufv1 "github.com/sigstore/sigstore/pkg/tuf" ) func DoInitialize(ctx context.Context, root, mirror string) error { @@ -57,11 +62,44 @@ func doInitialize(ctx context.Context, root, mirror, rootChecksum string, forceS } } - if err := tuf.Initialize(ctx, mirror, rootFileBytes); err != nil { + opts := tuf.DefaultOptions() + if root != "" { + opts.Root = rootFileBytes + } + if mirror != "" { + opts.RepositoryBaseURL = mirror + } + if tufCacheDir := env.Getenv(env.VariableTUFRootDir); tufCacheDir != "" { //nolint:forbidigo + opts.CachePath = tufCacheDir + } + + // Leave a hint for where the current remote is. Adopted from sigstore/sigstore TUF client. + remote := map[string]string{"mirror": opts.RepositoryBaseURL} + remoteBytes, err := json.Marshal(remote) + if err != nil { + return err + } + if err := os.MkdirAll(opts.CachePath, 0o700); err != nil { + return fmt.Errorf("creating cache directory: %w", err) + } + if err := os.WriteFile(filepath.FromSlash(filepath.Join(opts.CachePath, "remote.json")), remoteBytes, 0o600); err != nil { + return fmt.Errorf("storing remote: %w", err) + } + + trustedRoot, err := tufroot.NewLiveTrustedRoot(opts) + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF mirror (encountered error: %v), falling back to individual targets. It is recommended to update your TUF metadata repository to include trusted_root.json.", err) + } + if trustedRoot != nil { + return nil + } + + // The mirror did not have a trusted_root.json, so initialize the legacy TUF targets. + if err := tufv1.Initialize(ctx, mirror, rootFileBytes); err != nil { return err } - status, err := tuf.GetRootStatus(ctx) + status, err := tufv1.GetRootStatus(ctx) if err != nil { return err } diff --git a/cmd/cosign/cli/initialize/init_test.go b/cmd/cosign/cli/initialize/init_test.go new file mode 100644 index 00000000000..73def460a22 --- /dev/null +++ b/cmd/cosign/cli/initialize/init_test.go @@ -0,0 +1,226 @@ +// Copyright 2025 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 initialize + +import ( + "context" + "crypto" + "crypto/ed25519" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/sigstore/sigstore/pkg/signature" + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/v2/metadata" +) + +func newKey() (*metadata.Key, signature.Signer, error) { + pub, private, err := ed25519.GenerateKey(nil) + if err != nil { + return nil, nil, err + } + public, err := metadata.KeyFromPublicKey(pub) + if err != nil { + return nil, nil, err + } + signer, err := signature.LoadSigner(private, crypto.Hash(0)) + if err != nil { + return nil, nil, err + } + return public, signer, nil +} + +func newTUF(td string, targetList map[string][]byte) error { + expiration := time.Now().AddDate(0, 0, 1).UTC() + targets := metadata.Targets(expiration) + targetsDir := filepath.Join(td, "targets") + err := os.Mkdir(targetsDir, 0700) + if err != nil { + return err + } + for name, content := range targetList { + targetPath := filepath.Join(targetsDir, name) + err := os.WriteFile(targetPath, content, 0600) + if err != nil { + return err + } + targetFileInfo, err := metadata.TargetFile().FromFile(targetPath, "sha256") + if err != nil { + return err + } + targets.Signed.Targets[name] = targetFileInfo + } + snapshot := metadata.Snapshot(expiration) + timestamp := metadata.Timestamp(expiration) + root := metadata.Root(expiration) + root.Signed.ConsistentSnapshot = false + public, signer, err := newKey() + if err != nil { + return err + } + for _, name := range []string{"targets", "snapshot", "timestamp", "root"} { + err := root.Signed.AddKey(public, name) + if err != nil { + return err + } + switch name { + case "targets": + _, err = targets.Sign(signer) + case "snapshot": + _, err = snapshot.Sign(signer) + case "timestamp": + _, err = timestamp.Sign(signer) + case "root": + _, err = root.Sign(signer) + } + if err != nil { + return err + } + } + err = targets.ToFile(filepath.Join(td, "targets.json"), false) + if err != nil { + return err + } + err = snapshot.ToFile(filepath.Join(td, "snapshot.json"), false) + if err != nil { + return err + } + err = timestamp.ToFile(filepath.Join(td, "timestamp.json"), false) + if err != nil { + return err + } + err = root.ToFile(filepath.Join(td, "1.root.json"), false) + if err != nil { + return err + } + err = root.VerifyDelegate("root", root) + if err != nil { + return err + } + err = root.VerifyDelegate("targets", targets) + if err != nil { + return err + } + err = root.VerifyDelegate("snapshot", snapshot) + if err != nil { + return err + } + err = root.VerifyDelegate("timestamp", timestamp) + return err +} + +func captureOutput(f func() error) (string, string, error) { + stdout := os.Stdout + stderr := os.Stderr + rout, wout, _ := os.Pipe() + os.Stdout = wout + rerr, werr, _ := os.Pipe() + os.Stderr = werr + err := f() + os.Stdout = stdout + os.Stderr = stderr + wout.Close() + werr.Close() + out, _ := io.ReadAll(rout) + errMsg, _ := io.ReadAll(rerr) + return string(out), string(errMsg), err +} + +func TestDoInitialize(t *testing.T) { + tests := []struct { + name string + targets map[string][]byte + root string + wantStdOut string + wantStdErr string + wantErr bool + wantFiles []string + expectV2 bool + }{ + { + name: "tuf v2 with trusted root", + targets: map[string][]byte{"trusted_root.json": []byte(`{"mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1"}`)}, + root: "1.root.json", + wantStdOut: "", + wantStdErr: "", + wantErr: false, + wantFiles: []string{filepath.Join("targets", "trusted_root.json")}, + expectV2: true, + }, + { + name: "tuf v1", + targets: map[string][]byte{"ctfe.pub": []byte(`-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----`)}, + root: "1.root.json", + wantStdOut: "ctfe.pub", + wantStdErr: "WARNING: Could not fetch trusted_root.json from the TUF mirror (encountered error: getting info for target \"trusted_root.json\": target trusted_root.json not found), falling back to individual targets. It is recommended to update your TUF metadata repository to include trusted_root.json.", + wantErr: false, + wantFiles: []string{filepath.Join("targets", "ctfe.pub")}, + expectV2: false, + }, + { + name: "invalid root - should not try to use embedded", + wantErr: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + tufRepo := t.TempDir() + err := newTUF(tufRepo, test.targets) + if err != nil { + t.Fatal(err) + } + tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir(tufRepo)).ServeHTTP(w, r) + })) + rootJSONPath := filepath.Join(tufRepo, test.root) + tufCache := t.TempDir() + t.Setenv("TUF_ROOT", tufCache) + gotStdOut, gotStdErr, gotErr := captureOutput(func() error { + return DoInitialize(context.Background(), rootJSONPath, tufServer.URL) + }) + if test.wantErr { + assert.Error(t, gotErr) + return + } + assert.NoError(t, gotErr) + if test.wantStdOut == "" { + assert.Empty(t, gotStdOut) + } else { + assert.Contains(t, gotStdOut, test.wantStdOut) + } + if test.wantStdErr == "" { + assert.Empty(t, gotStdErr) + } else { + assert.Contains(t, gotStdErr, test.wantStdErr) + } + var mirrorDir string + if test.expectV2 { + mirrorDir = tufServer.URL + mirrorDir, _ = strings.CutPrefix(mirrorDir, "http://") + mirrorDir = strings.ReplaceAll(mirrorDir, "/", "-") + mirrorDir = strings.ReplaceAll(mirrorDir, ":", "-") + } + for _, f := range test.wantFiles { + assert.FileExists(t, filepath.Join(tufCache, mirrorDir, f)) + } + assert.FileExists(t, filepath.Join(tufCache, "remote.json")) + }) + } +} diff --git a/cmd/cosign/cli/options/attest.go b/cmd/cosign/cli/options/attest.go index d71e986680a..7b67e4ee563 100644 --- a/cmd/cosign/cli/options/attest.go +++ b/cmd/cosign/cli/options/attest.go @@ -74,8 +74,7 @@ func (o *AttestOptions) AddFlags(cmd *cobra.Command) { _ = cmd.MarkFlagFilename("certificate-chain", certificateExts...) cmd.Flags().BoolVar(&o.NoUpload, "no-upload", false, - "do not upload the generated attestation") - + "do not upload the generated attestation, but send the attestation output to STDOUT") cmd.Flags().BoolVarP(&o.Replace, "replace", "", false, "") diff --git a/cmd/cosign/cli/options/key.go b/cmd/cosign/cli/options/key.go index 634911fdb59..2cae8d5cdbe 100644 --- a/cmd/cosign/cli/options/key.go +++ b/cmd/cosign/cli/options/key.go @@ -15,7 +15,10 @@ package options -import "github.com/sigstore/cosign/v2/pkg/cosign" +import ( + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/sigstore-go/pkg/root" +) type KeyOpts struct { Sk bool @@ -53,4 +56,7 @@ type KeyOpts struct { // Modeled after InsecureSkipVerify in tls.Config, this disables // verifying the SCT. InsecureSkipFulcioVerify bool + + // TrustedMaterial contains trusted metadata for all Sigstore services. It is exclusive with RekorPubKeys, RootCerts, IntermediateCerts, CTLogPubKeys, and the TSA* cert fields. + TrustedMaterial root.TrustedMaterial } diff --git a/cmd/cosign/cli/options/trustedroot.go b/cmd/cosign/cli/options/trustedroot.go index 21561aa527a..d82730cd65a 100644 --- a/cmd/cosign/cli/options/trustedroot.go +++ b/cmd/cosign/cli/options/trustedroot.go @@ -21,12 +21,16 @@ import ( type TrustedRootCreateOptions struct { CertChain []string + FulcioURI []string CtfeKeyPath []string CtfeStartTime []string + CtfeURL []string Out string RekorKeyPath []string RekorStartTime []string + RekorURL []string TSACertChainPath []string + TSAURI []string } var _ Interface = (*TrustedRootCreateOptions)(nil) @@ -39,6 +43,9 @@ func (o *TrustedRootCreateOptions) AddFlags(cmd *cobra.Command) { "signing certificate and end with the root certificate.") _ = cmd.MarkFlagFilename("certificate-chain", certificateExts...) + cmd.Flags().StringArrayVar(&o.FulcioURI, "fulcio-uri", nil, + "URI of the Fulcio server issuing certificates.") + cmd.Flags().StringArrayVar(&o.CtfeKeyPath, "ctfe-key", nil, "path to a PEM-encoded public key used by certificate authority for "+ "certificate transparency log.") @@ -48,19 +55,30 @@ func (o *TrustedRootCreateOptions) AddFlags(cmd *cobra.Command) { "RFC 3339 string describing validity start time for key use by "+ "certificate transparency log.") + cmd.Flags().StringArrayVar(&o.CtfeURL, "ctfe-url", nil, + "URL of the certificate transparency log.") + cmd.Flags().StringVar(&o.Out, "out", "", "path to output trusted root") // _ = cmd.MarkFlagFilename("output") // no typical extensions cmd.Flags().StringArrayVar(&o.RekorKeyPath, "rekor-key", nil, - "path to a PEM-encoded public key used by transparency log like Rekor.") + "path to a PEM-encoded public key used by transparency log like Rekor. "+ + "For Rekor V2, append the Rekor server name with ',', e.g. "+ + "'--rekor-key=/path/to/key.pub,rekor.example.test'.") _ = cmd.MarkFlagFilename("rekor-key", publicKeyExts...) cmd.Flags().StringArrayVar(&o.RekorStartTime, "rekor-start-time", nil, "RFC 3339 string describing validity start time for key use by "+ "transparency log like Rekor.") + cmd.Flags().StringArrayVar(&o.RekorURL, "rekor-url", nil, + "URL of the transparency log.") + cmd.Flags().StringArrayVar(&o.TSACertChainPath, "timestamp-certificate-chain", nil, "path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. "+ "Optionally may contain intermediate CA certificates") _ = cmd.MarkFlagFilename("timestamp-certificate-chain", certificateExts...) + + cmd.Flags().StringArrayVar(&o.TSAURI, "timestamp-uri", nil, + "URI of the timestamp authority server.") } diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index e21fc6f7623..24a659bc828 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -16,12 +16,16 @@ package cli import ( + "context" "fmt" "os" "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/internal/ui" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/spf13/cobra" ) @@ -102,6 +106,7 @@ race conditions or (worse) malicious tampering. if err != nil { return err } + ko := options.KeyOpts{ KeyRef: o.Key, PassFunc: generate.GetPass, @@ -126,6 +131,13 @@ race conditions or (worse) malicious tampering. TSAServerURL: o.TSAServerURL, IssueCertificateForExistingKey: o.IssueCertificate, } + if (o.Key == "" || o.IssueCertificate) && env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" { + trustedMaterial, err := cosign.TrustedRoot() + if err != nil { + ui.Warnf(context.Background(), "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + ko.TrustedMaterial = trustedMaterial + } if err := sign.SignCmd(ro, ko, *o, args); err != nil { if o.Attachment == "" { return fmt.Errorf("signing %v: %w", args, err) diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 8cca5d40d25..a6ee88122d1 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -497,23 +497,6 @@ func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef strin if _, err := cosign.TrustedCert(leafCert, rootPool, subPool); err != nil { return nil, fmt.Errorf("unable to validate certificate chain: %w", err) } - // Verify SCT if present in the leaf certificate. - contains, err := cosign.ContainsSCT(leafCert.Raw) - if err != nil { - return nil, err - } - if contains { - pubKeys, err := cosign.GetCTLogPubs(ctx) - if err != nil { - return nil, fmt.Errorf("getting CTLog public keys: %w", err) - } - var chain []*x509.Certificate - chain = append(chain, leafCert) - chain = append(chain, certChain...) - if err := cosign.VerifyEmbeddedSCT(context.Background(), chain, pubKeys); err != nil { - return nil, err - } - } certSigner.Chain = certChainBytes return certSigner, nil @@ -622,9 +605,6 @@ func fetchLocalSignedPayload(sig oci.Signature) (*cosign.LocalSignedPayload, err } if sigCert != nil { signedPayload.Cert = base64.StdEncoding.EncodeToString(sigCert.Raw) - if err != nil { - return nil, err - } } else { signedPayload.Cert = "" } diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index 5a7b960b927..c01e7952044 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -45,7 +45,6 @@ import ( // nolint func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string, b64 bool, outputSignature string, outputCertificate string, tlogUpload bool) ([]byte, error) { var payload internal.HashReader - var err error ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() @@ -55,15 +54,12 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string } else { ui.Infof(ctx, "Using payload from: %s", payloadPath) f, err := os.Open(filepath.Clean(payloadPath)) - defer f.Close() if err != nil { return nil, err } + defer f.Close() payload = internal.NewHashReader(f, sha256.New()) } - if err != nil { - return nil, err - } sv, err := SignerFromKeyOpts(ctx, "", "", ko) if err != nil { diff --git a/cmd/cosign/cli/signblob.go b/cmd/cosign/cli/signblob.go index e1a2cbf8b0e..8909b9f90ac 100644 --- a/cmd/cosign/cli/signblob.go +++ b/cmd/cosign/cli/signblob.go @@ -16,12 +16,16 @@ package cli import ( + "context" "fmt" "os" "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/internal/ui" + "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -68,6 +72,7 @@ func SignBlob() *cobra.Command { if err != nil { return err } + ko := options.KeyOpts{ KeyRef: o.Key, PassFunc: generate.GetPass, @@ -94,6 +99,13 @@ func SignBlob() *cobra.Command { RFC3161TimestampPath: o.RFC3161TimestampPath, IssueCertificateForExistingKey: o.IssueCertificate, } + if (o.Key == "" || o.IssueCertificate) && env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" { + trustedMaterial, err := cosign.TrustedRoot() + if err != nil { + ui.Warnf(context.Background(), "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + ko.TrustedMaterial = trustedMaterial + } for _, blob := range args { // TODO: remove when the output flag has been deprecated diff --git a/cmd/cosign/cli/trustedroot.go b/cmd/cosign/cli/trustedroot.go index 7dfd43f18e5..cb96cabbd4f 100644 --- a/cmd/cosign/cli/trustedroot.go +++ b/cmd/cosign/cli/trustedroot.go @@ -45,12 +45,16 @@ func trustedRootCreate() *cobra.Command { RunE: func(cmd *cobra.Command, _ []string) error { trCreateCmd := &trustedroot.CreateCmd{ CertChain: o.CertChain, + FulcioURI: o.FulcioURI, CtfeKeyPath: o.CtfeKeyPath, CtfeStartTime: o.CtfeStartTime, + CtfeURL: o.CtfeURL, Out: o.Out, RekorKeyPath: o.RekorKeyPath, RekorStartTime: o.RekorStartTime, + RekorURL: o.RekorURL, TSACertChainPath: o.TSACertChainPath, + TSAURI: o.TSAURI, } ctx, cancel := context.WithTimeout(cmd.Context(), ro.Timeout) diff --git a/cmd/cosign/cli/trustedroot/trustedroot.go b/cmd/cosign/cli/trustedroot/trustedroot.go index 794c932084c..8e11f44de47 100644 --- a/cmd/cosign/cli/trustedroot/trustedroot.go +++ b/cmd/cosign/cli/trustedroot/trustedroot.go @@ -23,21 +23,27 @@ import ( "encoding/pem" "fmt" "os" + "strings" "time" "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/rekor-tiles/pkg/note" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/cryptoutils" ) type CreateCmd struct { CertChain []string + FulcioURI []string CtfeKeyPath []string CtfeStartTime []string + CtfeURL []string Out string RekorKeyPath []string RekorStartTime []string + RekorURL []string TSACertChainPath []string + TSAURI []string } func (c *CreateCmd) Exec(_ context.Context) error { @@ -47,7 +53,11 @@ func (c *CreateCmd) Exec(_ context.Context) error { rekorTransparencyLogs := make(map[string]*root.TransparencyLog) for i := 0; i < len(c.CertChain); i++ { - fulcioAuthority, err := parseCAPEMFile(c.CertChain[i]) + var fulcioURI string + if i < len(c.FulcioURI) { + fulcioURI = c.FulcioURI[i] + } + fulcioAuthority, err := parseCAPEMFile(c.CertChain[i], fulcioURI) if err != nil { return err } @@ -76,13 +86,29 @@ func (c *CreateCmd) Exec(_ context.Context) error { PublicKey: *ctLogPubKey, SignatureHashFunc: crypto.SHA256, } + + if i < len(c.CtfeURL) { + ctLogs[id].BaseURL = c.CtfeURL[i] + } } for i := 0; i < len(c.RekorKeyPath); i++ { - tlogPubKey, id, idBytes, err := getPubKey(c.RekorKeyPath[i]) + keyParts := strings.SplitN(c.RekorKeyPath[i], ",", 2) + keyPath := keyParts[0] + tlogPubKey, id, idBytes, err := getPubKey(keyPath) if err != nil { return err } + var origin string + if len(keyParts) > 1 { + origin = keyParts[1] + } + if origin != "" { + id, idBytes, err = getCheckpointID(origin, *tlogPubKey) + if err != nil { + return err + } + } startTime := time.Unix(0, 0) @@ -100,10 +126,18 @@ func (c *CreateCmd) Exec(_ context.Context) error { PublicKey: *tlogPubKey, SignatureHashFunc: crypto.SHA256, } + + if i < len(c.RekorURL) { + rekorTransparencyLogs[id].BaseURL = c.RekorURL[i] + } } for i := 0; i < len(c.TSACertChainPath); i++ { - timestampAuthority, err := parseTAPEMFile(c.TSACertChainPath[i]) + var tsaURI string + if i < len(c.TSAURI) { + tsaURI = c.TSAURI[i] + } + timestampAuthority, err := parseTAPEMFile(c.TSACertChainPath[i], tsaURI) if err != nil { return err } @@ -137,7 +171,7 @@ func (c *CreateCmd) Exec(_ context.Context) error { return nil } -func parseCAPEMFile(path string) (root.CertificateAuthority, error) { +func parseCAPEMFile(path, uri string) (root.CertificateAuthority, error) { certs, err := parseCerts(path) if err != nil { return nil, err @@ -149,11 +183,12 @@ func parseCAPEMFile(path string) (root.CertificateAuthority, error) { if len(certs) > 1 { ca.Intermediates = certs[:len(certs)-1] } + ca.URI = uri return &ca, nil } -func parseTAPEMFile(path string) (root.TimestampingAuthority, error) { +func parseTAPEMFile(path, uri string) (root.TimestampingAuthority, error) { certs, err := parseCerts(path) if err != nil { return nil, err @@ -165,6 +200,7 @@ func parseTAPEMFile(path string) (root.TimestampingAuthority, error) { if len(certs) > 1 { ta.Intermediates = certs[:len(certs)-1] } + ta.URI = uri return &ta, nil } @@ -219,3 +255,11 @@ func getPubKey(path string) (*crypto.PublicKey, string, []byte, error) { return &pubKey, keyID, idBytes, nil } + +func getCheckpointID(origin string, key crypto.PublicKey) (string, []byte, error) { + _, id, err := note.KeyHash(origin, key) + if err != nil { + return "", nil, err + } + return hex.EncodeToString(id), id, nil +} diff --git a/cmd/cosign/cli/trustedroot/trustedroot_test.go b/cmd/cosign/cli/trustedroot/trustedroot_test.go index d681e24abe6..8ec7d5dda3d 100644 --- a/cmd/cosign/cli/trustedroot/trustedroot_test.go +++ b/cmd/cosign/cli/trustedroot/trustedroot_test.go @@ -17,6 +17,7 @@ package trustedroot import ( "context" + "crypto/ed25519" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -41,12 +42,21 @@ func TestCreateCmd(t *testing.T) { tsaChainPath := filepath.Join(td, "timestamp.pem") makeChain(t, tsaChainPath, 3) + rekorV1KeyPath := filepath.Join(td, "rekor.v1.pub") + makeKey(t, rekorV1KeyPath) + rekorV2KeyPath := filepath.Join(td, "rekor.v2.pub") + makeKey(t, rekorV2KeyPath) + outPath := filepath.Join(td, "trustedroot.json") trustedrootCreate := CreateCmd{ CertChain: []string{fulcioChainPath}, + FulcioURI: []string{"https://fulcio.sigstore.example"}, + RekorURL: []string{"https://rekor.sigstore.example"}, + RekorKeyPath: []string{rekorV1KeyPath, rekorV2KeyPath + ",rekor.sigstore.example"}, Out: outPath, TSACertChainPath: []string{tsaChainPath}, + TSAURI: []string{"https://tsa.sigstore.example"}, } err := trustedrootCreate.Exec(ctx) @@ -126,6 +136,19 @@ func makeChain(t *testing.T, path string, size int) { checkErr(t, err) } +func makeKey(t *testing.T, path string) { + fd, err := os.Create(path) + checkErr(t, err) + defer fd.Close() + + pub, _, err := ed25519.GenerateKey(rand.Reader) + checkErr(t, err) + derBytes, err := x509.MarshalPKIXPublicKey(pub) + checkErr(t, err) + err = pem.Encode(fd, &pem.Block{Type: "PUBLIC KEY", Bytes: derBytes}) + checkErr(t, err) +} + func checkErr(t *testing.T, err error) { if err != nil { t.Fatal(err) diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index 9d79846d393..01a3ea60067 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -37,6 +37,7 @@ import ( "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/sigstore/cosign/v2/pkg/cosign/pivkey" "github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/v2/pkg/oci" @@ -144,14 +145,24 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return fmt.Errorf("loading trusted root: %w", err) } + } else if options.NOf(c.CertChain, c.CARoots, c.CAIntermediates, c.TSACertChainPath) == 0 && + env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" && + env.Getenv(env.VariableSigstoreRootFile) == "" && + env.Getenv(env.VariableSigstoreRekorPublicKey) == "" && + env.Getenv(env.VariableSigstoreTSACertificateFile) == "" { + // don't overrule the user's intentions if they provided their own keys + co.TrustedMaterial, err = cosign.TrustedRoot() + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } } if c.CheckClaims { co.ClaimVerifier = cosign.SimpleClaimVerifier } - // If we are using signed timestamps, we need to load the TSA certificates - if co.UseSignedTimestamps { + // If we are using signed timestamps and there is no trusted root, we need to load the TSA certificates + if co.UseSignedTimestamps && co.TrustedMaterial == nil { tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) if err != nil { return fmt.Errorf("unable to load TSA certificates: %w", err) @@ -169,14 +180,16 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { } co.RekorClient = rekorClient } - // This performs an online fetch of the Rekor public keys, but this is needed - // for verifying tlog entries (both online and offline). - co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) - if err != nil { - return fmt.Errorf("getting Rekor public keys: %w", err) + if co.TrustedMaterial == nil { + // This performs an online fetch of the Rekor public keys, but this is needed + // for verifying tlog entries (both online and offline). + co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) + if err != nil { + return fmt.Errorf("getting Rekor public keys: %w", err) + } } } - if keylessVerification(c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && keylessVerification(c.KeyRef, c.Sk) { if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { return err } @@ -186,7 +199,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { certRef := c.CertRef // Ignore Signed Certificate Timestamp if the flag is set or a key is provided - if shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx) if err != nil { return fmt.Errorf("getting ctlog public keys: %w", err) @@ -223,13 +236,15 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { switch { case c.CertChain == "" && co.RootCerts == nil: // If no certChain and no CARoots are passed, the Fulcio root certificate will be used - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) + if co.TrustedMaterial == nil { + co.RootCerts, err = fulcio.GetRoots() + if err != nil { + return fmt.Errorf("getting Fulcio roots: %w", err) + } + co.IntermediateCerts, err = fulcio.GetIntermediates() + if err != nil { + return fmt.Errorf("getting Fulcio intermediates: %w", err) + } } pubKey, err = cosign.ValidateAndUnpackCert(cert, co) if err != nil { diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index 935d5036552..66308ad2271 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -31,6 +31,7 @@ import ( "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/cue" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/sigstore/cosign/v2/pkg/cosign/pivkey" "github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/v2/pkg/cosign/rego" @@ -98,6 +99,21 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e return fmt.Errorf("constructing client options: %w", err) } + trustedMaterial, err := cosign.TrustedRoot() + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + + if options.NOf(c.CertChain, c.CARoots, c.CAIntermediates, c.TSACertChainPath) > 0 || + env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) != "" || + env.Getenv(env.VariableSigstoreRootFile) != "" || + env.Getenv(env.VariableSigstoreRekorPublicKey) != "" || + env.Getenv(env.VariableSigstoreTSACertificateFile) != "" { + // trusted_root.json was found, but a cert chain was explicitly provided, or environment variables point to the key material, + // so don't overrule the user's intentions. + trustedMaterial = nil + } + co := &cosign.CheckOpts{ RegistryClientOpts: ociremoteOpts, CertGithubWorkflowTrigger: c.CertGithubWorkflowTrigger, @@ -112,23 +128,39 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e MaxWorkers: c.MaxWorkers, UseSignedTimestamps: c.TSACertChainPath != "" || c.UseSignedTimestamps, NewBundleFormat: c.NewBundleFormat, + TrustedMaterial: trustedMaterial, } if c.CheckClaims { co.ClaimVerifier = cosign.IntotoSubjectClaimVerifier } + if c.TrustedRootPath != "" { + co.TrustedMaterial, err = root.NewTrustedRootFromPath(c.TrustedRootPath) + if err != nil { + return fmt.Errorf("loading trusted root: %w", err) + } + } else if options.NOf(c.CertChain, c.CARoots, c.CAIntermediates, c.TSACertChainPath) == 0 && + env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" && + env.Getenv(env.VariableSigstoreRootFile) == "" && + env.Getenv(env.VariableSigstoreRekorPublicKey) == "" && + env.Getenv(env.VariableSigstoreTSACertificateFile) == "" { + co.TrustedMaterial, err = cosign.TrustedRoot() + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + } + if c.NewBundleFormat { if err = checkSigstoreBundleUnsupportedOptions(c); err != nil { return err } - co.TrustedMaterial, err = loadTrustedRoot(ctx, c.TrustedRootPath) - if err != nil { - return err + if co.TrustedMaterial == nil { + return fmt.Errorf("trusted root is required when using new bundle format") } } // Ignore Signed Certificate Timestamp if the flag is set or a key is provided - if shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) && !c.NewBundleFormat { + if co.TrustedMaterial == nil && shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) && !c.NewBundleFormat { co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx) if err != nil { return fmt.Errorf("getting ctlog public keys: %w", err) @@ -136,7 +168,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e } // If we are using signed timestamps, we need to load the TSA certificates - if co.UseSignedTimestamps && !c.NewBundleFormat { + if co.UseSignedTimestamps && co.TrustedMaterial == nil && !c.NewBundleFormat { tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) if err != nil { return fmt.Errorf("unable to load TSA certificates: %w", err) @@ -154,15 +186,17 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e } co.RekorClient = rekorClient } - // This performs an online fetch of the Rekor public keys, but this is needed - // for verifying tlog entries (both online and offline). - co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) - if err != nil { - return fmt.Errorf("getting Rekor public keys: %w", err) + if co.TrustedMaterial == nil { + // This performs an online fetch of the Rekor public keys, but this is needed + // for verifying tlog entries (both online and offline). + co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) + if err != nil { + return fmt.Errorf("getting Rekor public keys: %w", err) + } } } - if keylessVerification(c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && keylessVerification(c.KeyRef, c.Sk) { if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { return err } @@ -202,13 +236,15 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e } if c.CertChain == "" { // If no certChain is passed, the Fulcio root certificate will be used - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) + if co.TrustedMaterial == nil { + co.RootCerts, err = fulcio.GetRoots() + if err != nil { + return fmt.Errorf("getting Fulcio roots: %w", err) + } + co.IntermediateCerts, err = fulcio.GetIntermediates() + if err != nil { + return fmt.Errorf("getting Fulcio intermediates: %w", err) + } } co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) if err != nil { diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index 25932f43f87..926ad71fe48 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -37,6 +37,7 @@ import ( "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/bundle" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/sigstore/cosign/v2/pkg/cosign/pivkey" "github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/v2/pkg/oci/static" @@ -140,16 +141,29 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } } + if c.TrustedRootPath != "" { + co.TrustedMaterial, err = root.NewTrustedRootFromPath(c.TrustedRootPath) + if err != nil { + return fmt.Errorf("loading trusted root: %w", err) + } + } else if options.NOf(c.CertChain, c.CARoots, c.CAIntermediates, c.TSACertChainPath) == 0 && + env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" && + env.Getenv(env.VariableSigstoreRootFile) == "" && + env.Getenv(env.VariableSigstoreRekorPublicKey) == "" && + env.Getenv(env.VariableSigstoreTSACertificateFile) == "" { + co.TrustedMaterial, err = cosign.TrustedRoot() + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + } + if co.NewBundleFormat { if options.NOf(c.RFC3161TimestampPath, c.TSACertChainPath, c.CertChain, c.CARoots, c.CAIntermediates, c.CertRef, c.SigRef, c.SCTRef) > 0 { return fmt.Errorf("when using --new-bundle-format, please supply signed content with --bundle and verification content with --trusted-root") } if co.TrustedMaterial == nil { - co.TrustedMaterial, err = loadTrustedRoot(ctx, c.TrustedRootPath) - if err != nil { - return err - } + return fmt.Errorf("trusted root is required when using new bundle format") } bundle, err := sgbundle.LoadJSONFromPath(c.BundlePath) @@ -191,7 +205,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } else if c.RFC3161TimestampPath == "" && co.UseSignedTimestamps { return fmt.Errorf("when specifying --use-signed-timestamps or --timestamp-certificate-chain, you must also specify --rfc3161-timestamp-path") } - if co.UseSignedTimestamps { + if co.UseSignedTimestamps && co.TrustedMaterial == nil { tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) if err != nil { return fmt.Errorf("unable to load TSA certificates: %w", err) @@ -209,15 +223,17 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } co.RekorClient = rekorClient } - // This performs an online fetch of the Rekor public keys, but this is needed - // for verifying tlog entries (both online and offline). - co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) - if err != nil { - return fmt.Errorf("getting Rekor public keys: %w", err) + if co.TrustedMaterial == nil { + // This performs an online fetch of the Rekor public keys, but this is needed + // for verifying tlog entries (both online and offline). + co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) + if err != nil { + return fmt.Errorf("getting Rekor public keys: %w", err) + } } } - if keylessVerification(c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && keylessVerification(c.KeyRef, c.Sk) { if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { return err } @@ -315,7 +331,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } // Ignore Signed Certificate Timestamp if the flag is set or a key is provided - if shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx) if err != nil { return fmt.Errorf("getting ctlog public keys: %w", err) @@ -393,12 +409,3 @@ func payloadDigest(blobRef string) (string, []byte, error) { } return hexAlg, digestBytes, nil } - -func loadTrustedRoot(_ context.Context, trustedRootPath string) (*root.TrustedRoot, error) { - if trustedRootPath != "" { - return root.NewTrustedRootFromPath(trustedRootPath) - } - // Assume we're using public good instance; fetch via TUF - // TODO: allow custom TUF settings - return root.FetchTrustedRoot() -} diff --git a/cmd/cosign/cli/verify/verify_blob_attestation.go b/cmd/cosign/cli/verify/verify_blob_attestation.go index e037f38d77f..60b4ece5c45 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation.go @@ -38,12 +38,14 @@ import ( "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/bundle" + "github.com/sigstore/cosign/v2/pkg/cosign/env" "github.com/sigstore/cosign/v2/pkg/cosign/pivkey" "github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/v2/pkg/oci/static" "github.com/sigstore/cosign/v2/pkg/policy" sigs "github.com/sigstore/cosign/v2/pkg/signature" sgbundle "github.com/sigstore/sigstore-go/pkg/bundle" + "github.com/sigstore/sigstore-go/pkg/root" sgverify "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" ) @@ -181,16 +183,29 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st co.ClaimVerifier = cosign.IntotoSubjectClaimVerifier } + if c.TrustedRootPath != "" { + co.TrustedMaterial, err = root.NewTrustedRootFromPath(c.TrustedRootPath) + if err != nil { + return fmt.Errorf("loading trusted root: %w", err) + } + } else if options.NOf(c.CertChain, c.CARoots, c.CAIntermediates, c.TSACertChainPath) == 0 && + env.Getenv(env.VariableSigstoreCTLogPublicKeyFile) == "" && + env.Getenv(env.VariableSigstoreRootFile) == "" && + env.Getenv(env.VariableSigstoreRekorPublicKey) == "" && + env.Getenv(env.VariableSigstoreTSACertificateFile) == "" { + co.TrustedMaterial, err = cosign.TrustedRoot() + if err != nil { + ui.Warnf(ctx, "Could not fetch trusted_root.json from the TUF repository. Continuing with individual targets. Error from TUF: %v", err) + } + } + if co.NewBundleFormat { if options.NOf(c.RFC3161TimestampPath, c.TSACertChainPath, c.CertChain, c.CARoots, c.CAIntermediates, c.CertRef, c.SCTRef) > 0 { return fmt.Errorf("when using --new-bundle-format, please supply signed content with --bundle and verification content with --trusted-root") } if co.TrustedMaterial == nil { - co.TrustedMaterial, err = loadTrustedRoot(ctx, c.TrustedRootPath) - if err != nil { - return err - } + return fmt.Errorf("trusted root is required when using new bundle format") } bundle, err := sgbundle.LoadJSONFromPath(c.BundlePath) @@ -215,7 +230,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st } else if c.RFC3161TimestampPath == "" && co.UseSignedTimestamps { return fmt.Errorf("when specifying --use-signed-timestamps or --timestamp-certificate-chain, you must also specify --rfc3161-timestamp-path") } - if co.UseSignedTimestamps { + if co.UseSignedTimestamps && co.TrustedMaterial == nil { tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) if err != nil { return fmt.Errorf("unable to load TSA certificates: %w", err) @@ -233,21 +248,23 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st } co.RekorClient = rekorClient } - // This performs an online fetch of the Rekor public keys, but this is needed - // for verifying tlog entries (both online and offline). - co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) - if err != nil { - return fmt.Errorf("getting Rekor public keys: %w", err) + if co.TrustedMaterial == nil { + // This performs an online fetch of the Rekor public keys, but this is needed + // for verifying tlog entries (both online and offline). + co.RekorPubKeys, err = cosign.GetRekorPubs(ctx) + if err != nil { + return fmt.Errorf("getting Rekor public keys: %w", err) + } } } - if keylessVerification(c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && keylessVerification(c.KeyRef, c.Sk) { if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { return err } } // Ignore Signed Certificate Timestamp if the flag is set or a key is provided - if shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { + if co.TrustedMaterial == nil && shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx) if err != nil { return fmt.Errorf("getting ctlog public keys: %w", err) diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index fbc356219ed..0e54449dd93 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -819,6 +819,7 @@ func makeLocalNewBundle(t *testing.T, sig []byte, digest [32]byte) string { } func TestVerifyBlobCmdWithBundle(t *testing.T) { + t.Setenv("TUF_ROOT", t.TempDir()) keyless := newKeylessStack(t) defer os.RemoveAll(keyless.td) @@ -1332,6 +1333,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { } func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { + t.Setenv("TUF_ROOT", t.TempDir()) keyless := newKeylessStack(t) defer os.RemoveAll(keyless.td) diff --git a/doc/cosign_attest.md b/doc/cosign_attest.md index b11e9090806..9b58de8c382 100644 --- a/doc/cosign_attest.md +++ b/doc/cosign_attest.md @@ -38,6 +38,9 @@ cosign attest [flags] # supply attestation via stdin echo | cosign attest --predicate - + # write attestation to stdout + cosign attest --predicate --type --key cosign.key --no-upload true + # attach an attestation to a container image and honor the creation timestamp of the signature cosign attest --predicate --type --key cosign.key --record-creation-timestamp ``` @@ -58,7 +61,7 @@ cosign attest [flags] --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --key string path to the private key file, KMS URI or Kubernetes Secret --new-bundle-format attach a Sigstore bundle using OCI referrers API - --no-upload do not upload the generated attestation + --no-upload do not upload the generated attestation, but send the attestation output to STDOUT --oidc-client-id string OIDC client ID for application (default "sigstore") --oidc-client-secret-file string Path to file containing OIDC client secret for application --oidc-disable-ambient-providers Disable ambient OIDC providers. When true, ambient credentials will not be read diff --git a/doc/cosign_trusted-root_create.md b/doc/cosign_trusted-root_create.md index 486aa8a8a44..ca2b33dfc0a 100644 --- a/doc/cosign_trusted-root_create.md +++ b/doc/cosign_trusted-root_create.md @@ -16,11 +16,15 @@ cosign trusted-root create [flags] --certificate-chain stringArray path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. --ctfe-key stringArray path to a PEM-encoded public key used by certificate authority for certificate transparency log. --ctfe-start-time stringArray RFC 3339 string describing validity start time for key use by certificate transparency log. + --ctfe-url stringArray URL of the certificate transparency log. + --fulcio-uri stringArray URI of the Fulcio server issuing certificates. -h, --help help for create --out string path to output trusted root - --rekor-key stringArray path to a PEM-encoded public key used by transparency log like Rekor. + --rekor-key stringArray path to a PEM-encoded public key used by transparency log like Rekor. For Rekor V2, append the Rekor server name with ',', e.g. '--rekor-key=/path/to/key.pub,rekor.example.test'. --rekor-start-time stringArray RFC 3339 string describing validity start time for key use by transparency log like Rekor. + --rekor-url stringArray URL of the transparency log. --timestamp-certificate-chain stringArray path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates + --timestamp-uri stringArray URI of the timestamp authority server. ``` ### Options inherited from parent commands diff --git a/go.mod b/go.mod index 055b711a92c..dd63fd4668b 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( cuelang.org/go v0.12.1 github.com/ThalesIgnite/crypto11 v1.2.5 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1 - github.com/buildkite/agent/v3 v3.97.0 + github.com/buildkite/agent/v3 v3.98.1 github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 - github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 + github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 github.com/dustin/go-humanize v1.0.1 @@ -22,7 +22,7 @@ require ( github.com/google/certificate-transparency-go v1.3.1 github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.20.5 - github.com/google/go-github/v55 v55.0.0 + github.com/google/go-github/v72 v72.0.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/manifoldco/promptui v0.9.0 @@ -31,13 +31,14 @@ require ( github.com/moby/term v0.5.2 github.com/mozillazg/docker-credential-acr-helper v0.4.0 github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 - github.com/open-policy-agent/opa v1.4.2 + github.com/open-policy-agent/opa v1.5.1 github.com/secure-systems-lab/go-securesystemslib v0.9.0 github.com/sigstore/fulcio v1.7.1 github.com/sigstore/protobuf-specs v0.4.2 github.com/sigstore/rekor v1.3.10 + github.com/sigstore/rekor-tiles v0.1.5 github.com/sigstore/sigstore v1.9.4 - github.com/sigstore/sigstore-go v0.7.2 + github.com/sigstore/sigstore-go v1.0.0 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.4 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.4 github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.4 @@ -48,25 +49,25 @@ require ( github.com/spf13/viper v1.20.1 github.com/spiffe/go-spiffe/v2 v2.5.0 github.com/stretchr/testify v1.10.0 - github.com/theupdateframework/go-tuf/v2 v2.0.2 + github.com/theupdateframework/go-tuf/v2 v2.1.1 github.com/transparency-dev/merkle v0.0.2 github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 - gitlab.com/gitlab-org/api/client-go v0.128.0 + gitlab.com/gitlab-org/api/client-go v0.130.1 golang.org/x/crypto v0.38.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/sync v0.14.0 + golang.org/x/sync v0.15.0 golang.org/x/term v0.32.0 - google.golang.org/api v0.234.0 + google.golang.org/api v0.236.0 google.golang.org/protobuf v1.36.6 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.33.1 + k8s.io/apimachinery v0.33.1 + k8s.io/client-go v0.33.1 k8s.io/utils v0.0.0-20241210054802-24370beab758 sigs.k8s.io/release-utils v0.11.1 ) require ( - cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go v0.121.1 // indirect cloud.google.com/go/auth v0.16.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect @@ -93,7 +94,6 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect @@ -107,7 +107,7 @@ require ( github.com/alibabacloud-go/tea-xml v1.1.3 // indirect github.com/aliyun/credentials-go v1.3.2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect @@ -130,10 +130,10 @@ require ( github.com/buildkite/interpolate v0.1.5 // indirect github.com/buildkite/roko v1.3.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect @@ -149,7 +149,8 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/proto v1.13.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect @@ -162,17 +163,17 @@ require ( github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect + github.com/go-sql-driver/mysql v1.9.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect + github.com/google/trillian v1.7.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.2 // indirect @@ -185,11 +186,16 @@ require ( github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.5 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hashicorp/vault/api v1.16.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/in-toto/attestation v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect github.com/jellydator/ttlcache/v3 v3.3.0 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect @@ -210,7 +216,7 @@ require ( github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -220,7 +226,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect @@ -246,6 +252,8 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/urfave/negroni v1.0.0 // indirect github.com/vbatts/tar-split v0.12.1 // indirect + github.com/vektah/gqlparser/v2 v2.5.26 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect @@ -254,10 +262,10 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.step.sm/crypto v0.66.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect @@ -269,16 +277,17 @@ require ( golang.org/x/time v0.11.0 // indirect golang.org/x/tools v0.33.0 // indirect google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/grpc v1.72.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5fcb9c50543..39cb84a4fc2 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= -cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go v0.121.1 h1:S3kTQSydxmu1JfLRLpKtxRPA7rSrYPRPEUmL/PavVUw= +cloud.google.com/go v0.121.1/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw= cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= @@ -67,10 +67,10 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= @@ -119,13 +119,15 @@ github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.2 h1:L4WppI9rctC8PdlMgyTkF8bBsy9pyKQEzBD1bHMRl+g= github.com/aliyun/credentials-go v1.3.2/go.mod h1:tlpz4uys4Rn7Ik4/piGRrTbXy2uLKvePgQJJduE+Y5c= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= @@ -165,19 +167,20 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/buildkite/agent/v3 v3.97.0 h1:OMvgZTp6HrZfD3Lixo5eA6EybtmxGjGTHussrQcaeLw= -github.com/buildkite/agent/v3 v3.97.0/go.mod h1:YgjSP0MmSvZpEwS6z1aGhmF+Xl9V62PHRzHvZJkIhDc= +github.com/buildkite/agent/v3 v3.98.1 h1:nahqxKduarNKb7r/rkW2dIqt2z8sliHKK+pTE+FrLsA= +github.com/buildkite/agent/v3 v3.98.1/go.mod h1:QA84xttUe4vxSs2MmMG33ri85VbAn5N3kmX9B1kYROA= github.com/buildkite/go-pipeline v0.13.3 h1:llI7sAdZ7sqYE7r8ePlmDADRhJ1K0Kua2+gv74Z9+Es= github.com/buildkite/go-pipeline v0.13.3/go.mod h1:1uC2XdHkTV1G5jYv9K8omERIwrsYbBruBrPx1Zu1uFw= github.com/buildkite/interpolate v0.1.5 h1:v2Ji3voik69UZlbfoqzx+qfcsOKLA61nHdU79VV+tPU= github.com/buildkite/interpolate v0.1.5/go.mod h1:dHnrwHew5O8VNOAgMDpwRlFnhL5VSN6M1bHVmRZ9Ccc= github.com/buildkite/roko v1.3.1 h1:t7K30ceLLYn6k7hQP4oq1c7dVlhgD5nRcuSRDEEnY1s= github.com/buildkite/roko v1.3.1/go.mod h1:23R9e6nHxgedznkwwfmqZ6+0VJZJZ2Sg/uVcp2cP46I= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -196,9 +199,6 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= @@ -215,8 +215,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3 github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.19 h1:tUN6H7LWqNx4hQVxomd0CVsDwaDr9gaRQaI4GpSmrsA= github.com/creack/pty v1.1.19/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= -github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -258,8 +258,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -271,8 +271,10 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= @@ -314,9 +316,10 @@ github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= @@ -336,6 +339,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= @@ -355,8 +360,8 @@ github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeW github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -368,16 +373,16 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.5 h1:4RnlYcDs5hoA++CeFjlbZ/U9Yp1EuWr+UhhTyYQjOP0= github.com/google/go-containerregistry v0.20.5/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E= -github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= -github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= +github.com/google/go-github/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM= +github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek= @@ -428,8 +433,6 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI= github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= @@ -487,8 +490,8 @@ github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUt github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -536,16 +539,16 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/open-policy-agent/opa v1.4.2 h1:ag4upP7zMsa4WE2p1pwAFeG4Pn3mNwfAx9DLhhJfbjU= -github.com/open-policy-agent/opa v1.4.2/go.mod h1:DNzZPKqKh4U0n0ANxcCVlw8lCSv2c+h5G/3QvSYdWZ8= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/open-policy-agent/opa v1.5.1 h1:LTxxBJusMVjfs67W4FoRcnMfXADIGFMzpqnfk6D08Cg= +github.com/open-policy-agent/opa v1.5.1/go.mod h1:bYbS7u+uhTI+cxHQIpzvr5hxX0hV7urWtY+38ZtjMgk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -554,8 +557,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -580,8 +583,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM= -github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -609,10 +612,12 @@ github.com/sigstore/protobuf-specs v0.4.2 h1:bD5bnhctpGNiR+FAEZl7N95XkN8TJFrNMIc github.com/sigstore/protobuf-specs v0.4.2/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= github.com/sigstore/rekor v1.3.10 h1:/mSvRo4MZ/59ECIlARhyykAlQlkmeAQpvBPlmJtZOCU= github.com/sigstore/rekor v1.3.10/go.mod h1:JvryKJ40O0XA48MdzYUPu0y4fyvqt0C4iSY7ri9iu3A= +github.com/sigstore/rekor-tiles v0.1.5 h1:NzCpMPhoIFUrFj39+Em+WGeyGgshY0gbCGfXObjtvug= +github.com/sigstore/rekor-tiles v0.1.5/go.mod h1:SO8yIfeP09Ggvs2PF6A5rfejA8a0LujSjz23fAxUdVw= github.com/sigstore/sigstore v1.9.4 h1:64+OGed80+A4mRlNzRd055vFcgBeDghjZw24rPLZgDU= github.com/sigstore/sigstore v1.9.4/go.mod h1:Q7tGTC3gbtK7c3jcxEmGc2MmK4rRpIRzi3bxRFWKvEY= -github.com/sigstore/sigstore-go v0.7.2 h1:CN4xPasChSEb0QBMxMW5dLcXdA9KD4QiRyVnMkhXj6U= -github.com/sigstore/sigstore-go v0.7.2/go.mod h1:AIRj4I3LC82qd07VFm3T2zXYiddxeBV1k/eoS8nTz0E= +github.com/sigstore/sigstore-go v1.0.0 h1:4N07S2zLxf09nTRwaPKyAxbKzpM8WJYUS8lWWaYxneU= +github.com/sigstore/sigstore-go v1.0.0/go.mod h1:UYsZ/XHE4eltv1o1Lu+n6poW1Z5to3f0+emvfXNxIN8= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.4 h1:kQqUJ1VuWdJltMkinFXAHTlJrzMRPoNgL+dy6WyJ/dA= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.4/go.mod h1:9miLz7c69vj/7VH7UpCKHDia41HCTIDJWJWf4Ex5yUk= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.4 h1:MHRm7YQuF4zFyoXRLgUdLaNxqVO6JlLGnkDUI9fm9ow= @@ -648,6 +653,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -667,8 +674,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= -github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if370jjzO84SS+Ebo= -github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= +github.com/theupdateframework/go-tuf/v2 v2.1.1 h1:OWcoHItwsGO+7m0wLa7FDWPR4oB1cj0zOr1kosE4G+I= +github.com/theupdateframework/go-tuf/v2 v2.1.1/go.mod h1:V675cQGhZONR0OGQ8r1feO0uwtsTBYPDWHzAAPn5rjE= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= @@ -688,8 +695,12 @@ github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4= +github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I= github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -714,8 +725,8 @@ github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97 github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= -gitlab.com/gitlab-org/api/client-go v0.128.0 h1:Wvy1UIuluKemubao2k8EOqrl3gbgJ1PVifMIQmg2Da4= -gitlab.com/gitlab-org/api/client-go v0.128.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE= +gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc= +gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -724,24 +735,24 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= +go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.step.sm/crypto v0.66.0 h1:9TW6BEguOtcS9NIjja9bDQ+j8OjhenU/F6lJfHjbXNU= go.step.sm/crypto v0.66.0/go.mod h1:anqGyvO/Px05D1mznHq4/a9wwP1I1DmMZvk+TWX5Dzo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -759,9 +770,7 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= @@ -797,9 +806,7 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -817,8 +824,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -844,10 +851,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -856,9 +860,7 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= @@ -870,9 +872,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= @@ -901,18 +901,18 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4= -google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= +google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= +google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= -google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= -google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -933,6 +933,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -954,24 +956,27 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/release-utils v0.11.1 h1:hzvXGpHgHJfLOJB6TRuu14bzWc3XEglHmXHJqwClSZE= sigs.k8s.io/release-utils v0.11.1/go.mod h1:ybR2V/uQAOGxYfzYtBenSYeXWkBGNP2qnEiX77ACtpc= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= diff --git a/pkg/cosign/env/env.go b/pkg/cosign/env/env.go index 016687acb8e..00500843dbf 100644 --- a/pkg/cosign/env/env.go +++ b/pkg/cosign/env/env.go @@ -60,6 +60,11 @@ const ( VariableSigstoreIDToken Variable = "SIGSTORE_ID_TOKEN" //nolint:gosec VariableSigstoreTSACertificateFile Variable = "SIGSTORE_TSA_CERTIFICATE_FILE" + // TUF environment variables + VariableTUFRootDir Variable = "TUF_ROOT" + VariableTUFMirror Variable = "TUF_MIRROR" + VariableTUFRootJSON Variable = "TUF_ROOT_JSON" + // Other external environment variables VariableGitHubHost Variable = "GITHUB_HOST" VariableGitHubToken Variable = "GITHUB_TOKEN" //nolint:gosec @@ -145,6 +150,24 @@ var ( Sensitive: false, External: true, }, + VariableTUFMirror: { + Description: "URL of the TUF mirror. Use with TUF_ROOT_JSON to refresh TUF metadata during signing and verification commands. Setting this will cause cosign to attempt to use trusted_root.json if available and will ignore custom TUF metadata.", + Expects: "URL of the TUF mirror", + Sensitive: false, + External: true, + }, + VariableTUFRootDir: { + Description: "path to the TUF cache directory", + Expects: "path to the TUF cache directory", + Sensitive: false, + External: true, + }, + VariableTUFRootJSON: { + Description: "path to the TUF root.json file used to initialize and update a local TUF repository. Use with TUF_MIRROR to refresh TUF metadata during signing and verification commands. Setting this will cause cosign to attempt to use trusted_root.json if available and will ignore custom TUF metadata.", + Expects: "path to root.json", + Sensitive: false, + External: true, + }, VariableGitHubHost: { Description: "is URL of the GitHub Enterprise instance", Expects: "string with the URL of GitHub Enterprise instance", diff --git a/pkg/cosign/git/github/github.go b/pkg/cosign/git/github/github.go index d58e68d414e..9866f6eef65 100644 --- a/pkg/cosign/git/github/github.go +++ b/pkg/cosign/git/github/github.go @@ -25,7 +25,7 @@ import ( "os" "strings" - "github.com/google/go-github/v55/github" + "github.com/google/go-github/v72/github" "golang.org/x/crypto/nacl/box" "golang.org/x/oauth2" diff --git a/pkg/cosign/tlog.go b/pkg/cosign/tlog.go index a9379ba9418..bd3c2a897e6 100644 --- a/pkg/cosign/tlog.go +++ b/pkg/cosign/tlog.go @@ -44,6 +44,8 @@ import ( hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" "github.com/sigstore/rekor/pkg/types/intoto" intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/tlog" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/tuf" "github.com/transparency-dev/merkle/proof" @@ -218,7 +220,7 @@ func doUpload(ctx context.Context, rekorClient *client.Rekor, pe models.Proposed if err != nil { return nil, err } - return e, VerifyTLogEntryOffline(ctx, e, rekorPubsFromAPI) + return e, VerifyTLogEntryOffline(ctx, e, rekorPubsFromAPI, nil) } return nil, err } @@ -442,20 +444,14 @@ func FindTlogEntry(ctx context.Context, rekorClient *client.Rekor, // VerifyTLogEntryOffline verifies a TLog entry against a map of trusted rekorPubKeys indexed // by log id. -func VerifyTLogEntryOffline(ctx context.Context, e *models.LogEntryAnon, rekorPubKeys *TrustedTransparencyLogPubKeys) error { +func VerifyTLogEntryOffline(ctx context.Context, e *models.LogEntryAnon, rekorPubKeys *TrustedTransparencyLogPubKeys, trustedMaterial root.TrustedMaterial) error { if e.Verification == nil || e.Verification.InclusionProof == nil { return errors.New("inclusion proof not provided") } - if rekorPubKeys == nil || rekorPubKeys.Keys == nil { + if trustedMaterial == nil && (rekorPubKeys == nil || rekorPubKeys.Keys == nil) { return errors.New("no trusted rekor public keys provided") } - // Make sure all the rekorPubKeys are ecsda.PublicKeys - for k, v := range rekorPubKeys.Keys { - if _, ok := v.PubKey.(*ecdsa.PublicKey); !ok { - return fmt.Errorf("rekor Public key for LogID %s is not type ecdsa.PublicKey", k) - } - } hashes := [][]byte{} for _, h := range e.Verification.InclusionProof.Hashes { @@ -477,6 +473,23 @@ func VerifyTLogEntryOffline(ctx context.Context, e *models.LogEntryAnon, rekorPu } // Verify rekor's signature over the SET. + if trustedMaterial != nil { + logID, err := hex.DecodeString(*e.LogID) + if err != nil { + return fmt.Errorf("decoding log ID: %w", err) + } + entry, err := tlog.NewEntry(entryBytes, *e.IntegratedTime, *e.LogIndex, logID, e.Verification.SignedEntryTimestamp, e.Verification.InclusionProof) + if err != nil { + return fmt.Errorf("converting tlog entry: %w", err) + } + if err := tlog.VerifySET(entry, trustedMaterial.RekorLogs()); err != nil { + return fmt.Errorf("verifying SET offline: %w", err) + } + return nil + } + + // No trusted root available, so verify the SET with legacy TUF metadata: + payload := bundle.RekorPayload{ Body: e.Body, IntegratedTime: *e.IntegratedTime, @@ -484,6 +497,13 @@ func VerifyTLogEntryOffline(ctx context.Context, e *models.LogEntryAnon, rekorPu LogID: *e.LogID, } + // Make sure all the rekorPubKeys are ecsda.PublicKeys + for k, v := range rekorPubKeys.Keys { + if _, ok := v.PubKey.(*ecdsa.PublicKey); !ok { + return fmt.Errorf("rekor Public key for LogID %s is not type ecdsa.PublicKey", k) + } + } + pubKey, ok := rekorPubKeys.Keys[payload.LogID] if !ok { return errors.New("rekor log public key not found for payload. Check your TUF root (see cosign initialize) or set a custom key with env var SIGSTORE_REKOR_PUBLIC_KEY") diff --git a/pkg/cosign/tlog_test.go b/pkg/cosign/tlog_test.go index 55bd76bb9f6..16a1abd58cd 100644 --- a/pkg/cosign/tlog_test.go +++ b/pkg/cosign/tlog_test.go @@ -15,19 +15,27 @@ package cosign import ( + "bytes" "context" "crypto" "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/x509" + "encoding/base64" "encoding/hex" "encoding/pem" "strings" "testing" + "time" + "github.com/go-openapi/swag" ttestdata "github.com/google/certificate-transparency-go/trillian/testdata" "github.com/sigstore/rekor/pkg/generated/models" + rtypes "github.com/sigstore/rekor/pkg/types" + hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/tuf" ) @@ -173,7 +181,57 @@ func TestVerifyTLogEntryOfflineFailsWithInvalidPublicKey(t *testing.T) { t.Fatalf("failed to add RSA key to transparency log public keys: %v", err) } - err = VerifyTLogEntryOffline(context.Background(), &models.LogEntryAnon{Verification: &models.LogEntryAnonVerification{InclusionProof: &models.InclusionProof{}}}, &rekorPubKeys) + // generate a valid log entry with valid inclusion proof + sigSigner, err := signature.LoadRSAPKCS1v15Signer(rsaPrivKey.(*rsa.PrivateKey), crypto.SHA256) + if err != nil { + t.Fatalf("Unable to load RSA signer") + } + ctx := context.Background() + blob := []byte("foo") + blobSignature, err := sigSigner.SignMessage(bytes.NewReader(blob)) + if err != nil { + t.Fatal(err) + } + logID := calculateLogID(t, signer.Public()) + payloadHash := sha256.Sum256(blob) + artifactProperties := rtypes.ArtifactProperties{ + ArtifactHash: hex.EncodeToString(payloadHash[:]), + SignatureBytes: blobSignature, + PublicKeyBytes: [][]byte{rsaPEM}, + PKIFormat: "x509", + } + entryProps, err := hashedrekord_v001.V001Entry{}.CreateFromArtifactProperties(ctx, artifactProperties) + if err != nil { + t.Fatal(err) + } + rekorEntry, err := rtypes.UnmarshalEntry(entryProps) + if err != nil { + t.Fatal(err) + } + canonicalEntry, err := rekorEntry.Canonicalize(ctx) + if err != nil { + t.Fatal(err) + } + lea := &models.LogEntryAnon{ + Body: base64.StdEncoding.EncodeToString(canonicalEntry), + LogIndex: swag.Int64(0), + LogID: swag.String(logID), + IntegratedTime: swag.Int64(time.Now().Unix()), + } + entryUUID, err := ComputeLeafHash(lea) + if err != nil { + t.Fatal(err) + } + lea.Verification = &models.LogEntryAnonVerification{ + InclusionProof: &models.InclusionProof{ + LogIndex: swag.Int64(0), + TreeSize: swag.Int64(1), + RootHash: swag.String(hex.EncodeToString(entryUUID)), + Hashes: []string{}, + }, + } + + err = VerifyTLogEntryOffline(ctx, lea, &rekorPubKeys, nil) if err == nil { t.Fatal("Wanted error got none") } diff --git a/pkg/cosign/tuf.go b/pkg/cosign/tuf.go new file mode 100644 index 00000000000..2a7049feec4 --- /dev/null +++ b/pkg/cosign/tuf.go @@ -0,0 +1,111 @@ +// Copyright 2025 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 cosign + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/sigstore/cosign/v2/pkg/cosign/env" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/tuf" +) + +func TrustedRoot() (root.TrustedMaterial, error) { + opts, err := setTUFOpts() + if err != nil { + return nil, fmt.Errorf("error setting TUF options: %w", err) + } + tr, err := root.NewLiveTrustedRoot(opts) + if err != nil { + return nil, fmt.Errorf("error getting live trusted root: %w", err) + } + return tr, nil +} + +// setTUFOpts sets the TUF cache directory, the mirror URL, and the root.json in the TUF options. +// The cache directory is provided by the user as an environment variable TUF_ROOT, or the default $HOME/.sigstore/root is used. +// The mirror URL is provided by the user as an environment variable TUF_MIRROR. If not overridden by the user, the value set during `cosign initialize` in remote.json in the cache directory is used. +// If the mirror happens to be the sigstore.dev production TUF CDN, the options are returned since it is safe to use all the default settings. +// If the mirror is a custom mirror, we try to find a cached root.json. We must not use the default embedded root.json. +// If the TUF options cannot be found through these steps, the caller should not try to use this TUF client to fetch the trusted root and should instead fall back to the legacy TUF client to fetch individual trusted keys. +func setTUFOpts() (*tuf.Options, error) { + opts := tuf.DefaultOptions() + if tufCacheDir := env.Getenv(env.VariableTUFRootDir); tufCacheDir != "" { //nolint:forbidigo + opts.CachePath = tufCacheDir + } + err := setTUFMirror(opts) + if err != nil { + return nil, fmt.Errorf("error setting TUF mirror: %w", err) + } + if opts.RepositoryBaseURL == tuf.DefaultMirror { + // Using the default mirror, so just use the embedded root.json. + return opts, nil + } + err = setTUFRootJSON(opts) + if err != nil { + return nil, fmt.Errorf("error setting root: %w", err) + } + return opts, nil +} + +func setTUFMirror(opts *tuf.Options) error { + if tufMirror := env.Getenv(env.VariableTUFMirror); tufMirror != "" { //nolint:forbidigo + opts.RepositoryBaseURL = tufMirror + return nil + } + // try using the mirror set by `cosign initialize` + cachedRemote := filepath.Join(opts.CachePath, "remote.json") + remoteBytes, err := os.ReadFile(cachedRemote) + if errors.Is(err, os.ErrNotExist) { + return nil // `cosign initialize` wasn't run, so use the default + } + if err != nil { + return fmt.Errorf("error reading remote.json: %w", err) + } + remote := make(map[string]string) + err = json.Unmarshal(remoteBytes, &remote) + if err != nil { + return fmt.Errorf("error unmarshalling remote.json: %w", err) + } + opts.RepositoryBaseURL = remote["mirror"] + return nil +} + +func setTUFRootJSON(opts *tuf.Options) error { + // TUF root set by TUF_ROOT_JSON + if tufRootJSON := env.Getenv(env.VariableTUFRootJSON); tufRootJSON != "" { //nolint:forbidigo + rootJSONBytes, err := os.ReadFile(tufRootJSON) + if err != nil { + return fmt.Errorf("error reading root.json given by TUF_ROOT_JSON") + } + opts.Root = rootJSONBytes + return nil + } + // Look for cached root.json + cachedRootJSON := filepath.Join(opts.CachePath, tuf.URLToPath(opts.RepositoryBaseURL), "root.json") + if _, err := os.Stat(cachedRootJSON); !os.IsNotExist(err) { + rootJSONBytes, err := os.ReadFile(cachedRootJSON) + if err != nil { + return fmt.Errorf("error reading cached root.json") + } + opts.Root = rootJSONBytes + return nil + } + return fmt.Errorf("could not find cached root.json") +} diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index b2c9ee66fce..29aa372955d 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -29,6 +29,7 @@ import ( "errors" "fmt" "io/fs" + "log" "net/http" "os" "regexp" @@ -38,7 +39,6 @@ import ( "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/digitorus/timestamp" "github.com/go-openapi/runtime" - "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" @@ -66,6 +66,7 @@ import ( sgbundle "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/tlog" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" @@ -96,6 +97,9 @@ type CheckOpts struct { // ClaimVerifier, if provided, verifies claims present in the oci.Signature. ClaimVerifier func(sig oci.Signature, imageDigest v1.Hash, annotations map[string]interface{}) error + // TrustedMaterial contains trusted metadata for all Sigstore services. It is exclusive with RekorPubKeys, RootCerts, IntermediateCerts, CTLogPubKeys, and the TSA* cert fields. + TrustedMaterial root.TrustedMaterial + // RekorClient, if set, is used to make online tlog calls use to verify signatures and public keys. RekorClient *client.Rekor // RekorPubKeys, if set, is used to validate signatures on log entries from @@ -170,10 +174,6 @@ type CheckOpts struct { // NewBundleFormat enables the new bundle format (Cosign Bundle Spec) and the new verifier. NewBundleFormat bool - - // TrustedMaterial is the trusted material to use for verification. - // Currently, this is only applicable when NewBundleFormat is true. - TrustedMaterial root.TrustedMaterial } type verifyTrustedMaterial struct { @@ -342,11 +342,27 @@ func ValidateAndUnpackCertWithIntermediates(cert *x509.Certificate, co *CheckOpt } // Now verify the cert, then the signature. - chains, err := TrustedCert(cert, co.RootCerts, intermediateCerts) + shouldVerifyEmbeddedSCT := !co.IgnoreSCT + contains, err := ContainsSCT(cert.Raw) if err != nil { return nil, err } + shouldVerifyEmbeddedSCT = shouldVerifyEmbeddedSCT && contains + // If trusted root is available and the SCT is embedded, use the verifiers from sigstore-go (preferred). + var chains [][]*x509.Certificate + if co.TrustedMaterial != nil && shouldVerifyEmbeddedSCT { + if chains, err = verify.VerifyLeafCertificate(cert.NotBefore, cert, co.TrustedMaterial); err != nil { + return nil, err + } + } else { + // If the trusted root is not available, OR if the SCT is detached, use the verifiers from cosign (legacy). + // The certificate chains will be needed for the legacy SCT verifiers, which is why we can't use sigstore-go. + chains, err = TrustedCert(cert, co.RootCerts, intermediateCerts) + if err != nil { + return nil, err + } + } err = CheckCertificatePolicy(cert, co) if err != nil { @@ -357,15 +373,20 @@ func ValidateAndUnpackCertWithIntermediates(cert *x509.Certificate, co *CheckOpt if co.IgnoreSCT { return verifier, nil } - contains, err := ContainsSCT(cert.Raw) - if err != nil { - return nil, err - } if !contains && len(co.SCT) == 0 { return nil, &VerificationFailure{ fmt.Errorf("certificate does not include required embedded SCT and no detached SCT was set"), } } + + // If trusted root is available and the SCT is embedded, use the verifiers from sigstore-go (preferred). + if co.TrustedMaterial != nil && contains { + if err := verify.VerifySignedCertificateTimestamp(chains, 1, co.TrustedMaterial); err != nil { + return nil, err + } + return verifier, nil + } + // handle if chains has more than one chain - grab first and print message if len(chains) > 1 { fmt.Fprintf(os.Stderr, "**Info** Multiple valid certificate chains found. Selecting the first to verify the SCT.\n") @@ -374,22 +395,22 @@ func ValidateAndUnpackCertWithIntermediates(cert *x509.Certificate, co *CheckOpt if err := VerifyEmbeddedSCT(context.Background(), chains[0], co.CTLogPubKeys); err != nil { return nil, err } - } else { - chain := chains[0] - if len(chain) < 2 { - return nil, errors.New("certificate chain must contain at least a certificate and its issuer") - } - certPEM, err := cryptoutils.MarshalCertificateToPEM(chain[0]) - if err != nil { - return nil, err - } - chainPEM, err := cryptoutils.MarshalCertificatesToPEM(chain[1:]) - if err != nil { - return nil, err - } - if err := VerifySCT(context.Background(), certPEM, chainPEM, co.SCT, co.CTLogPubKeys); err != nil { - return nil, err - } + return verifier, nil + } + chain := chains[0] + if len(chain) < 2 { + return nil, errors.New("certificate chain must contain at least a certificate and its issuer") + } + certPEM, err := cryptoutils.MarshalCertificateToPEM(chain[0]) + if err != nil { + return nil, err + } + chainPEM, err := cryptoutils.MarshalCertificatesToPEM(chain[1:]) + if err != nil { + return nil, err + } + if err := VerifySCT(context.Background(), certPEM, chainPEM, co.SCT, co.CTLogPubKeys); err != nil { + return nil, err } return verifier, nil @@ -527,7 +548,7 @@ func ValidateAndUnpackCertWithChain(cert *x509.Certificate, chain []*x509.Certif return ValidateAndUnpackCert(cert, co) } -func tlogValidateEntry(ctx context.Context, client *client.Rekor, rekorPubKeys *TrustedTransparencyLogPubKeys, +func tlogValidateEntry(ctx context.Context, client *client.Rekor, rekorPubKeys *TrustedTransparencyLogPubKeys, trustedMaterial root.TrustedMaterial, sig oci.Signature, pem []byte) (*models.LogEntryAnon, error) { b64sig, err := sig.Base64Signature() if err != nil { @@ -551,7 +572,7 @@ func tlogValidateEntry(ctx context.Context, client *client.Rekor, rekorPubKeys * entryVerificationErrs := make([]string, 0) for _, e := range tlogEntries { entry := e - if err := VerifyTLogEntryOffline(ctx, &entry, rekorPubKeys); err != nil { + if err := VerifyTLogEntryOffline(ctx, &entry, rekorPubKeys, trustedMaterial); err != nil { entryVerificationErrs = append(entryVerificationErrs, err.Error()) continue } @@ -594,8 +615,8 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co } // Enforce this up front. - if co.RootCerts == nil && co.SigVerifier == nil { - return nil, false, errors.New("one of verifier or root certs is required") + if co.RootCerts == nil && co.SigVerifier == nil && co.TrustedMaterial == nil { + return nil, false, errors.New("one of verifier, root certs, or trusted root is required") } // This is a carefully optimized sequence for fetching the signatures of the @@ -639,8 +660,8 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co // If there were no valid signatures, we return an error. func VerifyLocalImageSignatures(ctx context.Context, path string, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { // Enforce this up front. - if co.RootCerts == nil && co.SigVerifier == nil { - return nil, false, errors.New("one of verifier or root certs is required") + if co.RootCerts == nil && co.SigVerifier == nil && co.TrustedMaterial == nil { + return nil, false, errors.New("one of verifier, root certs, or trusted root is required") } se, err := layout.SignedImageIndex(path) @@ -805,7 +826,7 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash, return false, err } - e, err := tlogValidateEntry(ctx, co.RekorClient, co.RekorPubKeys, sig, pemBytes) + e, err := tlogValidateEntry(ctx, co.RekorClient, co.RekorPubKeys, co.TrustedMaterial, sig, pemBytes) if err != nil { return false, err } @@ -1016,8 +1037,8 @@ func VerifyImageAttestations(ctx context.Context, signedImgRef name.Reference, c // If there were no valid signatures, we return an error. func VerifyLocalImageAttestations(ctx context.Context, path string, co *CheckOpts) (checkedAttestations []oci.Signature, bundleVerified bool, err error) { // Enforce this up front. - if co.RootCerts == nil && co.SigVerifier == nil { - return nil, false, errors.New("one of verifier or root certs is required") + if co.RootCerts == nil && co.SigVerifier == nil && co.TrustedMaterial == nil { + return nil, false, errors.New("one of verifier, root certs, or trusted root is required") } se, err := layout.SignedImageIndex(path) @@ -1164,9 +1185,26 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { return false, nil } - if co.RekorPubKeys == nil || co.RekorPubKeys.Keys == nil { + if co.TrustedMaterial == nil && (co.RekorPubKeys == nil || co.RekorPubKeys.Keys == nil) { return false, errors.New("no trusted rekor public keys provided") } + + if co.TrustedMaterial != nil { + payload := bundle.Payload + logID, err := hex.DecodeString(payload.LogID) + if err != nil { + return false, fmt.Errorf("decoding log ID: %w", err) + } + body, _ := base64.StdEncoding.DecodeString(payload.Body.(string)) + entry, err := tlog.NewEntry(body, payload.IntegratedTime, payload.LogIndex, logID, bundle.SignedEntryTimestamp, nil) + if err != nil { + return false, fmt.Errorf("converting tlog entry: %w", err) + } + if err := tlog.VerifySET(entry, co.TrustedMaterial.RekorLogs()); err != nil { + return false, fmt.Errorf("verifying bundle with trusted root: %w", err) + } + return true, nil + } // Make sure all the rekorPubKeys are ecsda.PublicKeys for k, v := range co.RekorPubKeys.Keys { if _, ok := v.PubKey.(*ecdsa.PublicKey); !ok { @@ -1220,6 +1258,40 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { return true, nil } +type signedEntityForTimestamp struct { + verify.BaseSignedEntity + timestamp *cbundle.RFC3161Timestamp + sigContent *sigContent +} + +type sigContent struct { + rawSig []byte +} + +func (e *signedEntityForTimestamp) Timestamps() ([][]byte, error) { + timestamps := make([][]byte, 1) + timestamps[0] = e.timestamp.SignedRFC3161Timestamp + return timestamps, nil +} + +func (e *signedEntityForTimestamp) SignatureContent() (verify.SignatureContent, error) { + return e.sigContent, nil +} + +func (s *sigContent) Signature() []byte { + return s.rawSig +} + +func (s *sigContent) EnvelopeContent() verify.EnvelopeContent { + log.Fatal("programmer error: EnvelopeContent was called but not implemented") + return nil +} + +func (s *sigContent) MessageSignatureContent() verify.MessageSignatureContent { + log.Fatal("programmer error: MessageSignatureContent was called but not implemented") + return nil +} + // VerifyRFC3161Timestamp verifies that the timestamp in sig is correctly signed, and if so, // returns the timestamp value. // It returns (nil, nil) if there is no timestamp, or (nil, err) if there is an invalid timestamp or if @@ -1231,7 +1303,7 @@ func VerifyRFC3161Timestamp(sig oci.Signature, co *CheckOpts) (*timestamp.Timest return nil, err case ts == nil: return nil, nil - case co.TSARootCertificates == nil: + case co.TSARootCertificates == nil && co.TrustedMaterial == nil: return nil, errors.New("no TSA root certificate(s) provided to verify timestamp") } @@ -1257,6 +1329,21 @@ func VerifyRFC3161Timestamp(sig oci.Signature, co *CheckOpts) (*timestamp.Timest tsBytes = rawSig } + if co.TrustedMaterial != nil { + entity := &signedEntityForTimestamp{ + timestamp: ts, + sigContent: &sigContent{rawSig: tsBytes}, + } + verifiedTimestamps, verifyErrs, err := verify.VerifySignedTimestamp(entity, co.TrustedMaterial) + if err != nil { + return nil, fmt.Errorf("unable to verify signed timestamps with trusted root: %w", err) + } + if len(verifyErrs) > 0 { + log.Printf("Warning: subset of signed timestamps failed to verify: %v", verifyErrs) + } + return ×tamp.Timestamp{Time: verifiedTimestamps[0].Time}, nil + } + return tsaverification.VerifyTimestampResponse(ts.SignedRFC3161Timestamp, bytes.NewReader(tsBytes), tsaverification.VerifyOpts{ TSACertificate: co.TSACertificate, @@ -1464,8 +1551,8 @@ func correctAnnotations(wanted, have map[string]interface{}) bool { // If there were no valid signatures, we return an error, using OCI 1.1+ behavior. func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { // Enforce this up front. - if co.RootCerts == nil && co.SigVerifier == nil { - return nil, false, errors.New("one of verifier or root certs is required") + if co.RootCerts == nil && co.SigVerifier == nil && co.TrustedMaterial == nil { + return nil, false, errors.New("one of verifier, root certs, or trusted root is required") } // This is a carefully optimized sequence for fetching the signatures of the diff --git a/pkg/cosign/verify_bundle.go b/pkg/cosign/verify_bundle.go index 85a9a660283..9c6daf88140 100644 --- a/pkg/cosign/verify_bundle.go +++ b/pkg/cosign/verify_bundle.go @@ -27,7 +27,7 @@ func VerifyNewBundle(_ context.Context, co *CheckOpts, artifactPolicyOption veri if err != nil { return nil, err } - verifier, err := verify.NewSignedEntityVerifier(trustedMaterial, verifierOptions...) + verifier, err := verify.NewVerifier(trustedMaterial, verifierOptions...) if err != nil { return nil, err } diff --git a/pkg/cosign/verify_sct.go b/pkg/cosign/verify_sct.go index 1b904c2c4fd..444c488149c 100644 --- a/pkg/cosign/verify_sct.go +++ b/pkg/cosign/verify_sct.go @@ -19,7 +19,6 @@ import ( "crypto/x509" "encoding/hex" "encoding/json" - "errors" "fmt" "os" @@ -50,7 +49,7 @@ func getCTPublicKey(sct *ct.SignedCertificateTimestamp, keyID := hex.EncodeToString(sct.LogID.KeyID[:]) pubKeyMetadata, ok := pubKeys.Keys[keyID] if !ok { - return nil, errors.New("ctfe public key not found for payload. Check your TUF root (see cosign initialize) or set a custom key with env var SIGSTORE_CT_LOG_PUBLIC_KEY_FILE") + return nil, fmt.Errorf("ctfe public key not found for payload. Check your TUF root (see cosign initialize) or set a custom key with env var SIGSTORE_CT_LOG_PUBLIC_KEY_FILE") } return &pubKeyMetadata, nil } @@ -73,7 +72,7 @@ func getCTPublicKey(sct *ct.SignedCertificateTimestamp, // an alternate, the file can be PEM, or DER format. func VerifySCT(_ context.Context, certPEM, chainPEM, rawSCT []byte, pubKeys *TrustedTransparencyLogPubKeys) error { if pubKeys == nil || len(pubKeys.Keys) == 0 { - return errors.New("none of the CTFE keys have been found") + return fmt.Errorf("none of the CTFE keys have been found") } // parse certificate and chain @@ -86,7 +85,7 @@ func VerifySCT(_ context.Context, certPEM, chainPEM, rawSCT []byte, pubKeys *Tru return err } if len(certChain) == 0 { - return errors.New("no certificate chain found") + return fmt.Errorf("no certificate chain found") } // fetch embedded SCT if present @@ -96,7 +95,7 @@ func VerifySCT(_ context.Context, certPEM, chainPEM, rawSCT []byte, pubKeys *Tru } // SCT must be either embedded or in header if len(embeddedSCTs) == 0 && len(rawSCT) == 0 { - return errors.New("no SCT found") + return fmt.Errorf("no SCT found") } // check SCT embedded in certificate @@ -143,7 +142,7 @@ func VerifySCT(_ context.Context, certPEM, chainPEM, rawSCT []byte, pubKeys *Tru // VerifyEmbeddedSCT verifies an embedded SCT in a certificate. func VerifyEmbeddedSCT(ctx context.Context, chain []*x509.Certificate, pubKeys *TrustedTransparencyLogPubKeys) error { if len(chain) < 2 { - return errors.New("certificate chain must contain at least a certificate and its issuer") + return fmt.Errorf("certificate chain must contain at least a certificate and its issuer") } certPEM, err := cryptoutils.MarshalCertificateToPEM(chain[0]) if err != nil { diff --git a/release/cloudbuild.yaml b/release/cloudbuild.yaml index e6864a3611f..edf55c07177 100644 --- a/release/cloudbuild.yaml +++ b/release/cloudbuild.yaml @@ -38,14 +38,14 @@ steps: - TUF_ROOT=/tmp args: - 'verify' - - 'ghcr.io/gythialy/golang-cross:v1.24.3-0@sha256:b0e66440a1dc4216c45d9df95ac9c34b9cb2e7de1d9e55a94914eb38c2ec2249' + - 'ghcr.io/gythialy/golang-cross:v1.24.4-0@sha256:0b29abd58891e1b3dc915efbfec697f93151118e20c13860ac1c8667ef14fb24' - '--certificate-oidc-issuer' - "https://token.actions.githubusercontent.com" - '--certificate-identity' - - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.24.3-0" + - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.24.4-0" # maybe we can build our own image and use that to be more in a safe side - - name: ghcr.io/gythialy/golang-cross:v1.24.3-0@sha256:b0e66440a1dc4216c45d9df95ac9c34b9cb2e7de1d9e55a94914eb38c2ec2249 + - name: ghcr.io/gythialy/golang-cross:v1.24.4-0@sha256:0b29abd58891e1b3dc915efbfec697f93151118e20c13860ac1c8667ef14fb24 entrypoint: /bin/sh dir: "go/src/sigstore/cosign" env: @@ -68,7 +68,7 @@ steps: gcloud auth configure-docker \ && make release - - name: ghcr.io/gythialy/golang-cross:v1.24.3-0@sha256:b0e66440a1dc4216c45d9df95ac9c34b9cb2e7de1d9e55a94914eb38c2ec2249 + - name: ghcr.io/gythialy/golang-cross:v1.24.4-0@sha256:0b29abd58891e1b3dc915efbfec697f93151118e20c13860ac1c8667ef14fb24 entrypoint: 'bash' dir: "go/src/sigstore/cosign" env: diff --git a/test/e2e_test.go b/test/e2e_test.go index f43d8e46fe6..4c84ba671bb 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -60,6 +60,7 @@ import ( "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/publickey" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/trustedroot" cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify" "github.com/sigstore/cosign/v2/internal/pkg/cosign/fulcio/fulcioroots" "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" @@ -555,6 +556,38 @@ func downloadTSACerts(downloadDirectory string, tsaServer string) (string, strin return leafPath, intermediatePath, rootPath, nil } +func prepareTrustedRoot(t *testing.T, tsaURL string) string { + downloadDirectory := t.TempDir() + caPath := filepath.Join(downloadDirectory, "fulcio.crt.pem") + caFP, err := os.Create(caPath) + must(err, t) + defer caFP.Close() + must(downloadFile(fulcioURL+"/api/v1/rootCert", caFP), t) + rekorPath := filepath.Join(downloadDirectory, "rekor.pub") + rekorFP, err := os.Create(rekorPath) + must(err, t) + defer rekorFP.Close() + must(downloadFile(rekorURL+"/api/v1/log/publicKey", rekorFP), t) + ctfePath := filepath.Join(downloadDirectory, "ctfe.pub") + home, err := os.UserHomeDir() + must(err, t) + must(copyFile(filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), ctfePath), t) + tsaPath := filepath.Join(downloadDirectory, "tsa.crt.pem") + tsaFP, err := os.Create(tsaPath) + must(err, t) + must(downloadFile(tsaURL+"/api/v1/timestamp/certchain", tsaFP), t) + out := filepath.Join(downloadDirectory, "trusted_root.json") + cmd := &trustedroot.CreateCmd{ + CertChain: []string{caPath}, + CtfeKeyPath: []string{ctfePath}, + Out: out, + RekorKeyPath: []string{rekorPath}, + TSACertChainPath: []string{tsaPath}, + } + must(cmd.Exec(context.TODO()), t) + return out +} + func TestSignVerifyWithTUFMirror(t *testing.T) { home, err := os.UserHomeDir() // fulcio repo was downloaded to $HOME in e2e_test.sh must(err, t) @@ -572,6 +605,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { mirror := tufServer.URL tsaLeaf, tsaInter, tsaRoot, err := downloadTSACerts(t.TempDir(), tsaServer.URL) must(err, t) + trustedRoot := prepareTrustedRoot(t, tsaServer.URL) tests := []struct { name string targets []targetInfo @@ -687,6 +721,15 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { }, }, }, + { + name: "trusted root", + targets: []targetInfo{ + { + name: "trusted_root.json", + source: trustedRoot, + }, + }, + }, } tuf, err := newTUF(tufMirror, tests[0].targets) must(err, t) @@ -704,6 +747,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { t.Fatal(err) } + // Sign an image repo, stop := reg(t) defer stop() imgName := path.Join(repo, "cosign-e2e-tuf") @@ -717,6 +761,10 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { SkipConfirmation: true, TSAServerURL: tsaServer.URL + "/api/v1/timestamp", } + trustedMaterial, err := cosign.TrustedRoot() + if err == nil { + ko.TrustedMaterial = trustedMaterial + } so := options.SignOptions{ Upload: true, TlogUpload: true, @@ -728,6 +776,8 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { return } must(gotErr, t) + + // Verify an image issuer := os.Getenv("OIDC_URL") verifyCmd := cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ @@ -744,6 +794,42 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { } else { must(gotErr, t) } + + // Sign a blob + blob := "someblob" + blobDir := t.TempDir() + bp := filepath.Join(blobDir, blob) + if err := os.WriteFile(bp, []byte(blob), 0644); err != nil { + t.Fatal(err) + } + tsPath := filepath.Join(blobDir, "ts.txt") + bundlePath := filepath.Join(blobDir, "bundle.sig") + // TODO(cmurphy): make this work with ko.NewBundleFormat = true + ko.BundlePath = bundlePath + ko.RFC3161TimestampPath = tsPath + _, gotErr = sign.SignBlobCmd(ro, ko, bp, true, "", "", true) + if test.wantSignErr { + mustErr(gotErr, t) + } else { + must(gotErr, t) + } + + // Verify a blob + verifyBlobCmd := cliverify.VerifyBlobCmd{ + KeyOpts: ko, + CertVerifyOptions: options.CertVerifyOptions{ + CertOidcIssuer: issuer, + CertIdentity: certID, + }, + Offline: true, + UseSignedTimestamps: true, + } + gotErr = verifyBlobCmd.Exec(ctx, bp) + if test.wantVerifyErr { + mustErr(gotErr, t) + } else { + must(gotErr, t) + } }) } }