Repository: sigstore/cosign
Pull request #3031 (2023-06-02 14:19:57)

Deep inspect attestations when filtering download
<!--
Thanks for opening a pull request! Please do not just delete this text.  The three fields below are mandatory.

Please remember to:
- This PR requires an issue. If it is a new feature, the issue should proceed the PR and will have allowed sufficent time for discussions to take place. Please use
issue tags such as "Closes #XYZ" or "Resolves sigstore/repo-name#XYZ".
  [documentation](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)
- ensure your commits are signed-off, as sigstore uses the [DCO](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) using `git commit -s`, or `git commit -s --amend` if you want to amend already existing commits
- lastly, ensure there are no merge commits!

Thank you :)
-->

#### Summary
<!--
 Explain the **motivation** for making this change. What existing problem does the pull request solve? How can reviewers test this PR?
-->

Resolves https://github.com/sigstore/cosign/issues/3030 by inspecting the attestation payload when filtering by predicate type. See additional context there.

#### Release Note
<!--
Add a release note for each of the following conditions:

* Config changes (additions, deletions, updates)
* API additions—new endpoint, new response fields, or newly accepted request parameters
* Database changes (any)
* Websocket additions or changes
* Anything noteworthy to an administrator running private sigstore instances (err on the side of over-communicating)
* New features and improvements, including behavioural changes, UI changes and CLI changes
* Bug fixes and fixes of previous known issues
* Deprecation warnings, breaking changes, or compatibility notes

If no release notes are required write NONE. Use past-tense.

-->

```
Fix predicate type filtering in `cosign download attestation` command.
```

#### Documentation
<!--

Does this change require an update to documentation? How will users implement your new feature?

Please reference a PR within https://docs.sigstore.dev

-->

Updated the command docs to no longer mention the predicate type is fetched from annotations.


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sigstore/cosign/pull/3031?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) Report
> Merging [#3031](https://app.codecov.io/gh/sigstore/cosign/pull/3031?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) (fc238bf) into [main](https://app.codecov.io/gh/sigstore/cosign/commit/83c08ce4f35116da7c74350a67d5740ca5e28ca5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) (83c08ce) will **decrease** coverage by `0.03%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head fc238bf differs from pull request most recent head 1aba417. Consider uploading reports for the commit 1aba417 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #3031      +/-   ##
==========================================
- Coverage   30.45%   30.42%   -0.03%     
==========================================
  Files         152      152              
  Lines        9586     9593       +7     
==========================================
  Hits         2919     2919              
- Misses       6210     6217       +7     
  Partials      457      457              
```


| [Impacted Files](https://app.codecov.io/gh/sigstore/cosign/pull/3031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) | Coverage Δ | |
|---|---|---|
| [cmd/cosign/cli/options/download.go](https://app.codecov.io/gh/sigstore/cosign/pull/3031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#diff-Y21kL2Nvc2lnbi9jbGkvb3B0aW9ucy9kb3dubG9hZC5nbw==) | `0.00% <0.00%> (ø)` | |
| [pkg/cosign/fetch.go](https://app.codecov.io/gh/sigstore/cosign/pull/3031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#diff-cGtnL2Nvc2lnbi9mZXRjaC5nbw==) | `0.00% <0.00%> (ø)` | |



Patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index da5508ef..149baca8 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -41,7 +41,7 @@ jobs:
       contents: read
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
       - uses: sigstore/cosign-installer@dd6b2e2b610a11fd73dd187a43d57cc1394e35f9 # v3.0.5
 
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 39ea90fb..552d2b10 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -47,7 +47,7 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
     - name: Utilize Go Module Cache
       uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
@@ -67,7 +67,7 @@ jobs:
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+      uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
       with:
         languages: ${{ matrix.language }}
 
@@ -75,4 +75,4 @@ jobs:
       run: make cosign
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+      uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
diff --git a/.github/workflows/cross.yaml b/.github/workflows/cross.yaml
index 2e16a4a9..19af62f3 100644
--- a/.github/workflows/cross.yaml
+++ b/.github/workflows/cross.yaml
@@ -39,7 +39,7 @@ jobs:
           go-version: '1.20.x'
           check-latest: true
       - name: Checkout code
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - name: build cosign
         run: |
           make cosign && mv ./cosign ./${{matrix.COSIGN_TARGET}}
diff --git a/.github/workflows/donotsubmit.yaml b/.github/workflows/donotsubmit.yaml
index 51f4cb3d..cfe3ec02 100644
--- a/.github/workflows/donotsubmit.yaml
+++ b/.github/workflows/donotsubmit.yaml
@@ -14,7 +14,7 @@ jobs:
 
     steps:
       - name: Check out code
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab #v2.4.0
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 #v2.4.0
 
       - name: Do Not Submit
         uses: chainguard-dev/actions/donotsubmit@84c993eaf02da1c325854fb272a4df9184bd80fc # main
diff --git a/.github/workflows/github-oidc.yaml b/.github/workflows/github-oidc.yaml
index 7f541bdf..7b796eaf 100644
--- a/.github/workflows/github-oidc.yaml
+++ b/.github/workflows/github-oidc.yaml
@@ -42,7 +42,7 @@ jobs:
       KO_PREFIX: ghcr.io/${{ github.repository }}
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: '1.20.x'
diff --git a/.github/workflows/scorecard-action.yml b/.github/workflows/scorecard-action.yml
index 93566681..358168a9 100644
--- a/.github/workflows/scorecard-action.yml
+++ b/.github/workflows/scorecard-action.yml
@@ -23,7 +23,7 @@ jobs:
       id-token: write
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
         with:
           persist-credentials: false
 
@@ -52,6 +52,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml
index 14e40575..f4e53a46 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.20.4-0@sha256:c9f009c018d406f08e6e2e601eb6945f66a003112a220dca039621078e565188 \
+          cosign verify ghcr.io/gythialy/golang-cross:v1.20.5-0@sha256:d74efd682562c4da37de085b50dca0bfdc7a7a778de6412c9d277e2bd5927e3d \
           --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.20.4-0"
+          --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.20.5-0"
         env:
           TUF_ROOT: /tmp
 
@@ -43,12 +43,12 @@ jobs:
       - check-signature
 
     container:
-      image: ghcr.io/gythialy/golang-cross:v1.20.4-0@sha256:c9f009c018d406f08e6e2e601eb6945f66a003112a220dca039621078e565188
+      image: ghcr.io/gythialy/golang-cross:v1.20.5-0@sha256:d74efd682562c4da37de085b50dca0bfdc7a7a778de6412c9d277e2bd5927e3d
 
     permissions: {}
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
       # Error: fatal: detected dubious ownership in repository at '/__w/cosign/cosign'
       #      To add an exception for this directory, call:
diff --git a/.github/workflows/verify-docgen.yaml b/.github/workflows/verify-docgen.yaml
index ec5f4b2c..a3c5dd0f 100644
--- a/.github/workflows/verify-docgen.yaml
+++ b/.github/workflows/verify-docgen.yaml
@@ -31,7 +31,7 @@ jobs:
     steps:
       - name: deps
         run: sudo apt-get update && sudo apt-get install -yq libpcsclite-dev
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: '1.20.x'
diff --git a/.github/workflows/whitespace.yaml b/.github/workflows/whitespace.yaml
index c5fd7c65..95ec2867 100644
--- a/.github/workflows/whitespace.yaml
+++ b/.github/workflows/whitespace.yaml
@@ -14,7 +14,7 @@ jobs:
 
     steps:
       - name: Check out code
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
       - uses: chainguard-dev/actions/trailing-space@84c993eaf02da1c325854fb272a4df9184bd80fc # main
         if: ${{ always() }}
diff --git a/.golangci.yml b/.golangci.yml
index deaceaac..eeb8a1aa 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -43,6 +43,81 @@ linters-settings:
       - 'os\.Getenv.*'
       - 'os\.LookupEnv.*'
     exclude_godoc_examples: false
+  depguard:
+    rules:
+      main:
+        files:
+          - $all
+          - "!$test"
+        allow:
+          - $gostd
+
+          - github.com/open-policy-agent/opa/rego
+          - github.com/xanzy/go-gitlab
+          - github.com/spf13/cobra
+          - github.com/spf13/viper
+          - github.com/spf13/pflag
+          - github.com/pkg/errors
+          - github.com/moby/term
+          - github.com/google/go-cmp/cmp
+          - github.com/in-toto/in-toto-golang
+          - github.com/secure-systems-lab/go-securesystemslib
+          - github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer
+          - github.com/digitorus/timestamp
+          - github.com/transparency-dev/merkle
+          - github.com/google/certificate-transparency-go
+          - github.com/go-openapi
+          - github.com/theupdateframework/go-tuf
+          - github.com/kelseyhightower/envconfig
+          - github.com/google/go-github
+          - github.com/buildkite/agent
+          - github.com/mitchellh/go-wordwrap
+          - github.com/withfig/autocomplete-tools/integrations/cobra
+          - github.com/awslabs/amazon-ecr-credential-helper/ecr-login
+          - github.com/chrismellard/docker-credential-acr-env/pkg/credhelper
+          - github.com/mozillazg/docker-credential-acr-helper/pkg/credhelper
+
+          - cuelang.org/go/cue/load
+          - cuelang.org/go/cue/cuecontext
+          - cuelang.org/go/encoding/json
+
+          - k8s.io/utils/pointer
+          - k8s.io/client-go
+          - k8s.io/api/core
+          - k8s.io/apimachinery
+          - sigs.k8s.io/release-utils/version
+
+          - github.com/spiffe/go-spiffe
+          - github.com/google/go-containerregistry
+          - github.com/sigstore
+
+      test:
+        files:
+          - $test
+        allow:
+          - $gostd
+          - github.com/google/go-cmp/cmp
+          - github.com/go-openapi
+          - github.com/google/go-containerregistry
+          - github.com/in-toto/in-toto-golang
+          - github.com/stretchr/testify
+          - github.com/pkg/errors
+          - github.com/theupdateframework/go-tuf
+          - github.com/google/certificate-transparency-go
+          - github.com/secure-systems-lab/go-securesystemslib
+          - github.com/transparency-dev/merkle/rfc6962
+          - github.com/depcheck-test/depcheck-test/depcheck
+          - github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer
+
+          - k8s.io/api/core
+          - k8s.io/apimachinery
+          - k8s.io/utils/pointer
+
+          - github.com/sigstore/cosign
+          - github.com/sigstore/sigstore
+          - github.com/sigstore/rekor
+          - github.com/sigstore/fulcio
+
 output:
   uniq-by-line: false
 issues:
diff --git a/Makefile b/Makefile
index 2ce7d7ba..91e9a4d9 100644
--- a/Makefile
+++ b/Makefile
@@ -98,7 +98,7 @@ cross:
 golangci-lint:
 	rm -f $(GOLANGCI_LINT_BIN) || :
 	set -e ;\
-	GOBIN=$(GOLANGCI_LINT_DIR) $(GOEXE) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 ;\
+	GOBIN=$(GOLANGCI_LINT_DIR) $(GOEXE) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2	 ;\
 
 lint: golangci-lint ## Run golangci-lint linter
 	$(GOLANGCI_LINT_BIN) run -n
diff --git a/cmd/cosign/cli/attach.go b/cmd/cosign/cli/attach.go
index 3ab3787b..3d43623d 100644
--- a/cmd/cosign/cli/attach.go
+++ b/cmd/cosign/cli/attach.go
@@ -49,7 +49,7 @@ func attachSignature() *cobra.Command {
 		PersistentPreRun: options.BindViper,
 		Args:             cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return attach.SignatureCmd(cmd.Context(), o.Registry, o.Signature, o.Payload, o.Cert, o.CertChain, args[0])
+			return attach.SignatureCmd(cmd.Context(), o.Registry, o.Signature, o.Payload, o.Cert, o.CertChain, o.TimeStampedSig, args[0])
 		},
 	}
 
diff --git a/cmd/cosign/cli/attach/sig.go b/cmd/cosign/cli/attach/sig.go
index f1ad36cd..a0b59951 100644
--- a/cmd/cosign/cli/attach/sig.go
+++ b/cmd/cosign/cli/attach/sig.go
@@ -23,15 +23,15 @@ import (
 	"path/filepath"
 
 	"github.com/google/go-containerregistry/pkg/name"
-
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
 	"github.com/sigstore/cosign/v2/pkg/cosign"
+	"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
 	"github.com/sigstore/cosign/v2/pkg/oci/mutate"
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
 	"github.com/sigstore/cosign/v2/pkg/oci/static"
 )
 
-func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, imageRef string) error {
+func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef, payloadRef, certRef, certChainRef, timeStampedSigRef, imageRef string) error {
 	b64SigBytes, err := signatureBytes(sigRef)
 	if err != nil {
 		return err
@@ -73,6 +73,7 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,
 
 	var cert []byte
 	var certChain []byte
+	var timeStampedSig []byte
 
 	if certRef != "" {
 		cert, err = os.ReadFile(filepath.Clean(certRef))
@@ -88,7 +89,15 @@ func SignatureCmd(ctx context.Context, regOpts options.RegistryOptions, sigRef,
 		}
 	}
 
-	newSig, err := mutate.Signature(sig, mutate.WithCertChain(cert, certChain))
+	if timeStampedSigRef != "" {
+		timeStampedSig, err = os.ReadFile(filepath.Clean(timeStampedSigRef))
+		if err != nil {
+			return err
+		}
+	}
+	bundle := bundle.TimestampToRFC3161Timestamp(timeStampedSig)
+
+	newSig, err := mutate.Signature(sig, mutate.WithCertChain(cert, certChain), mutate.WithRFC3161Timestamp(bundle))
 	if err != nil {
 		return err
 	}
diff --git a/cmd/cosign/cli/options/attach.go b/cmd/cosign/cli/options/attach.go
index 5c026635..036ed1f1 100644
--- a/cmd/cosign/cli/options/attach.go
+++ b/cmd/cosign/cli/options/attach.go
@@ -27,11 +27,12 @@ import (
 
 // AttachSignatureOptions is the top level wrapper for the attach signature command.
 type AttachSignatureOptions struct {
-	Signature string
-	Payload   string
-	Cert      string
-	CertChain string
-	Registry  RegistryOptions
+	Signature      string
+	Payload        string
+	Cert           string
+	CertChain      string
+	TimeStampedSig string
+	Registry       RegistryOptions
 }
 
 var _ Interface = (*AttachSignatureOptions)(nil)
@@ -54,6 +55,8 @@ func (o *AttachSignatureOptions) AddFlags(cmd *cobra.Command) {
 			"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. Included in the OCI Signature")
+	cmd.Flags().StringVar(&o.TimeStampedSig, "tsr", "",
+		"path to the Time Stamped Signature Response from RFC3161 compliant TSA")
 }
 
 // AttachSBOMOptions is the top level wrapper for the attach sbom command.
diff --git a/cmd/cosign/cli/options/download.go b/cmd/cosign/cli/options/download.go
index 5a28196c..b5e58471 100644
--- a/cmd/cosign/cli/options/download.go
+++ b/cmd/cosign/cli/options/download.go
@@ -40,7 +40,7 @@ func (o *SBOMDownloadOptions) AddFlags(cmd *cobra.Command) {
 // AddFlags implements Interface
 func (o *AttestationDownloadOptions) AddFlags(cmd *cobra.Command) {
 	cmd.Flags().StringVar(&o.PredicateType, "predicate-type", "",
-		"download attestation with matching predicateType annotation")
+		"download attestation with matching predicateType")
 	cmd.Flags().StringVar(&o.Platform, "platform", "",
 		"download attestation for a specific platform image")
 }
diff --git a/cmd/cosign/cli/options/oidc.go b/cmd/cosign/cli/options/oidc.go
index 580b93d4..c68421be 100644
--- a/cmd/cosign/cli/options/oidc.go
+++ b/cmd/cosign/cli/options/oidc.go
@@ -70,7 +70,7 @@ func (o *OIDCOptions) AddFlags(cmd *cobra.Command) {
 		"OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'.")
 
 	cmd.Flags().StringVar(&o.Provider, "oidc-provider", "",
-		"Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem, buildkite-agent]")
+		"Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]")
 
 	cmd.Flags().BoolVar(&o.DisableAmbientProviders, "oidc-disable-ambient-providers", false,
 		"Disable ambient OIDC providers. When true, ambient credentials will not be read")
diff --git a/cmd/cosign/cli/options/predicate.go b/cmd/cosign/cli/options/predicate.go
index 4ee874e7..707a38ca 100644
--- a/cmd/cosign/cli/options/predicate.go
+++ b/cmd/cosign/cli/options/predicate.go
@@ -87,6 +87,7 @@ func (o *PredicateLocalOptions) AddFlags(cmd *cobra.Command) {
 
 	cmd.Flags().StringVar(&o.Path, "predicate", "",
 		"path to the predicate file.")
+	_ = cmd.MarkFlagRequired("predicate")
 }
 
 // PredicateRemoteOptions is the wrapper for remote predicate related options.
diff --git a/cmd/cosign/cli/options/registry.go b/cmd/cosign/cli/options/registry.go
index d6f50448..425a7c60 100644
--- a/cmd/cosign/cli/options/registry.go
+++ b/cmd/cosign/cli/options/registry.go
@@ -44,6 +44,9 @@ type RegistryOptions struct {
 	KubernetesKeychain bool
 	RefOpts            ReferenceOptions
 	Keychain           Keychain
+
+	// RegistryClientOpts allows overriding the result of GetRegistryClientOpts.
+	RegistryClientOpts []remote.Option
 }
 
 var _ Interface = (*RegistryOptions)(nil)
@@ -86,6 +89,12 @@ func (o *RegistryOptions) NameOptions() []name.Option {
 }
 
 func (o *RegistryOptions) GetRegistryClientOpts(ctx context.Context) []remote.Option {
+	if o.RegistryClientOpts != nil {
+		ropts := o.RegistryClientOpts
+		ropts = append(ropts, remote.WithContext(ctx))
+		return ropts
+	}
+
 	opts := []remote.Option{
 		remote.WithContext(ctx),
 		remote.WithUserAgent(UserAgent()),
diff --git a/cmd/cosign/cli/options/sign.go b/cmd/cosign/cli/options/sign.go
index d2cda047..0359fa8e 100644
--- a/cmd/cosign/cli/options/sign.go
+++ b/cmd/cosign/cli/options/sign.go
@@ -21,21 +21,22 @@ import (
 
 // SignOptions is the top level wrapper for the sign command.
 type SignOptions struct {
-	Key               string
-	Cert              string
-	CertChain         string
-	Upload            bool
-	Output            string // deprecated: TODO remove when the output flag is fully deprecated
-	OutputSignature   string // TODO: this should be the root output file arg.
-	OutputPayload     string
-	OutputCertificate string
-	PayloadPath       string
-	Recursive         bool
-	Attachment        string
-	SkipConfirmation  bool
-	TlogUpload        bool
-	TSAServerURL      string
-	IssueCertificate  bool
+	Key                   string
+	Cert                  string
+	CertChain             string
+	Upload                bool
+	Output                string // deprecated: TODO remove when the output flag is fully deprecated
+	OutputSignature       string // TODO: this should be the root output file arg.
+	OutputPayload         string
+	OutputCertificate     string
+	PayloadPath           string
+	Recursive             bool
+	Attachment            string
+	SkipConfirmation      bool
+	TlogUpload            bool
+	TSAServerURL          string
+	IssueCertificate      bool
+	SignContainerIdentity string
 
 	Rekor       RekorOptions
 	Fulcio      FulcioOptions
@@ -108,4 +109,7 @@ func (o *SignOptions) AddFlags(cmd *cobra.Command) {
 
 	cmd.Flags().BoolVar(&o.IssueCertificate, "issue-certificate", false,
 		"issue a code signing certificate from Fulcio, even if a key is provided")
+
+	cmd.Flags().StringVar(&o.SignContainerIdentity, "sign-container-identity", "",
+		"manually set the .critical.docker-reference field for the signed identity, which is useful when image proxies are being used where the pull reference should match the signature")
 }
diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go
index 879c46cf..78d51381 100644
--- a/cmd/cosign/cli/sign.go
+++ b/cmd/cosign/cli/sign.go
@@ -79,7 +79,10 @@ race conditions or (worse) malicious tampering.
   cosign sign --key cosign.key <IMAGE DIGEST>
 
   # sign a container image and skip uploading to the transparency log
-  cosign sign --key cosign.key --tlog-upload=false <IMAGE DIGEST>`,
+  cosign sign --key cosign.key --tlog-upload=false <IMAGE DIGEST>
+
+  # sign a container image by manually setting the container image identity
+  cosign sign --sign-container-identity <NEW IMAGE DIGEST> <IMAGE DIGEST>`,
 
 		Args:             cobra.MinimumNArgs(1),
 		PersistentPreRun: options.BindViper,
diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go
index a09bc339..eaea6d72 100644
--- a/cmd/cosign/cli/sign/sign.go
+++ b/cmd/cosign/cli/sign/sign.go
@@ -179,7 +179,9 @@ func SignCmd(ro *options.RootOptions, ko options.KeyOpts, signOpts options.SignO
 
 		if digest, ok := ref.(name.Digest); ok && !signOpts.Recursive {
 			se, err := ociremote.SignedEntity(ref, opts...)
-			if err != nil {
+			if err == ociremote.ErrEntityNotFound {
+				se = ociremote.SignedUnknown(digest)
+			} else if err != nil {
 				return fmt.Errorf("accessing image: %w", err)
 			}
 			err = signDigest(ctx, digest, staticPayload, ko, signOpts, annotations, dd, sv, se)
@@ -221,8 +223,9 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko opti
 	// The payload can be passed to skip generation.
 	if len(payload) == 0 {
 		payload, err = (&sigPayload.Cosign{
-			Image:       digest,
-			Annotations: annotations,
+			Image:           digest,
+			ClaimedIdentity: signOpts.SignContainerIdentity,
+			Annotations:     annotations,
 		}).MarshalJSON()
 		if err != nil {
 			return fmt.Errorf("payload: %w", err)
diff --git a/cmd/cosign/errors/error_wrap.go b/cmd/cosign/errors/error_wrap.go
index 96c4fad6..4505b2ac 100644
--- a/cmd/cosign/errors/error_wrap.go
+++ b/cmd/cosign/errors/error_wrap.go
@@ -15,30 +15,15 @@
 
 package errors
 
-import (
-	"errors"
-
-	verificationError "github.com/sigstore/cosign/v2/pkg/cosign"
-)
-
 // WrapError takes an error type and depending on the type of error
 // passed, will access it's error message and errorType (and return
 // the associated exitCode) and wrap them in a generic `CosignError`.
 // If no custom error has been found, then it will still return a
 // `CosignError` with an error message, but the `exitCode` will be `1`.
 func WrapError(err error) error {
-	// VerificationError
-	var verificationError *verificationError.VerificationError
-	if errors.As(err, &verificationError) {
-		return &CosignError{
-			Message: verificationError.Error(),
-			Code:    LookupExitCodeForErrorType(verificationError.ErrorType()),
-		}
-	}
-
 	// return default cosign error with error message and default exit code
 	return &CosignError{
 		Message: err.Error(),
-		Code:    1,
+		Code:    LookupExitCodeForError(err),
 	}
 }
diff --git a/cmd/cosign/errors/exit_code_lookup.go b/cmd/cosign/errors/exit_code_lookup.go
index 7bc40781..93d097ab 100644
--- a/cmd/cosign/errors/exit_code_lookup.go
+++ b/cmd/cosign/errors/exit_code_lookup.go
@@ -16,25 +16,49 @@
 package errors
 
 import (
-	verificationError "github.com/sigstore/cosign/v2/pkg/cosign"
+	"errors"
+
+	cosignError "github.com/sigstore/cosign/v2/pkg/cosign"
 )
 
-// exitCodeLookup contains a map of errorTypes and their associated exitCodes.
-var exitCodeLookup = map[string]int{
-	verificationError.ErrNoMatchingSignaturesType: NoMatchingSignature,
-	verificationError.ErrImageTagNotFoundType:     NonExistentTag,
-	verificationError.ErrNoSignaturesFoundType:    ImageWithoutSignature,
-}
+func LookupExitCodeForError(err interface{ error }) int {
+	if noMatchingSignatureError(err) {
+		return NoMatchingSignature
+	}
+
+	if imageTagNotFoundError(err) {
+		return NonExistentTag
+	}
 
-func LookupExitCodeForErrorType(errorType string) int {
-	exitCode := exitCodeLookup[errorType]
+	if noSignaturesFoundError(err) {
+		return ImageWithoutSignature
+	}
 
-	// if there is no entry in the lookup map for the passed errorType,
-	// then by default, it will return `0`. however, as `0` as an exitCode
-	// for success, we want to return `1` instead until there is a valid
-	// exit code entry in the map for the passed errorType.
-	if exitCode == 0 {
-		return 1
+	if noCertificateFoundOnSignature(err) {
+		return NoCertificateFoundOnSignature
 	}
-	return exitCode
+
+	// we want to return exit code = `1` at this point because there is
+	// no valid exit code found for the error type passed, so we default to 1.
+	return 1
+}
+
+func noMatchingSignatureError(err interface{ error }) bool {
+	var errNoMatchingSignatures *cosignError.ErrNoMatchingSignatures
+	return errors.As(err, &errNoMatchingSignatures)
+}
+
+func imageTagNotFoundError(err interface{ error }) bool {
+	var errImageTagNotFound *cosignError.ErrImageTagNotFound
+	return errors.As(err, &errImageTagNotFound)
+}
+
+func noSignaturesFoundError(err interface{ error }) bool {
+	var errNoSignaturesFound *cosignError.ErrNoSignaturesFound
+	return errors.As(err, &errNoSignaturesFound)
+}
+
+func noCertificateFoundOnSignature(err interface{ error }) bool {
+	var errNoCertificateFoundOnSignature *cosignError.ErrNoCertificateFoundOnSignature
+	return errors.As(err, &errNoCertificateFoundOnSignature)
 }
diff --git a/cmd/cosign/errors/exit_codes.go b/cmd/cosign/errors/exit_codes.go
index 5454198e..62287694 100644
--- a/cmd/cosign/errors/exit_codes.go
+++ b/cmd/cosign/errors/exit_codes.go
@@ -33,3 +33,6 @@ const NonExistentTag = 11
 
 // Error verifying image due to no matching signature
 const NoMatchingSignature = 12
+
+// Error verifying image due to no certificate found on signature
+const NoCertificateFoundOnSignature = 13
diff --git a/doc/cosign_attach_signature.md b/doc/cosign_attach_signature.md
index 861a142e..d1334e46 100644
--- a/doc/cosign_attach_signature.md
+++ b/doc/cosign_attach_signature.md
@@ -24,6 +24,7 @@ cosign attach signature [flags]
       --k8s-keychain                                                                             whether to use the kubernetes keychain instead of the default keychain (supports workload identity).
       --payload string                                                                           path to the payload covered by the signature
       --signature string                                                                         path to the signature, or {-} for stdin
+      --tsr string                                                                               path to the Time Stamped Signature Response from RFC3161 compliant TSA
 ```
 
 ### Options inherited from parent commands
diff --git a/doc/cosign_exit_codes.md b/doc/cosign_exit_codes.md
index 5548c663..1ebaf201 100644
--- a/doc/cosign_exit_codes.md
+++ b/doc/cosign_exit_codes.md
@@ -7,3 +7,4 @@
 | 10 | Error verifying image due to no signature|
 | 11 | Error verifying image due to non-existent tag|
 | 12 | Error verifying image due to no matching signature|
+| 13 | Error verifying image due to no certificate found on signature|
diff --git a/doc/cosign_sign-blob.md b/doc/cosign_sign-blob.md
index 72d4ed5e..d512d6b6 100644
--- a/doc/cosign_sign-blob.md
+++ b/doc/cosign_sign-blob.md
@@ -45,7 +45,7 @@ cosign sign-blob [flags]
       --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
       --oidc-issuer string               OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth")
-      --oidc-provider string             Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem, buildkite-agent]
+      --oidc-provider string             Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]
       --oidc-redirect-url string         OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'.
       --output string                    write the signature to FILE
       --output-certificate string        write the certificate to FILE
diff --git a/doc/cosign_sign.md b/doc/cosign_sign.md
index 655f3ae4..be48ff79 100644
--- a/doc/cosign_sign.md
+++ b/doc/cosign_sign.md
@@ -61,6 +61,9 @@ cosign sign [flags]
 
   # sign a container image and skip uploading to the transparency log
   cosign sign --key cosign.key --tlog-upload=false <IMAGE DIGEST>
+
+  # sign a container image by manually setting the container image identity
+  cosign sign --sign-container-identity <NEW IMAGE DIGEST> <IMAGE DIGEST>
 ```
 
 ### Options
@@ -84,7 +87,7 @@ cosign sign [flags]
       --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
       --oidc-issuer string                                                                       OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth")
-      --oidc-provider string                                                                     Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem, buildkite-agent]
+      --oidc-provider string                                                                     Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]
       --oidc-redirect-url string                                                                 OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'.
       --output-certificate string                                                                write the certificate to FILE
       --output-payload string                                                                    write the signed payload to FILE
@@ -93,6 +96,7 @@ cosign sign [flags]
   -r, --recursive                                                                                if a multi-arch image is specified, additionally sign each discrete image
       --registry-referrers-mode registryReferrersMode                                            mode for fetching references from the registry. allowed: legacy, oci-1-1
       --rekor-url string                                                                         address of rekor STL server (default "https://rekor.sigstore.dev")
+      --sign-container-identity string                                                           manually set the .critical.docker-reference field for the signed identity, which is useful when image proxies are being used where the pull reference should match the signature
       --sk                                                                                       whether to use a hardware security key
       --slot string                                                                              security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)
       --timestamp-server-url string                                                              url to the Timestamp RFC3161 server, default none. Must be the path to the API to request timestamp responses, e.g. https://freetsa.org/tsr
diff --git a/go.mod b/go.mod
index 738458c2..238c2243 100644
--- a/go.mod
+++ b/go.mod
@@ -6,14 +6,14 @@ require (
 	cuelang.org/go v0.5.0
 	github.com/ThalesIgnite/crypto11 v1.2.5
 	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795
-	github.com/buildkite/agent/v3 v3.47.0
+	github.com/buildkite/agent/v3 v3.48.0
 	github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21
 	github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7
 	github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936
 	github.com/digitorus/timestamp v0.0.0-20221019182153-ef3b63b79b31
 	github.com/go-openapi/runtime v0.26.0
 	github.com/go-openapi/strfmt v0.21.7
-	github.com/go-openapi/swag v0.22.3
+	github.com/go-openapi/swag v0.22.4
 	github.com/go-piv/piv-go v1.11.0
 	github.com/google/certificate-transparency-go v1.1.6
 	github.com/google/go-cmp v0.5.9
@@ -30,31 +30,31 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/secure-systems-lab/go-securesystemslib v0.6.0
 	github.com/sigstore/fulcio v1.3.1
-	github.com/sigstore/rekor v1.2.1
-	github.com/sigstore/sigstore v1.6.5
-	github.com/sigstore/sigstore/pkg/signature/kms/aws v1.6.5
-	github.com/sigstore/sigstore/pkg/signature/kms/azure v1.6.5
-	github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.6.5
-	github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.6.5
+	github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23
+	github.com/sigstore/sigstore v1.7.0
+	github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.0
+	github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.0
+	github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.0
+	github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.0
 	github.com/sigstore/timestamp-authority v1.1.1
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.16.0
-	github.com/spiffe/go-spiffe/v2 v2.1.5
+	github.com/spiffe/go-spiffe/v2 v2.1.6
 	github.com/stretchr/testify v1.8.4
 	github.com/theupdateframework/go-tuf v0.5.2
 	github.com/transparency-dev/merkle v0.0.2
 	github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
-	github.com/xanzy/go-gitlab v0.84.0
-	go.step.sm/crypto v0.31.2
-	golang.org/x/crypto v0.9.0
-	golang.org/x/oauth2 v0.8.0
-	golang.org/x/sync v0.2.0
-	golang.org/x/term v0.8.0
-	google.golang.org/api v0.125.0
-	k8s.io/api v0.26.3
-	k8s.io/apimachinery v0.26.3
-	k8s.io/client-go v0.25.4
+	github.com/xanzy/go-gitlab v0.85.0
+	go.step.sm/crypto v0.32.0
+	golang.org/x/crypto v0.10.0
+	golang.org/x/oauth2 v0.9.0
+	golang.org/x/sync v0.3.0
+	golang.org/x/term v0.9.0
+	google.golang.org/api v0.127.0
+	k8s.io/api v0.26.6
+	k8s.io/apimachinery v0.26.6
+	k8s.io/client-go v0.26.6
 	k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
 	sigs.k8s.io/release-utils v0.7.4
 )
@@ -62,12 +62,12 @@ require (
 require (
 	cloud.google.com/go/compute v1.19.3 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
-	cloud.google.com/go/iam v0.13.0 // indirect
-	cloud.google.com/go/kms v1.10.2 // indirect
+	cloud.google.com/go/iam v1.1.0 // indirect
+	cloud.google.com/go/kms v1.12.0 // indirect
 	filippo.io/edwards25519 v1.0.0 // indirect
 	github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
 	github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // indirect
@@ -98,21 +98,21 @@ require (
 	github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
 	github.com/aliyun/credentials-go v1.2.3 // indirect
 	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
-	github.com/aws/aws-sdk-go v1.44.271 // indirect
-	github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect
-	github.com/aws/aws-sdk-go-v2/config v1.18.23 // indirect
-	github.com/aws/aws-sdk-go-v2/credentials v1.13.22 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect
+	github.com/aws/aws-sdk-go v1.44.282 // indirect
+	github.com/aws/aws-sdk-go-v2 v1.18.1 // indirect
+	github.com/aws/aws-sdk-go-v2/config v1.18.26 // indirect
+	github.com/aws/aws-sdk-go-v2/credentials v1.13.25 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect
-	github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect
+	github.com/aws/aws-sdk-go-v2/service/kms v1.22.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.12.11 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.19.1 // indirect
 	github.com/aws/smithy-go v1.13.5 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
@@ -134,7 +134,7 @@ require (
 	github.com/docker/docker v23.0.5+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
 	github.com/docker/go-units v0.5.0 // indirect
-	github.com/emicklei/go-restful/v3 v3.8.0 // indirect
+	github.com/emicklei/go-restful/v3 v3.9.0 // indirect
 	github.com/emicklei/proto v1.10.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
@@ -165,9 +165,8 @@ require (
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
 	github.com/google/tink/go v1.7.0 // indirect
-	github.com/google/trillian v1.5.2 // indirect
 	github.com/google/uuid v1.3.0 // indirect
-	github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
+	github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
 	github.com/googleapis/gax-go/v2 v2.10.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -178,7 +177,7 @@ require (
 	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
 	github.com/hashicorp/go-sockaddr v1.0.2 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
-	github.com/hashicorp/vault/api v1.9.1 // indirect
+	github.com/hashicorp/vault/api v1.9.2 // indirect
 	github.com/imdario/mergo v0.3.12 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
@@ -219,7 +218,6 @@ require (
 	github.com/sassoftware/relic v7.2.1+incompatible // indirect
 	github.com/segmentio/ksuid v1.0.4 // indirect
 	github.com/shibumi/go-pathspec v1.3.0 // indirect
-	github.com/sigstore/protobuf-specs v0.1.0 // indirect
 	github.com/sirupsen/logrus v1.9.0 // indirect
 	github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
 	github.com/spf13/afero v1.9.5 // indirect
@@ -247,9 +245,9 @@ require (
 	go.uber.org/zap v1.24.0 // indirect
 	golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
 	golang.org/x/mod v0.10.0 // indirect
-	golang.org/x/net v0.10.0 // indirect
-	golang.org/x/sys v0.8.0 // indirect
-	golang.org/x/text v0.9.0 // indirect
+	golang.org/x/net v0.11.0 // indirect
+	golang.org/x/sys v0.9.0 // indirect
+	golang.org/x/text v0.10.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/tools v0.8.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 815b1ef4..d1d60fff 100644
--- a/go.sum
+++ b/go.sum
@@ -17,7 +17,7 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
 cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
+cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -31,10 +31,10 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
-cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
-cloud.google.com/go/kms v1.10.2 h1:8UePKEypK3SQ6g+4mn/s/VgE5L7XOh+FwGGRUqvY3Hw=
-cloud.google.com/go/kms v1.10.2/go.mod h1:9mX3Q6pdroWzL20pbK6RaOdBbXBEhMNgK4Pfz2bweb4=
+cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
+cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
+cloud.google.com/go/kms v1.12.0 h1:IEYV44WsGc6yVO1PlvnRlYzsHM2ImpB598Cglh/3uGw=
+cloud.google.com/go/kms v1.12.0/go.mod h1:syfpIBSOqQ/ZqK48RLPkwUhFhvbsA1SyGAq/vPohd20=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -56,8 +56,8 @@ github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo
 github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
@@ -164,30 +164,30 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
 github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 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.44.271 h1:aa+Nu2JcnFmW1TLIz/67SS7KPq1I1Adl4RmExSMjGVo=
-github.com/aws/aws-sdk-go v1.44.271/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aws/aws-sdk-go v1.44.282 h1:ZPB9QhwxmMIEC8ja0DdFowOl5fODWaZ6s2cZ40fx6r8=
+github.com/aws/aws-sdk-go v1.44.282/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250=
 github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU=
-github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY=
-github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
+github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo=
+github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
 github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA=
-github.com/aws/aws-sdk-go-v2/config v1.18.23 h1:gc3lPsAnZpwfi2exupmgHfva0JiAY2BWDg5JWYlmA28=
-github.com/aws/aws-sdk-go-v2/config v1.18.23/go.mod h1:rx0ruaQ+gk3OrLFHRRx56lA//XxP8K8uPzeNiKNuWVY=
+github.com/aws/aws-sdk-go-v2/config v1.18.26 h1:ivCHcSmKd1+9rBlqVsxZHB35eCW88KWbMdG2VL3BuBw=
+github.com/aws/aws-sdk-go-v2/config v1.18.26/go.mod h1:NVmd//z/PNl7U+ZU2EnuffxOA060JWzgbH3BnqQrUoY=
 github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.22 h1:Hp9rwJS4giQ48xqonRV/s7QcDf/wxF6UY7osRmBabvI=
-github.com/aws/aws-sdk-go-v2/credentials v1.13.22/go.mod h1:BfNcm6A9nSd+bzejDcMJ5RE+k6WbkCwWkQil7q4heRk=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.25 h1:5wROoMcUC7nAE66e0b3IIht6Tos76M4HC+GQw8MeqxU=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.25/go.mod h1:W9I2660WXSwZQ23mM1Ks72+UGeyirIxuU7/KzN7daeA=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5/go.mod h1:2hXc8ooJqF2nAznsbJQIn+7h851/bu8GVC80OVTTqf8=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0 h1:lY2Z2sBP+zSbJ6CvvmnFgPcgknoQ0OJV88AwVetRRFk=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0/go.mod h1:4zYI85WiYDhFaU1jPFVfkD7HlBcdnITDE3QxDwy4Kus=
@@ -195,18 +195,18 @@ github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0
 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 h1:LsqBpyRofMG6eDs6YGud6FhdGyIyXelAasPOZ6wWLro=
 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0/go.mod h1:IArQ3IBR00FkuraKwudKZZU32OxJfdTdwV+W5iZh3Y4=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
-github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU=
-github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
+github.com/aws/aws-sdk-go-v2/service/kms v1.22.1 h1:JV2csVfMP5pxbVG1DIvRKGdW+x/K0CNLcvZoo5lOdqE=
+github.com/aws/aws-sdk-go-v2/service/kms v1.22.1/go.mod h1:aNfh11Smy55o65PB3MyKbkM8BFyFUcZmj1k+4g8eNfg=
 github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY=
-github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.12.11 h1:cNrMc266RsZJ8V1u1OQQONKcf9HmfxQFqgcpY7ZJBhY=
+github.com/aws/aws-sdk-go-v2/service/sso v1.12.11/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11 h1:h2VhtCE5PBiJefmlVCjJRSzBfFcQeAE10SXIGkXw1jQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w=
 github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg=
-github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 h1:uBE+Zj478pfxV98L6SEpvxYiADNjTlMNY714PJLE7uo=
-github.com/aws/aws-sdk-go-v2/service/sts v1.18.11/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
+github.com/aws/aws-sdk-go-v2/service/sts v1.19.1 h1:ehPTnLR/es8TL1fpBfq8qw9cAwOpQr47fLmZD9yhHjk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.19.1/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg=
 github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
 github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
 github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
@@ -222,8 +222,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
 github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 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.47.0 h1:Pp0KhftqesUuEzUKqvPBQpZ7E+IRm6Pt8Jx8VofMw/E=
-github.com/buildkite/agent/v3 v3.47.0/go.mod h1:WlwCY3wUBz1OWCHhz0N87MJpml8F6NP+pt2ZKB802fg=
+github.com/buildkite/agent/v3 v3.48.0 h1:lZeIE/ODOMZXZbnK2ShB5IqaJhwkaQcib7oeN2kd8ac=
+github.com/buildkite/agent/v3 v3.48.0/go.mod h1:DGCANm93sPZI2mYl9gxrm5RpXvj9m2AyeJUAr0tRIrk=
 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
 github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
 github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
@@ -312,8 +312,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
-github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
+github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
 github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -387,8 +387,8 @@ github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KA
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
+github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU=
 github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
 github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg=
@@ -400,7 +400,7 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
 github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
-github.com/go-rod/rod v0.113.1 h1:+Qb4K/vkR7BOhW6FhfhtLzUD3l11+0XlF4do+27sOQk=
+github.com/go-rod/rod v0.113.3 h1:oLiKZW721CCMwA5g7977cWfcAKQ+FuosP47Zf1QiDrA=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
@@ -526,15 +526,13 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
 github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
 github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w=
 github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM=
-github.com/google/trillian v1.5.2 h1:roGP6G8aaAch7vP08+oitPkvmZzxjTfIkguozqJ04Ok=
-github.com/google/trillian v1.5.2/go.mod h1:H8vOoa2dxd3xCdMzOOwt9kIz/3MSoJhcqLJGG8iRwbg=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
-github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
+github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
+github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
@@ -588,8 +586,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38=
-github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs=
+github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
+github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
 github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc=
 github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -666,7 +664,7 @@ github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GW
 github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
 github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 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.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -804,20 +802,18 @@ github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sigstore/fulcio v1.3.1 h1:0ntW9VbQbt2JytoSs8BOGB84A65eeyvGSavWteYp29Y=
 github.com/sigstore/fulcio v1.3.1/go.mod h1:/XfqazOec45ulJZpyL9sq+OsVQ8g2UOVoNVi7abFgqU=
-github.com/sigstore/protobuf-specs v0.1.0 h1:X0l/E2C2c79t/rI/lmSu8WAoKWsQtMqDzAMiDdEMGr8=
-github.com/sigstore/protobuf-specs v0.1.0/go.mod h1:5shUCxf82hGnjUEFVWiktcxwzdtn6EfeeJssxZ5Q5HE=
-github.com/sigstore/rekor v1.2.1 h1:cEI4qn9IBvM7EkPQYl3YzCwCw97Mx8O2nHrv02XiI8U=
-github.com/sigstore/rekor v1.2.1/go.mod h1:zcFO54qIg2G1/i0sE/nvmELUOng/n0MPjTszRYByVPo=
-github.com/sigstore/sigstore v1.6.5 h1:/liHIo7YPJp6sN31DzBYDOuRPmN1xbzROMBE5DLllYM=
-github.com/sigstore/sigstore v1.6.5/go.mod h1:h+EoQsf9+6UKgNYxKhBcPgo4PZeEVfzAJxKRRIYhyN4=
-github.com/sigstore/sigstore/pkg/signature/kms/aws v1.6.5 h1:SXzp0/S2fBT3O45BKBxInJ5zLqPTqN/nJRG9ZU6ZS1o=
-github.com/sigstore/sigstore/pkg/signature/kms/aws v1.6.5/go.mod h1:iJ1fhlOHajTppwDbFpSrrxvteZcbDt96yOkAQZ3AWdY=
-github.com/sigstore/sigstore/pkg/signature/kms/azure v1.6.5 h1:Eq7FRpCmtWgqO0W8PTV4uEtoLfVOJfDuLYGEwCN1Cy8=
-github.com/sigstore/sigstore/pkg/signature/kms/azure v1.6.5/go.mod h1:dmXaolTdykiQwqCb4WIGjEQWWz0V9NQ+OI2HeeWETRk=
-github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.6.5 h1:ayZpTdJReIyvS1LrXlTFo+cP7WUJalQAs3Bc2PWf6Zk=
-github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.6.5/go.mod h1:xFrqobvdK11t7uvLovNwTcH8ht7/aqR+KIF93oTw+LQ=
-github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.6.5 h1:mddEhD42KiPm3ZCW8gNa0bI6gcAYzTEPgqykweE6uGM=
-github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.6.5/go.mod h1:uvKjzkw4TdbyAwQLxDdGD/Q2CKqiZGzgEb/0T3SZG5I=
+github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23 h1:eZY7mQFcc0VvNr0fiAK3/n7kh73+T06KzBEIUYzFSDQ=
+github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23/go.mod h1:h1tOLhldpfILtziWpUDgGBu0vulWk9Kh72t6XzBGJok=
+github.com/sigstore/sigstore v1.7.0 h1:0jLlzxX68LtirwSTWAwRPMKhulT0aWVLmFU5ofnbtYA=
+github.com/sigstore/sigstore v1.7.0/go.mod h1:0PmMzfJP2Y9+lugD0wer4e7TihR5tM7NcIs3bQNk5xg=
+github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.0 h1:fRv9grFx22NsmXTkfhF8/+UzqkrCND8JI/QfCpYjEnc=
+github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.0/go.mod h1:IgMwzsYgwhi0eB37GTylJxHPUVRtYXDBtL6/wOTvMKU=
+github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.0 h1:ST1G207w73d1t4nXHyXGM9LvRDjSQUtnANbkoGRaBi4=
+github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.0/go.mod h1:VFU3ghlvBSAExeyzrGPn8LHz7CASkma49/uzN4OJObo=
+github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.0 h1:rYJ7+nRDGxSneFcawUzYyVcRKvGrqqdMUbjZMLI6iCc=
+github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.0/go.mod h1:pJD5JoV/1i61LwGFMgQ/xfpEX8QlSk90sfYFGMnxQX0=
+github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.0 h1:mKNyQ5b/G0KgwJ9HSYpGvkXmpeuC/2DYFKsp/mKmtXs=
+github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.0/go.mod h1:SN4QZHHDs2VqXh5bRXrIi8vqLbOijIp2XoSlmV/WJ/c=
 github.com/sigstore/timestamp-authority v1.1.1 h1:EldrdeBED0edNzDMvYZDf5CyWgtSchtR9DKYyksNR8M=
 github.com/sigstore/timestamp-authority v1.1.1/go.mod h1:cEDLEHl/L3ppqKDaiZ3Cg4ikcaYleuq90I/BFNePzF0=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -856,8 +852,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
 github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
 github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
-github.com/spiffe/go-spiffe/v2 v2.1.5 h1:nFzp6pllCxpso6A2CaokdjhmH3uHWMNL9DGYXeZrShs=
-github.com/spiffe/go-spiffe/v2 v2.1.5/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk=
+github.com/spiffe/go-spiffe/v2 v2.1.6 h1:4SdizuQieFyL9eNU+SPiCArH4kynzaKOOj0VvM8R7Xo=
+github.com/spiffe/go-spiffe/v2 v2.1.6/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -908,8 +904,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 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/xanzy/go-gitlab v0.84.0 h1:PdpCaskQSgcVDsx21c6ikf8Rfyo7SNtFAJwP9PrbCFE=
-github.com/xanzy/go-gitlab v0.84.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
+github.com/xanzy/go-gitlab v0.85.0 h1:E/wjnsd/mM5kV6O9y5+i6zxjx+wfAwa97sgcT1ETNwk=
+github.com/xanzy/go-gitlab v0.85.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
 github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
 github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
@@ -959,8 +955,8 @@ go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiM
 go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
 go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.step.sm/crypto v0.31.2 h1:GJX4A15zXxxcbuS++g2SvETTitAUClGIfg5QnKlscDs=
-go.step.sm/crypto v0.31.2/go.mod h1:gFQ/XlQIIiFRfZrXglqKbrX9bgC1HmsASErev9sZN4A=
+go.step.sm/crypto v0.32.0 h1:6vW12tmOLZ9czP0ezW5bFaLvy/jAlXtIOTBCU09n8jI=
+go.step.sm/crypto v0.32.0/go.mod h1:eRZkOZVHvZWyWBrxfiR9XCndRtxjuJRpBQLm4MezNEQ=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -992,8 +988,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1079,8 +1075,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1090,8 +1086,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
-golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
+golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
+golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1105,8 +1101,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1172,14 +1168,14 @@ golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
+golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1192,8 +1188,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1284,8 +1280,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.125.0 h1:7xGvEY4fyWbhWMHf3R2/4w7L4fXyfpRGE9g6lp8+DCk=
-google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
+google.golang.org/api v0.127.0 h1:v7rj0vA0imM3Ou81k1eyFxQNScLzn71EyGnJDr+V/XI=
+google.golang.org/api v0.127.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1419,12 +1415,12 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU=
-k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE=
-k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k=
-k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=
-k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8=
-k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw=
+k8s.io/api v0.26.6 h1:RZsJGP5p/qdWuFVqj/JFyt+6ttfgL+8/K8gtyi7riuo=
+k8s.io/api v0.26.6/go.mod h1:Z+i6M3de4+LJiXtIiWSz/yLpnG+YjxAkeW6cgZqoxn4=
+k8s.io/apimachinery v0.26.6 h1:OT04J9US8G+AqfqvcJZZ8s3WUQkWbc3t6ePPWieDN6I=
+k8s.io/apimachinery v0.26.6/go.mod h1:qYzLkrQ9lhrZRh0jNKo2cfvf/R1/kQONnSiyB7NUJU0=
+k8s.io/client-go v0.26.6 h1:CtC0wOxkAwjYyG2URGzdEKo0nLILopSDYn5AmzOkdi4=
+k8s.io/client-go v0.26.6/go.mod h1:HDjbQGY7XzFYFUWOPAfAsIYhvFXyc9l6Ne0pO0bOQ7o=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
diff --git a/pkg/cosign/errors.go b/pkg/cosign/errors.go
index 28cccd29..0fe40c40 100644
--- a/pkg/cosign/errors.go
+++ b/pkg/cosign/errors.go
@@ -14,55 +14,64 @@
 
 package cosign
 
-import "fmt"
+// VerificationFailure is the type of Go error that is used by cosign to surface
+// errors actually related to verification (vs. transient, misconfiguration,
+// transport, or authentication related issues).
+// It is now marked as deprecated and will be removed in favour of defined
+// error types with use of the ThrowError function.
+type VerificationFailure struct {
+	err error
+}
+
+// ThrowError returns the error type that is passed. It acts as a
+// single consistent way of throwing errors from the pkg level.
+// As long as the error type is defined before hand, this can be
+// used to throw errors up to the calling code without discrimination
+// around the error type.
+func ThrowError(err interface{ error }) error {
+	return err
+}
+
+func (e *VerificationFailure) Error() string {
+	return e.err.Error()
+}
 
-var (
-	// NoMatchingAttestations
-	ErrNoMatchingAttestationsMessage = "no matching attestations"
-	ErrNoMatchingAttestationsType    = "NoMatchingAttestations"
+type ErrNoMatchingSignatures struct {
+	err error
+}
 
-	// NoMatchingSignatures
-	ErrNoMatchingSignaturesType    = "NoMatchingSignatures"
-	ErrNoMatchingSignaturesMessage = "no matching signatures"
+func (e *ErrNoMatchingSignatures) Error() string {
+	return e.err.Error()
+}
 
-	// NonExistingTagType
-	ErrImageTagNotFoundType    = "ImageTagNotFound"
-	ErrImageTagNotFoundMessage = "image tag not found"
+type ErrImageTagNotFound struct {
+	err error
+}
 
-	// NoSignaturesFound
-	ErrNoSignaturesFoundType    = "NoSignaturesFound"
-	ErrNoSignaturesFoundMessage = "no signatures found for image"
-)
+func (e *ErrImageTagNotFound) Error() string {
+	return e.err.Error()
+}
 
-// VerificationError is the type of Go error that is used by cosign to surface
-// errors actually related to verification (vs. transient, misconfiguration,
-// transport, or authentication related issues).
-type VerificationError struct {
-	errorType string
-	message   string
+type ErrNoSignaturesFound struct {
+	err error
 }
 
-// NewVerificationError constructs a new VerificationError in a manner similar
-// to fmt.Errorf
-func NewVerificationError(msg string, args ...interface{}) error {
-	return &VerificationError{
-		message: fmt.Sprintf(msg, args...),
-	}
+func (e *ErrNoSignaturesFound) Error() string {
+	return e.err.Error()
 }
 
-// Assert that we implement error at build time.
-var _ error = (*VerificationError)(nil)
+type ErrNoMatchingAttestations struct {
+	err error
+}
 
-// Error implements error
-func (ve *VerificationError) Error() string {
-	return ve.message
+func (e *ErrNoMatchingAttestations) Error() string {
+	return e.err.Error()
 }
 
-// Error implements error
-func (ve *VerificationError) ErrorType() string {
-	return ve.errorType
+type ErrNoCertificateFoundOnSignature struct {
+	err error
 }
 
-func (ve *VerificationError) SetErrorType(errorType string) {
-	ve.errorType = errorType
+func (e *ErrNoCertificateFoundOnSignature) Error() string {
+	return e.err.Error()
 }
diff --git a/pkg/cosign/fetch.go b/pkg/cosign/fetch.go
index ed789d9f..84c9238f 100644
--- a/pkg/cosign/fetch.go
+++ b/pkg/cosign/fetch.go
@@ -18,6 +18,7 @@ package cosign
 import (
 	"context"
 	"crypto/x509"
+	"encoding/base64"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -26,6 +27,7 @@ import (
 	"sync"
 
 	"github.com/google/go-containerregistry/pkg/name"
+	"github.com/in-toto/in-toto-golang/in_toto"
 	"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
 	"github.com/sigstore/cosign/v2/pkg/oci"
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
@@ -150,28 +152,35 @@ func FetchAttestations(se oci.SignedEntity, predicateType string) ([]Attestation
 	g.SetLimit(runtime.NumCPU())
 
 	for _, att := range l {
-		if predicateType != "" {
-			anns, err := att.Annotations()
-			if err != nil {
-				return nil, err
-			}
-			pt, ok := anns["predicateType"]
-			// Skip attestation if predicateType annotation is not present or predicateType annotation does not match supplied predicate type
-			if !ok || pt != predicateType {
-				continue
-			}
-		}
 		att := att
-		var a AttestationPayload
 		g.Go(func() error {
-			attestPayload, _ := att.Payload()
-			err := json.Unmarshal(attestPayload, &a)
+			rawPayload, err := att.Payload()
 			if err != nil {
-				return err
+				return fmt.Errorf("fetching payload: %w", err)
 			}
+			var payload AttestationPayload
+			if err := json.Unmarshal(rawPayload, &payload); err != nil {
+				return fmt.Errorf("unmarshaling payload: %w", err)
+			}
+
+			if predicateType != "" {
+				var decodedPayload []byte
+				decodedPayload, err = base64.StdEncoding.DecodeString(payload.PayLoad)
+				if err != nil {
+					return fmt.Errorf("decoding payload: %w", err)
+				}
+				var statement in_toto.Statement
+				if err := json.Unmarshal(decodedPayload, &statement); err != nil {
+					return fmt.Errorf("unmarshaling statement: %w", err)
+				}
+				if statement.PredicateType != predicateType {
+					return nil
+				}
+			}
+
 			attMu.Lock()
 			defer attMu.Unlock()
-			attestations = append(attestations, a)
+			attestations = append(attestations, payload)
 			return nil
 		})
 	}
diff --git a/pkg/cosign/tlog.go b/pkg/cosign/tlog.go
index 5a75c7fb..87579dac 100644
--- a/pkg/cosign/tlog.go
+++ b/pkg/cosign/tlog.go
@@ -42,6 +42,8 @@ import (
 	"github.com/sigstore/rekor/pkg/generated/client/entries"
 	"github.com/sigstore/rekor/pkg/generated/models"
 	"github.com/sigstore/rekor/pkg/types"
+	"github.com/sigstore/rekor/pkg/types/dsse"
+	dsse_v001 "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1"
 	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"
@@ -82,6 +84,21 @@ func GetTransparencyLogID(pub crypto.PublicKey) (string, error) {
 	return hex.EncodeToString(digest[:]), nil
 }
 
+func dsseEntry(ctx context.Context, signature, pubKey []byte) (models.ProposedEntry, error) {
+	var pubKeyBytes [][]byte
+
+	if len(pubKey) == 0 {
+		return nil, errors.New("public key provided has 0 length")
+	}
+
+	pubKeyBytes = append(pubKeyBytes, pubKey)
+
+	return types.NewProposedEntry(ctx, dsse.KIND, dsse_v001.APIVERSION, types.ArtifactProperties{
+		ArtifactBytes:  signature,
+		PublicKeyBytes: pubKeyBytes,
+	})
+}
+
 func intotoEntry(ctx context.Context, signature, pubKey []byte) (models.ProposedEntry, error) {
 	var pubKeyBytes [][]byte
 
@@ -162,7 +179,17 @@ func TLogUpload(ctx context.Context, rekorClient *client.Rekor, signature []byte
 	return doUpload(ctx, rekorClient, &returnVal)
 }
 
-// TLogUploadInTotoAttestation will upload and in-toto entry for the signature and public key to the transparency log.
+// TLogUploadDSSEEnvelope will upload a DSSE entry for the signature and public key to the Rekor transparency log.
+func TLogUploadDSSEEnvelope(ctx context.Context, rekorClient *client.Rekor, signature, pemBytes []byte) (*models.LogEntryAnon, error) {
+	e, err := dsseEntry(ctx, signature, pemBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	return doUpload(ctx, rekorClient, e)
+}
+
+// TLogUploadInTotoAttestation will upload an in-toto entry for the signature and public key to the transparency log.
 func TLogUploadInTotoAttestation(ctx context.Context, rekorClient *client.Rekor, signature, pemBytes []byte) (*models.LogEntryAnon, error) {
 	e, err := intotoEntry(ctx, signature, pemBytes)
 	if err != nil {
diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go
index 2d12f748..a0bd7727 100644
--- a/pkg/cosign/verify.go
+++ b/pkg/cosign/verify.go
@@ -26,14 +26,16 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"encoding/pem"
-	"errors"
 	"fmt"
 	"os"
 	"regexp"
 	"strings"
 	"time"
 
+	"github.com/pkg/errors"
+
 	"github.com/digitorus/timestamp"
+	"github.com/go-openapi/runtime"
 	cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle"
 	"github.com/sigstore/sigstore/pkg/tuf"
 
@@ -53,6 +55,12 @@ import (
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
 	"github.com/sigstore/rekor/pkg/generated/client"
 	"github.com/sigstore/rekor/pkg/generated/models"
+	rekor_types "github.com/sigstore/rekor/pkg/types"
+	dsse_v001 "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1"
+	hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
+	intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1"
+	intoto_v002 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2"
+	rekord_v001 "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1"
 	"github.com/sigstore/sigstore/pkg/cryptoutils"
 	"github.com/sigstore/sigstore/pkg/signature"
 	"github.com/sigstore/sigstore/pkg/signature/dsse"
@@ -168,7 +176,9 @@ func verifyOCIAttestation(ctx context.Context, verifier signature.Verifier, att
 	}
 
 	if env.PayloadType != types.IntotoPayloadType {
-		return NewVerificationError("invalid payloadType %s on envelope. Expected %s", env.PayloadType, types.IntotoPayloadType)
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("invalid payloadType %s on envelope. Expected %s", env.PayloadType, types.IntotoPayloadType),
+		})
 	}
 	dssev, err := ssldsse.NewEnvelopeVerifier(&dsse.VerifierAdapter{SignatureVerifier: verifier})
 	if err != nil {
@@ -237,7 +247,9 @@ func ValidateAndUnpackCert(cert *x509.Certificate, co *CheckOpts) (signature.Ver
 	}
 	if !contains && len(co.SCT) == 0 {
 		// TODO: add error type instead of blank string
-		return nil, &VerificationError{"", "certificate does not include required embedded SCT and no detached SCT was set"}
+		return nil, ThrowError(&VerificationFailure{
+			fmt.Errorf("certificate does not include required embedded SCT and no detached SCT was set"),
+		})
 	}
 	// handle if chains has more than one chain - grab first and print message
 	if len(chains) > 1 {
@@ -330,9 +342,9 @@ func CheckCertificatePolicy(cert *x509.Certificate, co *CheckOpts) error {
 				return nil
 			}
 		}
-		return &VerificationError{"",
-			fmt.Sprintf("none of the expected identities matched what was in the certificate, got subjects [%s] with issuer %s",
-				strings.Join(sans, ", "), oidcIssuer)}
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("none of the expected identities matched what was in the certificate, got subjects [%s] with issuer %s", strings.Join(sans, ", "), oidcIssuer),
+		})
 	}
 	return nil
 }
@@ -341,35 +353,45 @@ func validateCertExtensions(ce CertExtensions, co *CheckOpts) error {
 	if co.CertGithubWorkflowTrigger != "" {
 		if ce.GetCertExtensionGithubWorkflowTrigger() != co.CertGithubWorkflowTrigger {
 			// TODO: add error type instead of blank string
-			return &VerificationError{"", "expected GitHub Workflow Trigger not found in certificate"}
+			return ThrowError(&VerificationFailure{
+				fmt.Errorf("expected GitHub Workflow Trigger not found in certificate"),
+			})
 		}
 	}
 
 	if co.CertGithubWorkflowSha != "" {
 		if ce.GetExtensionGithubWorkflowSha() != co.CertGithubWorkflowSha {
 			// TODO: add error type instead of blank string
-			return &VerificationError{"", "expected GitHub Workflow SHA not found in certificate"}
+			return ThrowError(&VerificationFailure{
+				fmt.Errorf("expected GitHub Workflow SHA not found in certificate"),
+			})
 		}
 	}
 
 	if co.CertGithubWorkflowName != "" {
 		if ce.GetCertExtensionGithubWorkflowName() != co.CertGithubWorkflowName {
 			// TODO: add error type instead of blank string
-			return &VerificationError{"", "expected GitHub Workflow Name not found in certificate"}
+			return ThrowError(&VerificationFailure{
+				fmt.Errorf("expected GitHub Workflow Name not found in certificate"),
+			})
 		}
 	}
 
 	if co.CertGithubWorkflowRepository != "" {
 		if ce.GetCertExtensionGithubWorkflowRepository() != co.CertGithubWorkflowRepository {
 			// TODO: add error type instead of blank string
-			return &VerificationError{"", "expected GitHub Workflow Repository not found in certificate"}
+			return ThrowError(&VerificationFailure{
+				fmt.Errorf("expected GitHub Workflow Repository not found in certificate"),
+			})
 		}
 	}
 
 	if co.CertGithubWorkflowRef != "" {
 		if ce.GetCertExtensionGithubWorkflowRef() != co.CertGithubWorkflowRef {
 			// TODO: add error type instead of blank string
-			return &VerificationError{"", "expected GitHub Workflow Ref not found in certificate"}
+			return ThrowError(&VerificationFailure{
+				fmt.Errorf("expected GitHub Workflow Ref not found in certificate"),
+			})
 		}
 	}
 	return nil
@@ -486,10 +508,9 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co
 	digest, err := ociremote.ResolveDigest(signedImgRef, co.RegistryClientOpts...)
 	if err != nil {
 		if strings.Contains(err.Error(), "MANIFEST_UNKNOWN") {
-			return nil, false, &VerificationError{
-				errorType: ErrImageTagNotFoundType,
-				message:   fmt.Sprintf("%s: %v", ErrImageTagNotFoundMessage, err),
-			}
+			return nil, false, ThrowError(&ErrImageTagNotFound{
+				fmt.Errorf("image tag not found: %w", err),
+			})
 		}
 		return nil, false, err
 	}
@@ -575,10 +596,9 @@ func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *C
 	}
 
 	if len(sl) == 0 {
-		return nil, false, &VerificationError{
-			errorType: ErrNoSignaturesFoundType,
-			message:   ErrNoSignaturesFoundMessage,
-		}
+		return nil, false, ThrowError(&ErrNoMatchingSignatures{
+			errors.New("no matching signatures"),
+		})
 	}
 
 	validationErrs := []string{}
@@ -600,10 +620,9 @@ func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *C
 		checkedSignatures = append(checkedSignatures, sig)
 	}
 	if len(checkedSignatures) == 0 {
-		return nil, false, &VerificationError{
-			errorType: ErrNoMatchingSignaturesType,
-			message:   fmt.Sprintf("%s:\n%s", ErrNoMatchingSignaturesMessage, strings.Join(validationErrs, "\n ")),
-		}
+		return nil, false, ThrowError(&ErrNoMatchingSignatures{
+			fmt.Errorf("no matching signatures: %s", strings.Join(validationErrs, "\n ")),
+		})
 	}
 	return checkedSignatures, bundleVerified, nil
 }
@@ -677,8 +696,9 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash,
 			return false, err
 		}
 		if cert == nil {
-			// TODO: add error type instead of blank string
-			return false, &VerificationError{"", "no certificate found on signature"}
+			return false, ThrowError(&ErrNoCertificateFoundOnSignature{
+				fmt.Errorf("no certificate found on signature"),
+			})
 		}
 		// Create a certificate pool for intermediate CA certificates, excluding the root
 		chain, err := sig.Chain()
@@ -746,7 +766,9 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash,
 				// If certificate is expired and not signed timestamp was provided then error the following message. Otherwise throw an expiration error.
 				if co.IgnoreTlog && acceptableRFC3161Time == nil {
 					// TODO: add error type instead of blank string
-					return false, &VerificationError{"", "expected a signed timestamp to verify an expired certificate"}
+					return false, ThrowError(&VerificationFailure{
+						fmt.Errorf("expected a signed timestamp to verify an expired certificate"),
+					})
 				}
 				return false, fmt.Errorf("checking expiry on certificate with bundle: %w", err)
 			}
@@ -935,10 +957,9 @@ func verifyImageAttestations(ctx context.Context, atts oci.Signatures, h v1.Hash
 		checkedAttestations = append(checkedAttestations, att)
 	}
 	if len(checkedAttestations) == 0 {
-		return nil, false, &VerificationError{
-			errorType: ErrNoMatchingAttestationsType,
-			message:   fmt.Sprintf("%s:\n%s", ErrNoMatchingAttestationsMessage, strings.Join(validationErrs, "\n ")),
-		}
+		return nil, false, ThrowError(&ErrNoMatchingAttestations{
+			fmt.Errorf("no matching attestations: %s", strings.Join(validationErrs, "\n ")),
+		})
 	}
 	return checkedAttestations, bundleVerified, nil
 }
@@ -949,12 +970,16 @@ func CheckExpiry(cert *x509.Certificate, it time.Time) error {
 		return t.Format(time.RFC3339)
 	}
 	if cert.NotAfter.Before(it) {
-		return NewVerificationError("certificate expired before signatures were entered in log: %s is before %s",
-			ft(cert.NotAfter), ft(it))
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("certificate expired before signatures were entered in log: %s is before %s",
+				ft(cert.NotAfter), ft(it)),
+		})
 	}
 	if cert.NotBefore.After(it) {
-		return NewVerificationError("certificate was issued after signatures were entered in log: %s is after %s",
-			ft(cert.NotAfter), ft(it))
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("certificate was issued after signatures were entered in log: %s is after %s",
+				ft(cert.NotAfter), ft(it)),
+		})
 	}
 	return nil
 }
@@ -1000,7 +1025,9 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) {
 	pubKey, ok := co.RekorPubKeys.Keys[bundle.Payload.LogID]
 	if !ok {
 		// TODO: add error type instead of blank string
-		return false, &VerificationError{"", "verifying bundle: rekor log public key not found for payload"}
+		return false, ThrowError(&VerificationFailure{
+			fmt.Errorf("verifying bundle: rekor log public key not found for payload"),
+		})
 	}
 	err = VerifySET(bundle.Payload, bundle.SignedEntryTimestamp, pubKey.PubKey.(*ecdsa.PublicKey))
 	if err != nil {
@@ -1091,7 +1118,9 @@ func compareSigs(bundleBody string, sig oci.Signature) error {
 	}
 	if bundleSignature != actualSig {
 		// TODO: add error type instead of blank string
-		return &VerificationError{"", "signature in bundle does not match signature being verified"}
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("signature in bundle does not match signature being verified"),
+		})
 	}
 	return nil
 }
@@ -1130,155 +1159,91 @@ func comparePublicKey(bundleBody string, sig oci.Signature, co *CheckOpts) error
 	return nil
 }
 
-func bundleHash(bundleBody, signature string) (string, string, error) {
-	var toto models.Intoto
-	var rekord models.Rekord
-	var hrekord models.Hashedrekord
-	var intotoObj models.IntotoV001Schema
-	var rekordObj models.RekordV001Schema
-	var hrekordObj models.HashedrekordV001Schema
-
-	bodyDecoded, err := base64.StdEncoding.DecodeString(bundleBody)
+func extractEntryImpl(bundleBody string) (rekor_types.EntryImpl, error) {
+	pe, err := models.UnmarshalProposedEntry(base64.NewDecoder(base64.StdEncoding, strings.NewReader(bundleBody)), runtime.JSONConsumer())
 	if err != nil {
-		return "", "", err
-	}
-
-	// The fact that there's no signature (or empty rather), implies
-	// that this is an Attestation that we're verifying.
-	if len(signature) == 0 {
-		err = json.Unmarshal(bodyDecoded, &toto)
-		if err != nil {
-			return "", "", err
-		}
-
-		specMarshal, err := json.Marshal(toto.Spec)
-		if err != nil {
-			return "", "", err
-		}
-		err = json.Unmarshal(specMarshal, &intotoObj)
-		if err != nil {
-			return "", "", err
-		}
-
-		return *intotoObj.Content.Hash.Algorithm, *intotoObj.Content.Hash.Value, nil
+		return nil, err
 	}
 
-	if err := json.Unmarshal(bodyDecoded, &rekord); err == nil {
-		specMarshal, err := json.Marshal(rekord.Spec)
-		if err != nil {
-			return "", "", err
-		}
-		err = json.Unmarshal(specMarshal, &rekordObj)
-		if err != nil {
-			return "", "", err
-		}
-		return *rekordObj.Data.Hash.Algorithm, *rekordObj.Data.Hash.Value, nil
-	}
+	return rekor_types.UnmarshalEntry(pe)
+}
 
-	// Try hashedRekordObj
-	err = json.Unmarshal(bodyDecoded, &hrekord)
+func bundleHash(bundleBody, _ string) (string, string, error) {
+	ei, err := extractEntryImpl(bundleBody)
 	if err != nil {
 		return "", "", err
 	}
-	specMarshal, err := json.Marshal(hrekord.Spec)
-	if err != nil {
-		return "", "", err
-	}
-	err = json.Unmarshal(specMarshal, &hrekordObj)
-	if err != nil {
-		return "", "", err
+
+	switch entry := ei.(type) {
+	case *dsse_v001.V001Entry:
+		return *entry.DSSEObj.EnvelopeHash.Algorithm, *entry.DSSEObj.EnvelopeHash.Value, nil
+	case *hashedrekord_v001.V001Entry:
+		return *entry.HashedRekordObj.Data.Hash.Algorithm, *entry.HashedRekordObj.Data.Hash.Value, nil
+	case *intoto_v001.V001Entry:
+		return *entry.IntotoObj.Content.Hash.Algorithm, *entry.IntotoObj.Content.Hash.Value, nil
+	case *intoto_v002.V002Entry:
+		return *entry.IntotoObj.Content.Hash.Algorithm, *entry.IntotoObj.Content.Hash.Value, nil
+	case *rekord_v001.V001Entry:
+		return *entry.RekordObj.Data.Hash.Algorithm, *entry.RekordObj.Data.Hash.Value, nil
+	default:
+		return "", "", errors.New("unsupported type")
 	}
-	return *hrekordObj.Data.Hash.Algorithm, *hrekordObj.Data.Hash.Value, nil
 }
 
 // bundleSig extracts the signature from the rekor bundle body
 func bundleSig(bundleBody string) (string, error) {
-	var rekord models.Rekord
-	var hrekord models.Hashedrekord
-	var rekordObj models.RekordV001Schema
-	var hrekordObj models.HashedrekordV001Schema
-
-	bodyDecoded, err := base64.StdEncoding.DecodeString(bundleBody)
+	ei, err := extractEntryImpl(bundleBody)
 	if err != nil {
-		return "", fmt.Errorf("decoding bundleBody: %w", err)
+		return "", err
 	}
 
-	// Try Rekord
-	if err := json.Unmarshal(bodyDecoded, &rekord); err == nil {
-		specMarshal, err := json.Marshal(rekord.Spec)
-		if err != nil {
-			return "", err
+	switch entry := ei.(type) {
+	case *dsse_v001.V001Entry:
+		if len(entry.DSSEObj.Signatures) > 1 {
+			return "", errors.New("multiple signatures on DSSE envelopes are not currently supported")
 		}
-		if err := json.Unmarshal(specMarshal, &rekordObj); err != nil {
-			return "", err
+		return *entry.DSSEObj.Signatures[0].Signature, nil
+	case *hashedrekord_v001.V001Entry:
+		return entry.HashedRekordObj.Signature.Content.String(), nil
+	case *intoto_v002.V002Entry:
+		if len(entry.IntotoObj.Content.Envelope.Signatures) > 1 {
+			return "", errors.New("multiple signatures on DSSE envelopes are not currently supported")
 		}
-		return rekordObj.Signature.Content.String(), nil
-	}
-
-	// Try hashedRekordObj
-	if err := json.Unmarshal(bodyDecoded, &hrekord); err != nil {
-		return "", err
-	}
-	specMarshal, err := json.Marshal(hrekord.Spec)
-	if err != nil {
-		return "", err
-	}
-	if err := json.Unmarshal(specMarshal, &hrekordObj); err != nil {
-		return "", err
+		return entry.IntotoObj.Content.Envelope.Signatures[0].Sig.String(), nil
+	case *rekord_v001.V001Entry:
+		return entry.RekordObj.Signature.Content.String(), nil
+	default:
+		return "", errors.New("unsupported type")
 	}
-	return hrekordObj.Signature.Content.String(), nil
 }
 
 // bundleKey extracts the key from the rekor bundle body
 func bundleKey(bundleBody string) (string, error) {
-	var rekord models.Rekord
-	var hrekord models.Hashedrekord
-	var intotod models.Intoto
-	var rekordObj models.RekordV001Schema
-	var hrekordObj models.HashedrekordV001Schema
-	var intotodObj models.IntotoV001Schema
-
-	bodyDecoded, err := base64.StdEncoding.DecodeString(bundleBody)
+	ei, err := extractEntryImpl(bundleBody)
 	if err != nil {
-		return "", fmt.Errorf("decoding bundleBody: %w", err)
-	}
-
-	// Try Rekord
-	if err := json.Unmarshal(bodyDecoded, &rekord); err == nil {
-		specMarshal, err := json.Marshal(rekord.Spec)
-		if err != nil {
-			return "", err
-		}
-		if err := json.Unmarshal(specMarshal, &rekordObj); err != nil {
-			return "", err
-		}
-		return rekordObj.Signature.PublicKey.Content.String(), nil
+		return "", err
 	}
 
-	// Try hashedRekordObj
-	if err := json.Unmarshal(bodyDecoded, &hrekord); err == nil {
-		specMarshal, err := json.Marshal(hrekord.Spec)
-		if err != nil {
-			return "", err
+	switch entry := ei.(type) {
+	case *dsse_v001.V001Entry:
+		if len(entry.DSSEObj.Signatures) > 1 {
+			return "", errors.New("multiple signatures on DSSE envelopes are not currently supported")
 		}
-		if err := json.Unmarshal(specMarshal, &hrekordObj); err != nil {
-			return "", err
+		return entry.DSSEObj.Signatures[0].Verifier.String(), nil
+	case *hashedrekord_v001.V001Entry:
+		return entry.HashedRekordObj.Signature.PublicKey.Content.String(), nil
+	case *intoto_v001.V001Entry:
+		return entry.IntotoObj.PublicKey.String(), nil
+	case *intoto_v002.V002Entry:
+		if len(entry.IntotoObj.Content.Envelope.Signatures) > 1 {
+			return "", errors.New("multiple signatures on DSSE envelopes are not currently supported")
 		}
-		return hrekordObj.Signature.PublicKey.Content.String(), nil
-	}
-
-	// Try Intoto
-	if err := json.Unmarshal(bodyDecoded, &intotod); err != nil {
-		return "", err
-	}
-	specMarshal, err := json.Marshal(intotod.Spec)
-	if err != nil {
-		return "", err
-	}
-	if err := json.Unmarshal(specMarshal, &intotodObj); err != nil {
-		return "", err
+		return entry.IntotoObj.Content.Envelope.Signatures[0].PublicKey.String(), nil
+	case *rekord_v001.V001Entry:
+		return entry.RekordObj.Signature.PublicKey.Content.String(), nil
+	default:
+		return "", errors.New("unsupported type")
 	}
-	return intotodObj.PublicKey.String(), nil
 }
 
 func VerifySET(bundlePayload cbundle.RekorPayload, signature []byte, pub *ecdsa.PublicKey) error {
@@ -1295,7 +1260,9 @@ func VerifySET(bundlePayload cbundle.RekorPayload, signature []byte, pub *ecdsa.
 	hash := sha256.Sum256(canonicalized)
 	if !ecdsa.VerifyASN1(pub, hash[:], signature) {
 		// TODO: add error type instead of blank string
-		return &VerificationError{"", "unable to verify SET"}
+		return ThrowError(&VerificationFailure{
+			fmt.Errorf("unable to verify SET"),
+		})
 	}
 	return nil
 }
diff --git a/pkg/oci/mutate/mutate.go b/pkg/oci/mutate/mutate.go
index ce667750..32d37be5 100644
--- a/pkg/oci/mutate/mutate.go
+++ b/pkg/oci/mutate/mutate.go
@@ -135,7 +135,7 @@ func AttachSignatureToEntity(se oci.SignedEntity, sig oci.Signature, opts ...Sig
 	case oci.SignedImageIndex:
 		return AttachSignatureToImageIndex(obj, sig, opts...)
 	default:
-		return nil, fmt.Errorf("unsupported type: %T", se)
+		return AttachSignatureToUnknown(obj, sig, opts...)
 	}
 }
 
@@ -147,7 +147,7 @@ func AttachAttestationToEntity(se oci.SignedEntity, att oci.Signature, opts ...S
 	case oci.SignedImageIndex:
 		return AttachAttestationToImageIndex(obj, att, opts...)
 	default:
-		return nil, fmt.Errorf("unsupported type: %T", se)
+		return AttachAttestationToUnknown(obj, att, opts...)
 	}
 }
 
@@ -159,7 +159,7 @@ func AttachFileToEntity(se oci.SignedEntity, name string, f oci.File, opts ...Si
 	case oci.SignedImageIndex:
 		return AttachFileToImageIndex(obj, name, f, opts...)
 	default:
-		return nil, fmt.Errorf("unsupported type: %T", se)
+		return AttachFileToUnknown(obj, name, f, opts...)
 	}
 }
 
@@ -348,3 +348,108 @@ func (sii *signedImageIndex) Attachment(attName string) (oci.File, error) {
 	}
 	return nil, fmt.Errorf("attachment %q not found", attName)
 }
+
+// AttachSignatureToUnknown attaches the provided signature to the provided image.
+func AttachSignatureToUnknown(se oci.SignedEntity, sig oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
+	return &signedUnknown{
+		SignedEntity: se,
+		sig:          sig,
+		attachments:  make(map[string]oci.File),
+		so:           makeSignOpts(opts...),
+	}, nil
+}
+
+// AttachAttestationToUnknown attaches the provided attestation to the provided image.
+func AttachAttestationToUnknown(se oci.SignedEntity, att oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
+	return &signedUnknown{
+		SignedEntity: se,
+		att:          att,
+		attachments:  make(map[string]oci.File),
+		so:           makeSignOpts(opts...),
+	}, nil
+}
+
+// AttachFileToUnknown attaches the provided file to the provided image.
+func AttachFileToUnknown(se oci.SignedEntity, name string, f oci.File, opts ...SignOption) (oci.SignedEntity, error) {
+	return &signedUnknown{
+		SignedEntity: se,
+		attachments: map[string]oci.File{
+			name: f,
+		},
+		so: makeSignOpts(opts...),
+	}, nil
+}
+
+type signedUnknown struct {
+	oci.SignedEntity
+	sig         oci.Signature
+	att         oci.Signature
+	so          *signOpts
+	attachments map[string]oci.File
+}
+
+type digestable interface {
+	Digest() (v1.Hash, error)
+}
+
+// Digest is generally implemented by oci.SignedEntity implementations.
+func (si *signedUnknown) Digest() (v1.Hash, error) {
+	d, ok := si.SignedEntity.(digestable)
+	if !ok {
+		return v1.Hash{}, fmt.Errorf("underlying signed entity not digestable: %T", si.SignedEntity)
+	}
+	return d.Digest()
+}
+
+// Signatures implements oci.SignedEntity
+func (si *signedUnknown) Signatures() (oci.Signatures, error) {
+	base, err := si.SignedEntity.Signatures()
+	if err != nil {
+		return nil, err
+	} else if si.sig == nil {
+		return base, nil
+	}
+	if si.so.dd != nil {
+		if existing, err := si.so.dd.Find(base, si.sig); err != nil {
+			return nil, err
+		} else if existing != nil {
+			// Just return base if the signature is redundant
+			return base, nil
+		}
+	}
+	return AppendSignatures(base, si.sig)
+}
+
+// Attestations implements oci.SignedEntity
+func (si *signedUnknown) Attestations() (oci.Signatures, error) {
+	base, err := si.SignedEntity.Attestations()
+	if err != nil {
+		return nil, err
+	} else if si.att == nil {
+		return base, nil
+	}
+	if si.so.dd != nil {
+		if existing, err := si.so.dd.Find(base, si.att); err != nil {
+			return nil, err
+		} else if existing != nil {
+			// Just return base if the signature is redundant
+			return base, nil
+		}
+	}
+	if si.so.ro != nil {
+		replace, err := si.so.ro.Replace(base, si.att)
+		if err != nil {
+			return nil, err
+		}
+		return ReplaceSignatures(replace)
+	}
+	return AppendSignatures(base, si.att)
+}
+
+// Attachment implements oci.SignedEntity
+func (si *signedUnknown) Attachment(attName string) (oci.File, error) {
+	if f, ok := si.attachments[attName]; ok {
+		return f, nil
+	}
+	return nil, fmt.Errorf("attachment %q not found", attName)
+}
diff --git a/pkg/oci/remote/remote.go b/pkg/oci/remote/remote.go
index 65057eb3..fab0a765 100644
--- a/pkg/oci/remote/remote.go
+++ b/pkg/oci/remote/remote.go
@@ -36,6 +36,10 @@ var (
 	remoteIndex = remote.Index
 	remoteGet   = remote.Get
 	remoteWrite = remote.Write
+
+	// ErrEntityNotFound is the error that SignedEntity returns when the
+	// provided ref does not exist.
+	ErrEntityNotFound = errors.New("entity not found in registry")
 )
 
 // SignedEntity provides access to a remote reference, and its signatures.
@@ -46,7 +50,7 @@ func SignedEntity(ref name.Reference, options ...Option) (oci.SignedEntity, erro
 	got, err := remoteGet(ref, o.ROpt...)
 	var te *transport.Error
 	if errors.As(err, &te) && te.StatusCode == http.StatusNotFound {
-		return nil, errors.New("entity not found in registry")
+		return nil, ErrEntityNotFound
 	} else if err != nil {
 		return nil, err
 	}
diff --git a/pkg/oci/remote/unknown.go b/pkg/oci/remote/unknown.go
new file mode 100644
index 00000000..90a0fc9a
--- /dev/null
+++ b/pkg/oci/remote/unknown.go
@@ -0,0 +1,60 @@
+//
+// Copyright 2023 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 remote
+
+import (
+	"github.com/google/go-containerregistry/pkg/name"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/sigstore/cosign/v2/pkg/oci"
+)
+
+// SignedUnknown provides access to signed metadata without directly accessing
+// the underlying entity.  This can be used to access signature metadata for
+// digests that have not been published (yet).
+func SignedUnknown(digest name.Digest, options ...Option) oci.SignedEntity {
+	o := makeOptions(digest.Context(), options...)
+	return &unknown{
+		digest: digest,
+		opt:    o,
+	}
+}
+
+type unknown struct {
+	digest name.Digest
+	opt    *options
+}
+
+var _ oci.SignedEntity = (*unknown)(nil)
+
+// Digest implements digestable
+func (i *unknown) Digest() (v1.Hash, error) {
+	return v1.NewHash(i.digest.DigestStr())
+}
+
+// Signatures implements oci.SignedEntity
+func (i *unknown) Signatures() (oci.Signatures, error) {
+	return signatures(i, i.opt)
+}
+
+// Attestations implements oci.SignedEntity
+func (i *unknown) Attestations() (oci.Signatures, error) {
+	return attestations(i, i.opt)
+}
+
+// Attachment implements oci.SignedEntity
+func (i *unknown) Attachment(name string) (oci.File, error) {
+	return attachment(i, name, i.opt)
+}
diff --git a/pkg/policy/errors.go b/pkg/policy/errors.go
new file mode 100644
index 00000000..5359a4bb
--- /dev/null
+++ b/pkg/policy/errors.go
@@ -0,0 +1,32 @@
+// Copyright 2022 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 policy
+
+type EvaluationFailure struct {
+	err error
+}
+
+func (e *EvaluationFailure) Error() string {
+	return e.err.Error()
+}
+
+// ThrowError returns the error type that is passed. It acts as a
+// single consistent way of throwing errors from the pkg level.
+// As long as the error type is defined before hand, this can be
+// used to throw errors up to the calling code without discrimination
+// around the error type.
+func ThrowError(err interface{ error }) error {
+	return err
+}
diff --git a/pkg/policy/eval.go b/pkg/policy/eval.go
index d341d313..991f3474 100644
--- a/pkg/policy/eval.go
+++ b/pkg/policy/eval.go
@@ -20,7 +20,6 @@ import (
 	"fmt"
 
 	"cuelang.org/go/cue/cuecontext"
-	"github.com/sigstore/cosign/v2/pkg/cosign"
 	"github.com/sigstore/cosign/v2/pkg/cosign/rego"
 )
 
@@ -36,12 +35,16 @@ func EvaluatePolicyAgainstJSON(ctx context.Context, name, policyType string, pol
 	case "cue":
 		cueValidationErr := evaluateCue(ctx, jsonBytes, policyBody)
 		if cueValidationErr != nil {
-			return nil, cosign.NewVerificationError("failed evaluating cue policy for %s: %v", name, cueValidationErr)
+			return nil, ThrowError(&EvaluationFailure{
+				fmt.Errorf("failed evaluating cue policy for %s: %w", name, cueValidationErr),
+			})
 		}
 	case "rego":
 		regoValidationWarn, regoValidationErr := evaluateRego(ctx, jsonBytes, policyBody)
 		if regoValidationErr != nil {
-			return regoValidationWarn, cosign.NewVerificationError("failed evaluating rego policy for type %s: %s", name, regoValidationErr)
+			return regoValidationWarn, ThrowError(&EvaluationFailure{
+				fmt.Errorf("failed evaluating rego policy for type %s: %w", name, regoValidationErr),
+			})
 		}
 		// It is possible to return warning messages when the policy is compliant
 		return regoValidationWarn, regoValidationErr
diff --git a/pkg/providers/all/all.go b/pkg/providers/all/all.go
index a05d0476..6f5952e7 100644
--- a/pkg/providers/all/all.go
+++ b/pkg/providers/all/all.go
@@ -19,10 +19,15 @@ import (
 	"github.com/sigstore/cosign/v2/pkg/providers"
 
 	// Link in all of the providers.
+	// Link the GitHub one first, since we might be running in a GitHub self-hosted
+	// runner running in one of the other environments, and we should prefer GitHub
+	// credentials if we can find them.
+	_ "github.com/sigstore/cosign/v2/pkg/providers/github"
+
+	// Link in the rest of the providers.
 	_ "github.com/sigstore/cosign/v2/pkg/providers/buildkite"
 	_ "github.com/sigstore/cosign/v2/pkg/providers/envvar"
 	_ "github.com/sigstore/cosign/v2/pkg/providers/filesystem"
-	_ "github.com/sigstore/cosign/v2/pkg/providers/github"
 	_ "github.com/sigstore/cosign/v2/pkg/providers/google"
 	_ "github.com/sigstore/cosign/v2/pkg/providers/spiffe"
 )
diff --git a/pkg/providers/interface.go b/pkg/providers/interface.go
index 1a85810d..30d595aa 100644
--- a/pkg/providers/interface.go
+++ b/pkg/providers/interface.go
@@ -24,9 +24,14 @@ import (
 
 var (
 	m         sync.Mutex
-	providers = make(map[string]Interface)
+	providers []providerEntry
 )
 
+type providerEntry struct {
+	name string
+	p    Interface
+}
+
 // Interface is what providers need to implement to participate in furnishing OIDC tokens.
 type Interface interface {
 	// Enabled returns true if the provider is enabled.
@@ -41,10 +46,12 @@ func Register(name string, p Interface) {
 	m.Lock()
 	defer m.Unlock()
 
-	if prev, ok := providers[name]; ok {
-		panic(fmt.Sprintf("duplicate provider for name %q, %T and %T", name, prev, p))
+	for _, pe := range providers {
+		if pe.name == name {
+			panic(fmt.Sprintf("duplicate provider for name %q, %T and %T", name, pe.p, p))
+		}
 	}
-	providers[name] = p
+	providers = append(providers, providerEntry{name: name, p: p})
 }
 
 // Enabled checks whether any of the registered providers are enabled in this execution context.
@@ -53,7 +60,7 @@ func Enabled(ctx context.Context) bool {
 	defer m.Unlock()
 
 	for _, provider := range providers {
-		if provider.Enabled(ctx) {
+		if provider.p.Enabled(ctx) {
 			return true
 		}
 	}
@@ -68,10 +75,10 @@ func Provide(ctx context.Context, audience string) (string, error) {
 	var id string
 	var err error
 	for _, provider := range providers {
-		if !provider.Enabled(ctx) {
+		if !provider.p.Enabled(ctx) {
 			continue
 		}
-		id, err = provider.Provide(ctx, audience)
+		id, err = provider.p.Provide(ctx, audience)
 		if err == nil {
 			return id, err
 		}
@@ -89,9 +96,10 @@ func ProvideFrom(_ context.Context, provider string) (Interface, error) {
 	m.Lock()
 	defer m.Unlock()
 
-	p, ok := providers[provider]
-	if !ok {
-		return nil, fmt.Errorf("%s is not a valid provider", provider)
+	for _, p := range providers {
+		if p.name == provider {
+			return p.p, nil
+		}
 	}
-	return p, nil
+	return nil, fmt.Errorf("%s is not a valid provider", provider)
 }
diff --git a/release/cloudbuild.yaml b/release/cloudbuild.yaml
index 90e7b978..d01626ee 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.20.4-0@sha256:c9f009c018d406f08e6e2e601eb6945f66a003112a220dca039621078e565188'
+      - 'ghcr.io/gythialy/golang-cross:v1.20.5-0@sha256:d74efd682562c4da37de085b50dca0bfdc7a7a778de6412c9d277e2bd5927e3d'
       - '--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.20.4-0"
+      - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.20.5-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.20.4-0@sha256:c9f009c018d406f08e6e2e601eb6945f66a003112a220dca039621078e565188
+  - name: ghcr.io/gythialy/golang-cross:v1.20.5-0@sha256:d74efd682562c4da37de085b50dca0bfdc7a7a778de6412c9d277e2bd5927e3d
     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.20.4-0@sha256:c9f009c018d406f08e6e2e601eb6945f66a003112a220dca039621078e565188
+  - name: ghcr.io/gythialy/golang-cross:v1.20.5-0@sha256:d74efd682562c4da37de085b50dca0bfdc7a7a778de6412c9d277e2bd5927e3d
     entrypoint: 'bash'
     dir: "go/src/sigstore/cosign"
     env:


Test patch:
diff --git a/.github/workflows/e2e-tests-kms.yml b/.github/workflows/e2e-tests-kms.yml
index 76b2008e..e1610a86 100644
--- a/.github/workflows/e2e-tests-kms.yml
+++ b/.github/workflows/e2e-tests-kms.yml
@@ -53,7 +53,7 @@ jobs:
       COSIGN_YES: "true"
     steps:
       - name: Checkout
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: cpanato/vault-installer@4246c92b8f047fdb824eb7387d86b3c7806e2bf3 # v0.0.2
         with:
           vault-release: '1.12.2'
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index de53b1f3..c9529bf2 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -39,7 +39,7 @@ jobs:
     runs-on: ${{ matrix.os }}
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: '1.20.x'
diff --git a/.github/workflows/e2e-with-binary.yml b/.github/workflows/e2e-with-binary.yml
index bcc64c31..bb6658ac 100644
--- a/.github/workflows/e2e-with-binary.yml
+++ b/.github/workflows/e2e-with-binary.yml
@@ -45,7 +45,7 @@ jobs:
       COSIGN_YES: "true"
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: '1.20.x'
diff --git a/.github/workflows/kind-e2e-insecure-registry.yaml b/.github/workflows/kind-e2e-insecure-registry.yaml
index 4970d751..53abd62d 100644
--- a/.github/workflows/kind-e2e-insecure-registry.yaml
+++ b/.github/workflows/kind-e2e-insecure-registry.yaml
@@ -44,7 +44,7 @@ jobs:
       KO_DOCKER_REPO: registry.local:5000/policy-controller
 
     steps:
-    - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+    - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
     - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
       with:
         go-version: '1.20.x'
diff --git a/.github/workflows/kind-verify-attestation.yaml b/.github/workflows/kind-verify-attestation.yaml
index e54bd63f..e6251da0 100644
--- a/.github/workflows/kind-verify-attestation.yaml
+++ b/.github/workflows/kind-verify-attestation.yaml
@@ -47,7 +47,7 @@ jobs:
       COSIGN_YES: "true"
 
     steps:
-    - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+    - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
     - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
       with:
         go-version: '1.20.x'
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index f21c01b4..3b3d16e2 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -46,7 +46,7 @@ jobs:
       OS: ${{ matrix.os }}
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds
       - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
         with:
@@ -88,7 +88,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds
       - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
         with:
@@ -127,7 +127,7 @@ jobs:
     name: Run PowerShell E2E tests
     runs-on: windows-latest
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: ${{ env.GO_VERSION }}
@@ -153,7 +153,7 @@ jobs:
     name: license boilerplate check
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: ${{ env.GO_VERSION }}
@@ -169,14 +169,14 @@ jobs:
     name: lint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
       - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
         with:
           go-version: 1.20.x
           check-latest: true
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3.4.0
+        uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0
         with:
           # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
-          version: v1.52
+          version: v1.53
           args: --timeout=5m
diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go
index 57285c0e..325c211d 100644
--- a/cmd/cosign/cli/attest.go
+++ b/cmd/cosign/cli/attest.go
@@ -55,7 +55,10 @@ func Attest() *cobra.Command {
   cosign attest --predicate <FILE> --type <TYPE> --key cosign.key --cert cosign.crt --cert-chain chain.crt <IMAGE>
 
   # attach an attestation to a container image which does not fully support OCI media types
-  COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate <FILE> --type <TYPE> --key cosign.key legacy-registry.example.com/my/image`,
+  COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate <FILE> --type <TYPE> --key cosign.key legacy-registry.example.com/my/image
+
+  # supply attestation via stdin
+  echo <PAYLOAD> | cosign attest --predicate - <IMAGE>`,
 
 		Args:             cobra.MinimumNArgs(1),
 		PersistentPreRun: options.BindViper,
diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go
index 2a0aab6b..7645bec4 100644
--- a/cmd/cosign/cli/attest/attest.go
+++ b/cmd/cosign/cli/attest/attest.go
@@ -21,7 +21,6 @@ import (
 	_ "crypto/sha256" // for `crypto.SHA256`
 	"encoding/json"
 	"fmt"
-	"io"
 	"os"
 	"time"
 
@@ -90,6 +89,10 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
 		return &options.KeyParseError{}
 	}
 
+	if c.PredicatePath == "" {
+		return fmt.Errorf("predicate cannot be empty")
+	}
+
 	predicateURI, err := options.ParsePredicateType(c.PredicateType)
 	if err != nil {
 		return err
@@ -131,18 +134,11 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
 	wrapped := dsse.WrapSigner(sv, types.IntotoPayloadType)
 	dd := cremote.NewDupeDetector(sv)
 
-	var predicate io.ReadCloser
-	if c.PredicatePath == "-" {
-		fmt.Fprintln(os.Stderr, "Using payload from: standard input")
-		predicate = os.Stdin
-	} else {
-		fmt.Fprintln(os.Stderr, "Using payload from:", c.PredicatePath)
-		predicate, err = os.Open(c.PredicatePath)
-		if err != nil {
-			return err
-		}
-		defer predicate.Close()
+	predicate, err := predicateReader(c.PredicatePath)
+	if err != nil {
+		return fmt.Errorf("getting predicate reader: %w", err)
 	}
+	defer predicate.Close()
 
 	sh, err := attestation.GenerateStatement(attestation.GenerateOpts{
 		Predicate: predicate,
@@ -214,10 +210,9 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
 		return err
 	}
 
-	se, err := ociremote.SignedEntity(digest, ociremoteOpts...)
-	if err != nil {
-		return err
-	}
+	// We don't actually need to access the remote entity to attach things to it
+	// so we use a placeholder here.
+	se := ociremote.SignedUnknown(digest)
 
 	signOpts := []mutate.SignOption{
 		mutate.WithDupeDetector(dd),
diff --git a/cmd/cosign/cli/attest/attest_blob.go b/cmd/cosign/cli/attest/attest_blob.go
index 2946ea1c..a0233080 100644
--- a/cmd/cosign/cli/attest/attest_blob.go
+++ b/cmd/cosign/cli/attest/attest_blob.go
@@ -71,6 +71,10 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error
 		return &options.KeyParseError{}
 	}
 
+	if c.PredicatePath == "" {
+		return fmt.Errorf("predicate cannot be empty")
+	}
+
 	if c.Timeout != 0 {
 		var cancelFn context.CancelFunc
 		ctx, cancelFn = context.WithTimeout(ctx, c.Timeout)
@@ -107,10 +111,9 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error
 		hexDigest = c.ArtifactHash
 	}
 
-	fmt.Fprintln(os.Stderr, "Using predicate from:", c.PredicatePath)
-	predicate, err := os.Open(c.PredicatePath)
+	predicate, err := predicateReader(c.PredicatePath)
 	if err != nil {
-		return err
+		return fmt.Errorf("getting predicate reader: %w", err)
 	}
 	defer predicate.Close()
 
diff --git a/cmd/cosign/cli/attest/common.go b/cmd/cosign/cli/attest/common.go
new file mode 100644
index 00000000..b9bb6dce
--- /dev/null
+++ b/cmd/cosign/cli/attest/common.go
@@ -0,0 +1,35 @@
+// Copyright 2023 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 attest
+
+import (
+	"fmt"
+	"io"
+	"os"
+)
+
+func predicateReader(predicatePath string) (io.ReadCloser, error) {
+	if predicatePath == "-" {
+		fmt.Fprintln(os.Stderr, "Using payload from: standard input")
+		return os.Stdin, nil
+	}
+
+	fmt.Fprintln(os.Stderr, "Using payload from:", predicatePath)
+	f, err := os.Open(predicatePath)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}
diff --git a/cmd/cosign/cli/attest/common_test.go b/cmd/cosign/cli/attest/common_test.go
new file mode 100644
index 00000000..26e58af7
--- /dev/null
+++ b/cmd/cosign/cli/attest/common_test.go
@@ -0,0 +1,78 @@
+// Copyright 2023 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 attest
+
+import (
+	"os"
+	"path"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestPredicateReader(t *testing.T) {
+	cases := []struct {
+		name       string
+		path       string
+		wantErr    bool
+		wantStdin  bool
+		createFile bool
+	}{
+		{
+			name:      "standard input",
+			path:      "-",
+			wantStdin: true,
+		},
+		{
+			name:       "regular file",
+			path:       "payload.json",
+			createFile: true,
+		},
+		{
+			name:    "missing file",
+			path:    "payload.json",
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			pf := tc.path
+			if tc.createFile {
+				pf = path.Join(t.TempDir(), tc.path)
+				err := os.WriteFile(pf, []byte("payload"), 0644)
+				require.NoError(t, err)
+			}
+
+			got, err := predicateReader(pf)
+			if err == nil {
+				defer got.Close()
+			}
+
+			if tc.wantErr {
+				require.Error(t, err)
+				return
+			}
+
+			require.NoError(t, err)
+
+			if tc.wantStdin {
+				require.Same(t, os.Stdin, got)
+			} else {
+				require.NotSame(t, os.Stdin, got)
+			}
+		})
+	}
+}
diff --git a/cmd/cosign/cli/attest_blob.go b/cmd/cosign/cli/attest_blob.go
index 81a85b2f..66f904cc 100644
--- a/cmd/cosign/cli/attest_blob.go
+++ b/cmd/cosign/cli/attest_blob.go
@@ -42,7 +42,10 @@ func AttestBlob() *cobra.Command {
   cosign attest-blob --predicate <FILE> --type <TYPE> --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY]/versions/[VERSION] <BLOB>
 
   # attach an attestation to a blob with a key pair stored in Hashicorp Vault
-  cosign attest-blob --predicate <FILE> --type <TYPE> --key hashivault://[KEY] <BLOB>`,
+  cosign attest-blob --predicate <FILE> --type <TYPE> --key hashivault://[KEY] <BLOB>
+
+  # supply attestation via stdin
+  echo <PAYLOAD> | cosign attest-blob --predicate - --yes`,
 
 		Args:             cobra.ExactArgs(1),
 		PersistentPreRun: options.BindViper,
diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go
index fab70bc3..05338a81 100644
--- a/cmd/cosign/cli/verify/verify_blob_test.go
+++ b/cmd/cosign/cli/verify/verify_blob_test.go
@@ -48,6 +48,7 @@ import (
 	"github.com/sigstore/rekor/pkg/generated/models"
 	"github.com/sigstore/rekor/pkg/pki"
 	"github.com/sigstore/rekor/pkg/types"
+	rekor_dsse "github.com/sigstore/rekor/pkg/types/dsse"
 	"github.com/sigstore/rekor/pkg/types/hashedrekord"
 	hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
 	"github.com/sigstore/rekor/pkg/types/intoto"
@@ -861,7 +862,44 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) {
 			t.Fatal("expected error due to expired cert, received nil")
 		}
 	})
-	t.Run("Attestation", func(t *testing.T) {
+	t.Run("dsse Attestation", func(t *testing.T) {
+		identity := "hello@foo.com"
+		issuer := "issuer"
+		leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer)
+
+		stmt := `{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`
+		wrapped := dsse.WrapSigner(signer, ctypes.IntotoPayloadType)
+		signedPayload, err := wrapped.SignMessage(bytes.NewReader([]byte(stmt)), signatureoptions.WithContext(context.Background()))
+		if err != nil {
+			t.Fatal(err)
+		}
+		// intoto sig = json-serialized dsse envelope
+		sig := signedPayload
+
+		// Create bundle
+		entry := genRekorEntry(t, rekor_dsse.KIND, "0.0.1", signedPayload, leafPemCert, sig)
+		b := createBundle(t, sig, leafPemCert, keyless.rekorLogID, leafCert.NotBefore.Unix()+1, entry)
+		b.Bundle.SignedEntryTimestamp = keyless.rekorSignPayload(t, b.Bundle.Payload)
+		bundlePath := writeBundleFile(t, keyless.td, b, "bundle.json")
+		blobPath := writeBlobFile(t, keyless.td, string(signedPayload), "attestation.txt")
+
+		// Verify command
+		cmd := VerifyBlobAttestationCommand{
+			CertVerifyOptions: options.CertVerifyOptions{
+				CertIdentity:   identity,
+				CertOidcIssuer: issuer,
+			},
+			CertRef:       "", // Cert is fetched from bundle
+			CertChain:     "", // Chain is fetched from TUF/SIGSTORE_ROOT_FILE
+			SignaturePath: "", // Sig is fetched from bundle
+			KeyOpts:       options.KeyOpts{BundlePath: bundlePath},
+			IgnoreSCT:     true,
+		}
+		if err := cmd.Exec(context.Background(), blobPath); err != nil {
+			t.Fatal(err)
+		}
+	})
+	t.Run("intoto Attestation", func(t *testing.T) {
 		identity := "hello@foo.com"
 		issuer := "issuer"
 		leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer)
@@ -1192,7 +1230,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) {
 			t.Fatalf("expected error with mismatched root, got %v", err)
 		}
 	})
-	t.Run("Attestation with keyless", func(t *testing.T) {
+	t.Run("intoto Attestation with keyless", func(t *testing.T) {
 		identity := "hello@foo.com"
 		issuer := "issuer"
 		leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer)
@@ -1477,6 +1515,8 @@ func createEntry(ctx context.Context, kind, apiVersion string, blobBytes, certBy
 		props.SignatureBytes = sigBytes
 	case intoto.KIND:
 		props.ArtifactBytes = blobBytes
+	case rekor_dsse.KIND:
+		props.ArtifactBytes = blobBytes
 	default:
 		return nil, fmt.Errorf("unexpected entry kind: %s", kind)
 	}
diff --git a/cmd/cosign/errors/error_wrap_test.go b/cmd/cosign/errors/error_wrap_test.go
index e9559460..6b397f64 100644
--- a/cmd/cosign/errors/error_wrap_test.go
+++ b/cmd/cosign/errors/error_wrap_test.go
@@ -18,24 +18,8 @@ package errors
 import (
 	"errors"
 	"testing"
-
-	verificationError "github.com/sigstore/cosign/v2/pkg/cosign"
 )
 
-func TestWrapWithVerificationError(t *testing.T) {
-	ve := &verificationError.VerificationError{}
-	ve.SetErrorType(verificationError.ErrNoMatchingSignaturesType)
-	err := WrapError(ve)
-
-	var cosignError *CosignError
-	if errors.As(err, &cosignError) {
-		if cosignError.ExitCode() != NoMatchingSignature {
-			t.Fatalf("verification error unsuccessfully wrapped")
-		}
-		t.Logf("verification error successfully wrapped and exit code returned")
-	}
-}
-
 func TestWrapWithGenericCosignError(t *testing.T) {
 	errorText := "i am a generic cosign error"
 	err := WrapError(errors.New(errorText))
diff --git a/cmd/cosign/errors/exit_code_lookup_test.go b/cmd/cosign/errors/exit_code_lookup_test.go
index d9e58461..88ff7bef 100644
--- a/cmd/cosign/errors/exit_code_lookup_test.go
+++ b/cmd/cosign/errors/exit_code_lookup_test.go
@@ -16,13 +16,26 @@
 package errors
 
 import (
+	"fmt"
 	"testing"
+
+	pkgError "github.com/sigstore/cosign/v2/pkg/cosign"
 )
 
-func TestDefaultExitCodeReturnIfErrorTypeDoesNotExist(t *testing.T) {
-	exitCode := LookupExitCodeForErrorType("I do not exist as an error type")
+func TestDefaultExitCodeReturnIfErrorTypeToExitCodeMappingDoesNotExist(t *testing.T) {
+	exitCode := LookupExitCodeForError(fmt.Errorf("I do not exist as an error type"))
 	if exitCode != 1 {
 		t.Fatalf("default exit code not returned when an error type doesn't exist. default should be 1")
 	}
 	t.Logf("Correct default exit code returned")
 }
+
+func TestDefaultExitCodeReturnIfErrorTypeToExitCodeMappingExists(t *testing.T) {
+	// We test with any error that is not a generic CosignError.
+	// In this case, ErrNoMatchingSignatures
+	exitCode := LookupExitCodeForError(&pkgError.ErrNoMatchingSignatures{})
+	if exitCode != NoMatchingSignature {
+		t.Fatalf("NoMatchingSignature exit code not returned when error is thrown")
+	}
+	t.Logf("Correct default exit code returned")
+}
diff --git a/doc/cosign_attest-blob.md b/doc/cosign_attest-blob.md
index 664d3208..e620f4e3 100644
--- a/doc/cosign_attest-blob.md
+++ b/doc/cosign_attest-blob.md
@@ -25,6 +25,9 @@ cosign attest-blob [flags]
 
   # attach an attestation to a blob with a key pair stored in Hashicorp Vault
   cosign attest-blob --predicate <FILE> --type <TYPE> --key hashivault://[KEY] <BLOB>
+
+  # supply attestation via stdin
+  echo <PAYLOAD> | cosign attest-blob --predicate - --yes
 ```
 
 ### Options
@@ -43,7 +46,7 @@ cosign attest-blob [flags]
       --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
       --oidc-issuer string                OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth")
-      --oidc-provider string              Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem, buildkite-agent]
+      --oidc-provider string              Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]
       --oidc-redirect-url string          OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'.
       --output-attestation string         write the attestation to FILE
       --output-certificate string         write the certificate to FILE
diff --git a/doc/cosign_attest.md b/doc/cosign_attest.md
index 2f3b3682..bf7c25e4 100644
--- a/doc/cosign_attest.md
+++ b/doc/cosign_attest.md
@@ -34,6 +34,9 @@ cosign attest [flags]
 
   # attach an attestation to a container image which does not fully support OCI media types
   COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate <FILE> --type <TYPE> --key cosign.key legacy-registry.example.com/my/image
+
+  # supply attestation via stdin
+  echo <PAYLOAD> | cosign attest --predicate - <IMAGE>
 ```
 
 ### Options
@@ -55,7 +58,7 @@ cosign attest [flags]
       --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
       --oidc-issuer string                                                                       OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth")
-      --oidc-provider string                                                                     Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github, filesystem, buildkite-agent]
+      --oidc-provider string                                                                     Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]
       --oidc-redirect-url string                                                                 OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'.
       --predicate string                                                                         path to the predicate file.
   -r, --recursive                                                                                if a multi-arch image is specified, additionally sign each discrete image
diff --git a/doc/cosign_download_attestation.md b/doc/cosign_download_attestation.md
index 7f9d7ea5..8f046b0e 100644
--- a/doc/cosign_download_attestation.md
+++ b/doc/cosign_download_attestation.md
@@ -21,7 +21,7 @@ cosign download attestation [flags]
   -h, --help                                                                                     help for attestation
       --k8s-keychain                                                                             whether to use the kubernetes keychain instead of the default keychain (supports workload identity).
       --platform string                                                                          download attestation for a specific platform image
-      --predicate-type string                                                                    download attestation with matching predicateType annotation
+      --predicate-type string                                                                    download attestation with matching predicateType
 ```
 
 ### Options inherited from parent commands
diff --git a/pkg/cosign/ctlog_test.go b/pkg/cosign/ctlog_test.go
index 92ad2941..2abb48f6 100644
--- a/pkg/cosign/ctlog_test.go
+++ b/pkg/cosign/ctlog_test.go
@@ -33,7 +33,7 @@ Nmo7M3bN7+dQddw9Ibc2R3SV8tzBZw0rST8FKcn4apJepcKM4qUpYUeNfw==
 func TestGetCTLogPubKeys(t *testing.T) {
 	keys, err := GetCTLogPubs(context.Background())
 	if err != nil {
-		t.Errorf("Unexpected error calling GetCTLogPubs, expected nil: %v", err)
+		t.Fatalf("Unexpected error calling GetCTLogPubs, expected nil: %v", err)
 	}
 	if len(keys.Keys) == 0 {
 		t.Errorf("expected 1 or more keys, got 0")
diff --git a/pkg/cosign/errors_test.go b/pkg/cosign/errors_test.go
index 8d6520a9..a75cec5f 100644
--- a/pkg/cosign/errors_test.go
+++ b/pkg/cosign/errors_test.go
@@ -15,20 +15,21 @@
 package cosign
 
 import (
-	"errors"
 	"fmt"
 	"testing"
+
+	"github.com/pkg/errors"
 )
 
 func TestErrors(t *testing.T) {
 	for _, want := range []error{
-		NewVerificationError("not a constant %d", 3),
-		NewVerificationError("not a string %s", "i am a string"),
+		ThrowError(&VerificationFailure{errors.Errorf("not a constant %d", 3)}),
+		ThrowError(&VerificationFailure{errors.Errorf("not a string %s", "i am a string")}),
 	} {
 		t.Run(want.Error(), func(t *testing.T) {
-			verr := &VerificationError{}
+			verr := &VerificationFailure{}
 			if !errors.As(want, &verr) {
-				t.Errorf("%v is not a %T", want, &VerificationError{})
+				t.Errorf("%v is not a %T", want, &VerificationFailure{})
 			}
 
 			// Check that Is sees it as the same error through multiple
diff --git a/pkg/cosign/tlog_test.go b/pkg/cosign/tlog_test.go
index 8afa5633..1034aa1c 100644
--- a/pkg/cosign/tlog_test.go
+++ b/pkg/cosign/tlog_test.go
@@ -38,7 +38,7 @@ var (
 func TestGetRekorPubKeys(t *testing.T) {
 	keys, err := GetRekorPubs(context.Background())
 	if err != nil {
-		t.Errorf("Unexpected error calling GetRekorPubs, expected nil: %v", err)
+		t.Fatalf("Unexpected error calling GetRekorPubs, expected nil: %v", err)
 	}
 	if len(keys.Keys) == 0 {
 		t.Errorf("expected 1 or more keys, got 0")
diff --git a/pkg/oci/mutate/mutate_test.go b/pkg/oci/mutate/mutate_test.go
index 089d5d41..4a315268 100644
--- a/pkg/oci/mutate/mutate_test.go
+++ b/pkg/oci/mutate/mutate_test.go
@@ -163,8 +163,21 @@ func TestSignEntity(t *testing.T) {
 	}
 	sii := signed.ImageIndex(ii)
 
+	// Create an explicitly unknown implementation of oci.SignedEntity, which we
+	// feed through the table tests below.
+	want := make([]byte, 300)
+	rand.Read(want)
+	orig, err := static.NewFile(want)
+	if err != nil {
+		t.Fatalf("static.NewFile() = %v", err)
+	}
+	sunk, err := AttachFileToUnknown(sii, "sbom", orig)
+	if err != nil {
+		t.Fatalf("AttachFileToUnknown() = %v", err)
+	}
+
 	t.Run("attach SBOMs", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			want := make([]byte, 300)
 			rand.Read(want)
 
@@ -197,7 +210,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("without duplicate detector (signature)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewSignature(nil, "")
 			if err != nil {
 				t.Fatalf("static.NewSignature() = %v", err)
@@ -232,7 +245,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("without duplicate detector (attestation)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewAttestation([]byte("payload"))
 			if err != nil {
 				t.Fatalf("static.NewAttestation() = %v", err)
@@ -267,7 +280,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("with duplicate detector (signature)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewSignature(nil, "")
 			if err != nil {
 				t.Fatalf("static.NewSignature() = %v", err)
@@ -306,7 +319,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("with duplicate detector (attestation)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewAttestation([]byte("blah"))
 			if err != nil {
 				t.Fatalf("static.NewAttestation() = %v", err)
@@ -345,7 +358,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("with erroring duplicate detector (signature)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewSignature(nil, "")
 			if err != nil {
 				t.Fatalf("static.NewSignature() = %v", err)
@@ -379,7 +392,7 @@ func TestSignEntity(t *testing.T) {
 	})
 
 	t.Run("with erroring duplicate detector (attestation)", func(t *testing.T) {
-		for _, se := range []oci.SignedEntity{si, sii} {
+		for _, se := range []oci.SignedEntity{si, sii, sunk} {
 			orig, err := static.NewAttestation([]byte("blah"))
 			if err != nil {
 				t.Fatalf("static.NewAttestation() = %v", err)
diff --git a/pkg/oci/remote/unknown_test.go b/pkg/oci/remote/unknown_test.go
new file mode 100644
index 00000000..384d2973
--- /dev/null
+++ b/pkg/oci/remote/unknown_test.go
@@ -0,0 +1,101 @@
+//
+// Copyright 2023 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 remote
+
+import (
+	"testing"
+
+	"github.com/google/go-containerregistry/pkg/name"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/random"
+	"github.com/google/go-containerregistry/pkg/v1/remote"
+)
+
+func TestSignedUnknown(t *testing.T) {
+	ri := remote.Image
+	t.Cleanup(func() {
+		remoteImage = ri
+	})
+	wantLayers := int64(7)
+
+	remoteImage = func(ref name.Reference, options ...remote.Option) (v1.Image, error) {
+		// Only called for signature images
+		return random.Image(300 /* byteSize */, wantLayers)
+	}
+
+	// :nonroot as of 2023/05/07
+	digest, err := name.NewDigest("gcr.io/distroless/static@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f")
+	if err != nil {
+		t.Fatalf("ParseRef() = %v", err)
+	}
+	si := SignedUnknown(digest)
+
+	sigs, err := si.Signatures()
+	if err != nil {
+		t.Fatalf("Signatures() = %v", err)
+	}
+
+	if sl, err := sigs.Get(); err != nil {
+		t.Errorf("Get() = %v", err)
+	} else if got := int64(len(sl)); got != wantLayers {
+		t.Errorf("len(Get()) = %d, wanted %d", got, wantLayers)
+	}
+
+	atts, err := si.Attestations()
+	if err != nil {
+		t.Fatalf("Signatures() = %v", err)
+	}
+
+	if al, err := atts.Get(); err != nil {
+		t.Errorf("Get() = %v", err)
+	} else if got := int64(len(al)); got != wantLayers {
+		t.Errorf("len(Get()) = %d, wanted %d", got, wantLayers)
+	}
+}
+
+func TestSignedUnknownWithAttachment(t *testing.T) {
+	ri := remote.Image
+	t.Cleanup(func() {
+		remoteImage = ri
+	})
+	wantLayers := int64(1) // File must have a single layer
+
+	remoteImage = func(ref name.Reference, options ...remote.Option) (v1.Image, error) {
+		// Only called for signature images
+		return random.Image(300 /* byteSize */, wantLayers)
+	}
+
+	// :nonroot as of 2023/05/07
+	digest, err := name.NewDigest("gcr.io/distroless/static@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f")
+	if err != nil {
+		t.Fatalf("ParseRef() = %v", err)
+	}
+	si := SignedUnknown(digest)
+
+	file, err := si.Attachment("sbom")
+	if err != nil {
+		t.Fatalf("Signatures() = %v", err)
+	}
+
+	payload, err := file.Payload()
+	if err != nil {
+		t.Errorf("Payload() = %v", err)
+	}
+	// We check greater than because it's wrapped in a tarball with `random.Layer`
+	if len(payload) < 300 {
+		t.Errorf("Payload() = %d bytes, wanted %d", len(payload), 300)
+	}
+}
diff --git a/test/e2e_test.go b/test/e2e_test.go
index ea30d360..ff651517 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -22,8 +22,12 @@ import (
 	"bytes"
 	"context"
 	"crypto"
+	"crypto/rand"
+	"crypto/sha256"
+	"crypto/x509"
 	"encoding/base64"
 	"encoding/json"
+	"encoding/pem"
 	"fmt"
 	"net/http/httptest"
 	"net/url"
@@ -55,6 +59,8 @@ import (
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/publickey"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
 	cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify"
+	"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
+	"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa/client"
 	"github.com/sigstore/cosign/v2/pkg/cosign"
 	"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
 	"github.com/sigstore/cosign/v2/pkg/cosign/env"
@@ -112,6 +118,23 @@ var verifyTSA = func(keyRef, imageRef string, checkClaims bool, annotations map[
 	return cmd.Exec(context.Background(), args)
 }
 
+var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, skipTlogVerify bool) error {
+	cmd := cliverify.VerifyCommand{
+		CertVerifyOptions: options.CertVerifyOptions{
+			CertOidcIssuerRegexp: ".*",
+			CertIdentityRegexp:   ".*",
+		},
+		HashAlgorithm:    crypto.SHA256,
+		TSACertChainPath: tsaCertChain,
+		IgnoreSCT:        skipSCT,
+		IgnoreTlog:       skipTlogVerify,
+	}
+
+	args := []string{imageRef}
+
+	return cmd.Exec(context.Background(), args)
+}
+
 // Used to verify local images stored on disk
 var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error {
 	cmd := cliverify.VerifyCommand{
@@ -809,6 +832,84 @@ func TestAttestationRFC3161Timestamp(t *testing.T) {
 	must(verifyAttestation.Exec(ctx, []string{imgName}), t)
 }
 
+func TestAttachWithRFC3161Timestamp(t *testing.T) {
+	ctx := context.Background()
+	// TSA server needed to create timestamp
+	viper.Set("timestamp-signer", "memory")
+	apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, 10*time.Second, 10*time.Second)
+	server := httptest.NewServer(apiServer.GetHandler())
+	t.Cleanup(server.Close)
+
+	repo, stop := reg(t)
+	defer stop()
+	td := t.TempDir()
+
+	imgName := path.Join(repo, "cosign-attach-timestamp-e2e")
+
+	_, _, cleanup := mkimage(t, imgName)
+	defer cleanup()
+
+	b := bytes.Buffer{}
+	must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t)
+
+	rootCert, rootKey, _ := GenerateRootCa()
+	subCert, subKey, _ := GenerateSubordinateCa(rootCert, rootKey)
+	leafCert, privKey, _ := GenerateLeafCert("subject@mail.com", "oidc-issuer", subCert, subKey)
+	pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw})
+	pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw})
+	pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw})
+
+	rootPool := x509.NewCertPool()
+	rootPool.AddCert(rootCert)
+
+	payloadref := mkfile(b.String(), td, t)
+
+	h := sha256.Sum256(b.Bytes())
+	signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256)
+	b64signature := base64.StdEncoding.EncodeToString([]byte(signature))
+	sigRef := mkfile(b64signature, td, t)
+	pemleafRef := mkfile(string(pemLeaf), td, t)
+	pemrootRef := mkfile(string(pemRoot), td, t)
+
+	certchainRef := mkfile(string(append(pemSub[:], pemRoot[:]...)), td, t)
+
+	t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef)
+
+	tsclient, err := tsaclient.GetTimestampClient(server.URL)
+	if err != nil {
+		t.Error(err)
+	}
+
+	chain, err := tsclient.Timestamp.GetTimestampCertChain(nil)
+	if err != nil {
+		t.Fatalf("unexpected error getting timestamp chain: %v", err)
+	}
+
+	file, err := os.CreateTemp(os.TempDir(), "tempfile")
+	if err != nil {
+		t.Fatalf("error creating temp file: %v", err)
+	}
+	defer os.Remove(file.Name())
+	_, err = file.WriteString(chain.Payload)
+	if err != nil {
+		t.Fatalf("error writing chain payload to temp file: %v", err)
+	}
+
+	tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(server.URL+"/api/v1/timestamp"))
+	if err != nil {
+		t.Fatalf("unexpected error creating timestamp: %v", err)
+	}
+	rfc3161TSRef := mkfile(string(tsBytes), td, t)
+
+	// Upload it!
+	err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, rfc3161TSRef, imgName)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	must(verifyKeylessTSA(imgName, file.Name(), true, true), t)
+}
+
 func TestRekorBundle(t *testing.T) {
 	// turn on the tlog
 	defer setenv(t, env.VariableExperimental.String(), "1")()
@@ -1490,9 +1591,8 @@ func TestUploadDownload(t *testing.T) {
 			} else {
 				sigRef = signature
 			}
-
 			// Upload it!
-			err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, "", "", imgName)
+			err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, "", "", "", imgName)
 			if testCase.expectedErr {
 				mustErr(err, t)
 			} else {
diff --git a/test/e2e_test_attach.sh b/test/e2e_test_attach.sh
index 35e44f86..708986fe 100644
--- a/test/e2e_test_attach.sh
+++ b/test/e2e_test_attach.sh
@@ -69,7 +69,7 @@ crane manifest $(./cosign triangulate $IMAGE_URI_DIGEST) | grep -q "dev.sigstore
 ## Verify Signature, payload, cert and cert-chain using SIGSTORE_ROOT_FILE
 
 export SIGSTORE_ROOT_FILE=./rootcert.pem
-./cosign verify $IMAGE_URI_DIGEST --insecure-ignore-sct --insecure-skip-tlog-verify --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'
+./cosign verify $IMAGE_URI_DIGEST --insecure-ignore-sct --insecure-ignore-tlog --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'
 
 
 # clean up a bit
diff --git a/test/e2e_test_secrets.sh b/test/e2e_test_secrets.sh
index 176a226a..f5cd317d 100755
--- a/test/e2e_test_secrets.sh
+++ b/test/e2e_test_secrets.sh
@@ -36,6 +36,7 @@ signing_key=cosign.key
 verification_key=cosign.pub
 img="${TEST_INSTANCE_REPO}/test"
 img2="${TEST_INSTANCE_REPO}/test-2"
+img3="${TEST_INSTANCE_REPO}/test-3"
 legacy_img="${TEST_INSTANCE_REPO}/legacy-test"
 for image in $img $img2 $legacy_img
 do
@@ -73,6 +74,21 @@ do
     ./cosign verify --key ${verification_key} "${multiarch_img}@$(crane digest --platform=$arch ${multiarch_img})"
 done
 
+# sign/attest an image that doesn't exist (yet) in the registry
+# This digest was generated with the following command and
+# does not exist anywhere AFAIK:
+#   head -10 /dev/urandom | sha256sum | cut -d' ' -f 1
+# We don't just run this here because the macos leg doesn't
+# have sha256sum
+./cosign sign --key ${signing_key} "$img3@sha256:17b14220441083f55dfa21e1deb3720457d3c2d571219801d629b43c53b99627"
+PREDICATE_FILE=$(mktemp)
+cat > "${PREDICATE_FILE}" <<EOF
+{
+  "foo": "bar"
+}
+EOF
+./cosign attest --key ${signing_key} --type custom --predicate "${PREDICATE_FILE}" "$img3@sha256:17b14220441083f55dfa21e1deb3720457d3c2d571219801d629b43c53b99627"
+
 ## confirm use of OCI media type in signature image
 crane manifest $(./cosign triangulate $img) | grep -q "application/vnd.oci.image.config.v1+json"