diff --git a/app/cli/cmd/attestation_verify.go b/app/cli/cmd/attestation_verify.go index 0c28d372b..c80fb5cac 100644 --- a/app/cli/cmd/attestation_verify.go +++ b/app/cli/cmd/attestation_verify.go @@ -1,5 +1,5 @@ // -// Copyright 2025 The Chainloop Authors. +// Copyright 2025-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,15 +35,11 @@ func newAttestationVerifyCmd() *cobra.Command { # verify an attestation stored in an https endpoint chainloop attestation verify -b https://myrepository/attestation.json`, RunE: func(cmd *cobra.Command, _ []string) error { - res, err := action.NewAttestationVerifyAction(ActionOpts).Run(cmd.Context(), fileOrURL) - if err != nil { + if err := action.NewAttestationVerifyAction(ActionOpts).Run(cmd.Context(), fileOrURL); err != nil { return fmt.Errorf("verifying attestation: %w", err) } - if res { - ActionOpts.Logger.Info().Msg("attestation verified successfully") - } else { - ActionOpts.Logger.Warn().Msg("attestation couldn't be verified") - } + + ActionOpts.Logger.Info().Msg("attestation verified successfully") return nil }, diff --git a/app/cli/pkg/action/attestation_verify.go b/app/cli/pkg/action/attestation_verify.go index b7c5feb06..d14849baa 100644 --- a/app/cli/pkg/action/attestation_verify.go +++ b/app/cli/pkg/action/attestation_verify.go @@ -1,5 +1,5 @@ // -// Copyright 2025 The Chainloop Authors. +// Copyright 2025-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,12 @@ import ( "google.golang.org/grpc/status" ) +// ErrAttestationNotVerifiable indicates that the attestation's authenticity could not be +// established at all, either because the bundle carries no verification material or because +// the control plane has no trusted root to verify it against. It is a verification failure: +// a bundle that was never verified must never be reported as verified. +var ErrAttestationNotVerifiable = errors.New("attestation could not be verified") + type AttestationVerifyAction struct { cfg *ActionsOpts } @@ -35,39 +41,57 @@ func NewAttestationVerifyAction(cfg *ActionsOpts) *AttestationVerifyAction { return &AttestationVerifyAction{cfg} } -func (action *AttestationVerifyAction) Run(ctx context.Context, fileOrURL string) (bool, error) { +// Run verifies the given attestation bundle and returns an error if it can not be +// verified, so callers gating on the exit code never take an unverified bundle as good. +func (action *AttestationVerifyAction) Run(ctx context.Context, fileOrURL string) error { content, err := blob.LoadFileOrURL(fileOrURL) if err != nil { - return false, fmt.Errorf("loading attestation: %w", err) + return fmt.Errorf("loading attestation: %w", err) } - return verifyBundle(ctx, content, action.cfg) + return verifyBundleOrFail(ctx, content, action.cfg) } -func verifyBundle(ctx context.Context, content []byte, opts *ActionsOpts) (bool, error) { +// verifyBundleOrFail verifies the bundle against the trusted root configured in the control +// plane. Any outcome other than a successful verification is returned as an error. +func verifyBundleOrFail(ctx context.Context, content []byte, opts *ActionsOpts) error { sc := pb.NewSigningServiceClient(opts.CPConnection) trResp, err := sc.GetTrustedRoot(ctx, &pb.GetTrustedRootRequest{}) if err != nil { - // if trusted root is not implemented, skip verification - if status.Code(err) != codes.Unimplemented { - return false, fmt.Errorf("failed getting trusted root: %w", err) + if status.Code(err) == codes.Unimplemented { + return fmt.Errorf("%w: the control plane has no trusted root configured", ErrAttestationNotVerifiable) } + + return fmt.Errorf("failed getting trusted root: %w", err) } - if trResp != nil { - tr, err := trustedRootPbToVerifier(trResp) - if err != nil { - return false, fmt.Errorf("getting roots: %w", err) + tr, err := trustedRootPbToVerifier(trResp) + if err != nil { + return fmt.Errorf("getting roots: %w", err) + } + + if err := verifier.VerifyBundle(ctx, content, tr); err != nil { + if errors.Is(err, verifier.ErrMissingVerificationMaterial) { + return fmt.Errorf("%w: the bundle does not contain verification material", ErrAttestationNotVerifiable) } - if err = verifier.VerifyBundle(ctx, content, tr); err != nil { - if !errors.Is(err, verifier.ErrMissingVerificationMaterial) { - opts.Logger.Debug().Err(err).Msg("bundle verification failed") - return false, errors.New("bundle verification failed") - } - } else { - return true, nil + + opts.Logger.Debug().Err(err).Msg("bundle verification failed") + return errors.New("bundle verification failed") + } + + return nil +} + +// verifyBundle reports whether the bundle could be verified. Unlike verifyBundleOrFail, a +// bundle that is not verifiable is not an error, it is simply reported as not verified. +func verifyBundle(ctx context.Context, content []byte, opts *ActionsOpts) (bool, error) { + if err := verifyBundleOrFail(ctx, content, opts); err != nil { + if errors.Is(err, ErrAttestationNotVerifiable) { + return false, nil } + + return false, err } - return false, nil + return true, nil } diff --git a/app/cli/pkg/action/attestation_verify_test.go b/app/cli/pkg/action/attestation_verify_test.go new file mode 100644 index 000000000..607d52b1e --- /dev/null +++ b/app/cli/pkg/action/attestation_verify_test.go @@ -0,0 +1,143 @@ +// +// Copyright 2026 The Chainloop 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 action + +import ( + "context" + "errors" + "net" + "os" + "testing" + + pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "google.golang.org/grpc/test/bufconn" +) + +// caKeyID is the authority key identifier of testdata/ca.pub, the CA that signed +// the certificate embedded in the valid test bundles. +const caKeyID = "2a522d9652e0933d2a1237c395bc116e012f86dffff13122da59f76e0d2abe27" + +func TestAttestationVerifyRun(t *testing.T) { + testCases := []struct { + name string + bundle string + // withTrustedRoot serves the test CA as trusted root, otherwise the + // signing service replies Unimplemented, simulating a control plane + // with no signing CA configured. + withTrustedRoot bool + expectErr string + // expectNotVerifiable asserts the error is the sentinel telling the + // bundle was never verified, as opposed to failing verification. + expectNotVerifiable bool + }{ + { + name: "valid bundle is verified", + bundle: "testdata/bundle_valid.json", + withTrustedRoot: true, + }, + { + name: "bundle without verification material fails", + bundle: "testdata/bundle_valid_nomaterial.json", + withTrustedRoot: true, + expectErr: "verification material", + expectNotVerifiable: true, + }, + { + name: "no trusted root configured fails", + bundle: "testdata/bundle_valid.json", + expectErr: "trusted root", + expectNotVerifiable: true, + }, + { + name: "tampered bundle fails verification", + bundle: "testdata/bundle_invalid.json", + withTrustedRoot: true, + expectErr: "bundle verification failed", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + opts := &ActionsOpts{ + CPConnection: newSigningServiceConn(t, tc.withTrustedRoot), + Logger: zerolog.Nop(), + } + + err := NewAttestationVerifyAction(opts).Run(context.Background(), tc.bundle) + if tc.expectErr == "" { + assert.NoError(t, err) + return + } + + require.Error(t, err) + assert.Contains(t, err.Error(), tc.expectErr) + assert.Equal(t, tc.expectNotVerifiable, errors.Is(err, ErrAttestationNotVerifiable)) + }) + } +} + +// fakeSigningService serves the test CA as trusted root, or replies Unimplemented +// when none is configured. +type fakeSigningService struct { + pb.UnimplementedSigningServiceServer + trustedRoot *pb.GetTrustedRootResponse +} + +func (s *fakeSigningService) GetTrustedRoot(context.Context, *pb.GetTrustedRootRequest) (*pb.GetTrustedRootResponse, error) { + if s.trustedRoot == nil { + return nil, status.Error(codes.Unimplemented, "trusted root not available") + } + + return s.trustedRoot, nil +} + +func newSigningServiceConn(t *testing.T, withTrustedRoot bool) *grpc.ClientConn { + t.Helper() + + svc := &fakeSigningService{} + if withTrustedRoot { + ca, err := os.ReadFile("testdata/ca.pub") + require.NoError(t, err) + svc.trustedRoot = &pb.GetTrustedRootResponse{ + Keys: map[string]*pb.CertificateChain{caKeyID: {Certificates: []string{string(ca)}}}, + } + } + + l := bufconn.Listen(1 << 20) + srv := grpc.NewServer() + pb.RegisterSigningServiceServer(srv, svc) + go func() { _ = srv.Serve(l) }() + + conn, err := grpc.NewClient("passthrough:///bufnet", + grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { return l.DialContext(ctx) }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + require.NoError(t, err) + + t.Cleanup(func() { + conn.Close() + srv.Stop() + }) + + return conn +} diff --git a/app/cli/pkg/action/testdata/bundle_invalid.json b/app/cli/pkg/action/testdata/bundle_invalid.json new file mode 100644 index 000000000..08b52faee --- /dev/null +++ b/app/cli/pkg/action/testdata/bundle_invalid.json @@ -0,0 +1,17 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.3", + "verificationMaterial": { + "certificate": { + "rawBytes": "MIIDtzCCAZ+gAwIBAgIUHfh44qKz/2gMfB9BpJea03EW5SowDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCRVMxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAoMCUNoYWlubG9vcDESMBAGA1UECwwJY2hhaW5sb29wMRIwEAYDVQQDDAljaGFpbmxvb3AwHhcNMjUwMjEyMDkxOTEzWhcNMjUwMjEyMDkyOTEzWjAvMS0wKwYDVQQKEyQxOGMzZjc4Mi00OTM2LTQ2MzAtYWI4ZC0yMGI1MTEzNjY2OTkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATKeTBItaVgTUP55i2Xh4ygIvWeeQGGezrb1xee1k/h3COyK37D/xR+uo8/wBnti02rWJmsZQtOq7CZwONU48n6o2cwZTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFN8DZs+da6GELfQB1YuAfNay2rXPMB8GA1UdIwQYMBaAFKDk5Tdk8eOOMf/+GDw+62HNH6XKMA0GCSqGSIb3DQEBCwUAA4ICAQBwFFmXUNEB5yvD96iFclWxPhiCC+nGSrZl/Mq3emOQD31jkSgvH+SBkoLOG+CUwN3GANmSP99hQjcQUZW7WTvlrScUNVKmhKoODhlwajjbZ/FHK4JcKRX+2JlCPAauRiP2Z5ZSTqQT0CySs/eDdffMxzG6bzRJN/+fQheKbdnHwFgIuF2QD3TDPhXj8D7mHalWNAf4Urq7tR+8GJj9LhQwz5ioqBLBN5FxngpVZGcKX+B+jbGXij/s8Zn8PXJFQ+YtFu65++AGVWS0HpPMUDH6n/M/oInmh/jYworVr0qCMYmohVRu1doyL7LXfndApWmUOgZHhxdBRIpCBM8xy90TRylYfnWiJ338ERfh9fc+luNPxmfBADTY8Tli9OkgQXHdOW55B9WFnZSfIGWWgBH9aPzj6+VbOe/2jHKS1Gg3WbqYGfVTLjwPqpRkisnWPbWY/istvX53x1bGWvzZT+MmDlmfXnPAS/sjbKOK3EVofjNw0ziYaEuh/85P/iKk0+kV5ufmedte7J52sivJYISUi/+zys5jUpiOuVcAQN/1PlwtILZ29H4e9CNjPtGcTZUgJj48bfSgjJSBPxIoKY7L+yHpSmx27AfCiYw7R/U+wWHmQ0FCDgLOIv02jvKGNn+8H9pw//gNs9CnKZUSIOsAY5UeZLnOvp0C8TZormG4GA==" + } + }, + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCAic3ViamVjdCI6W3sibmFtZSI6ImNoYWlubG9vcC53b3JrZmxvdy5teXdmIiwgImRpZ2VzdCI6eyJzaGEyNTYiOiIyNjNjOTM1MDMxMGI5YzBiM2U0OGIwY2Y1ZDg0ZDM4NDAxZDBkZTAxMzMxM2RhOTcxZGE2NWViOWRiYzViODQ1In19LCB7Im5hbWUiOiJnaXQuaGVhZCIsICJkaWdlc3QiOnsic2hhMSI6ImJlYjBmOGMzYjRlNTYxOWIxM2Y2NDY5MTA2NDIwOTlhMTQzZDk1YTYifSwgImFubm90YXRpb25zIjp7ImF1dGhvci5lbWFpbCI6ImppcGFyaXNAY2hhaW5sb29wLmRldiIsICJhdXRob3IubmFtZSI6Ikpvc2UgSS4gUGFyaXMiLCAiZGF0ZSI6IjIwMjUtMDItMTJUMDk6MTg6MTZaIiwgIm1lc3NhZ2UiOiJNZXJnZSBicmFuY2ggJ21haW4nIGludG8gcGZtLTIyODMtdmVyaWYtY2hhaW5sb29wXG5cblNpZ25lZC1vZmYtYnk6IEpvc2UgSS4gUGFyaXMgPGppcGFyaXNAY2hhaW5sb29wLmRldj5cbiIsICJyZW1vdGVzIjpbeyJuYW1lIjoib3JpZ2luIiwgInVybCI6ImdpdEBnaXRodWIuY29tOmppcGFyaXMvY2hhaW5sb29wLmdpdCJ9LCB7Im5hbWUiOiJ1cHN0cmVhbSIsICJ1cmwiOiJnaXRAZ2l0aHViLmNvbTpjaGFpbmxvb3AtZGV2L2NoYWlubG9vcC5naXQifSwgeyJuYW1lIjoibWlndWUiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL21pZ21hcnRyaS9jaGFpbmxvb3AvIn0sIHsibmFtZSI6ImphdmkiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL2phdmlybG4vY2hhaW5sb29wIn1dLCAic2lnbmF0dXJlIjoiLS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS1cblxuaVFJeUJBQUJDQUFkRmlFRWpINTFITVdrQ05ZMmRoWndvSXR5Q2srbTN0NEZBbWVzWjFvQUNna1FvSXR5Q2srbVxuM3Q2K2lRLzRzdnhhUWpRU1NEUVVzNU9iZkJieGpZWkViTkgyemd0b0NYZ3U3QWdXcXNHbzVRUFRPblFEVm1VQ1xuajZrZnViejZ4WW9ZUUg4UjhTcEdPbWlJMTRiVUoweHZhTUZzdUp4WXVCdkVpZm9aQ2F0Mk1mM2RlUklYT2ZFRlxuSVdGdFlhb3B1blpSd05SOHNyYUZaaUtDcjNkVWJLbDZNeHdQek5JN2kxOEt5REcyUUsxcDdaUmxJK25ncGVaRVxuNU1XakM1UUFCTEhVMkJsMlpPSjFJa212RENTaXJKY3Arakw4eHk4TXJhOWQvSFVyNng2TlFDTmtYVEM5U2svNVxucHlPYWVBNVpaZExkaWgremc1cmNTL2RBYlJYMktueHdXVktJMzI2dWx0by9lN0tYVWZJNEwzRkNSQXNPWjJ0cVxuV2p0Y3JxWkhqRkVZOWdQVEU2TGppaU9uNkZMRXFmdk5ySHY1M0NNS0wxNnZLMTJEdUZrN3dMemU1eEpYMVRhdVxuQjc0dnFra21tcjVXSDBOeGhyS0p0UVhXZ291R2lJY1MyOFByV1F2QmtDdE90cDM2djRrUTBEOWdFOTVXcFVSNFxuRU9hTHB2WHpJdDBPR2IwMWRHZ2pkbm9kZFZRNVQvSDM1N3BwREtLZ254THhBWHFqRDRIejFmM0RoS1gvODZxOVxueGlLeWVZVXZDbGorOHkzQkFMcE4weitROWlNTjE4VFlRbnFsTE9kdG10ejM1TjdlQTdSSzRTc3l0eEsrNnVTZ1xuT2Z5SGQwZjd3T1AwUU1PczBiOTdyeWRvTFAxUU9kc1VjekhNQ2xkVy81aEZwbXp5SytWQ3B3Yi9pNGU1a0wyWVxuN293WFNOcUNRMTgyQWhoV3crZ1A0bzhhaVVCRytGcG41Y2FtNGhHaG1Ha0p4WDJrbEE9PVxuPXA3N1Fcbi0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLVxuIn19XSwgInByZWRpY2F0ZVR5cGUiOiJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjIiLCAicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6ImNoYWlubG9vcC5kZXYvd29ya2Zsb3dydW4vdjAuMSIsICJidWlsZGVyIjp7ImlkIjoiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1NjpkY2M0OGE5OGRlNjlkYTQwZDQ4NjgwYjBmMmM0YjQ1YWFhODNjYzY1YmIzMGE1MjBhYzk3MTRjYmZmZDFmZDdhIn0sICJtZXRhZGF0YSI6eyJjb250cmFjdE5hbWUiOiJteXByb2plY3QtbXl3ZiIsICJjb250cmFjdFZlcnNpb24iOiI5MyIsICJmaW5pc2hlZEF0IjoiMjAyNS0wMi0xMlQwOToxOToxMy43NzIyMTNaIiwgImluaXRpYWxpemVkQXQiOiIyMDI1LTAyLTEyVDA5OjE4OjUwLjU2ODEyWiIsICJuYW1lIjoibXl3ZiIsICJvcmdhbml6YXRpb24iOiJteS1vcmciLCAicHJvamVjdCI6Im15cHJvamVjdCIsICJwcm9qZWN0VmVyc2lvbiI6InYwLjE2Mi4wIiwgInByb2plY3RWZXJzaW9uUHJlcmVsZWFzZSI6dHJ1ZSwgInRlYW0iOiIiLCAid29ya2Zsb3dJRCI6Ijk1ZjFiNTk3LTIwZTEtNDk4Yy1iOTMxLWZjNmQ3MzMxODYwZSIsICJ3b3JrZmxvd1J1bklEIjoiNWZlZDk5ZWUtYzQ4ZS00NzVlLWFiOWQtMjk1NGMxZjliNDk1In0sICJwb2xpY3lBdHRCbG9ja2VkIjpmYWxzZSwgInBvbGljeUJsb2NrQnlwYXNzRW5hYmxlZCI6ZmFsc2UsICJwb2xpY3lDaGVja0Jsb2NraW5nU3RyYXRlZ3kiOiJBRFZJU09SWSIsICJwb2xpY3lFdmFsdWF0aW9ucyI6eyJDSEFJTkxPT1AuQVRURVNUQVRJT04iOlt7ImRlc2NyaXB0aW9uIjoiVmVyaWZpZXMgdGhhdCB0aGUgYXR0ZXN0YXRpb24gZXhwbGljaXRseSByZWZlcmVuY2VzIGEgc3BlY2lmaWMgR2l0IGNvbW1pdCIsICJuYW1lIjoic291cmNlLWNvbW1pdCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNvdXJjZS1jb21taXQiLCAib3JnYW5pemF0aW9uIjoiIn0sICJkaWdlc3QiOnsic2hhMjU2IjoiMTY0YmMzMjJiZjM1MGQ2YmI0NTc2OGM4NmRiMTgwZjUzMTUwODVjNzkxYWRlYmFmMDk0OTBmOWNjZTk5NTI4YyJ9LCAibmFtZSI6InNvdXJjZS1jb21taXQiLCAidXJpIjoiY2hhaW5sb29wOi8vbG9jYWxob3N0OjgwMDIvc291cmNlLWNvbW1pdCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9jb21taXQtc2lnbmVkIl0sICJza2lwcGVkIjpmYWxzZSwgInR5cGUiOiJBVFRFU1RBVElPTiIsICJ3aXRoIjp7ImNoZWNrX3NpZ25hdHVyZSI6InRydWUifX0sIHsiYW5ub3RhdGlvbnMiOnsiY2F0ZWdvcnkiOiJzYm9tIn0sICJkZXNjcmlwdGlvbiI6IkNoZWNrcyB0aGF0IGVpdGhlciBhIFNQRFggb3IgQ3ljbG9uZURYIFNCT00gbWF0ZXJpYWwgaXMgcHJlc2VudCBpbiB0aGUgYXR0ZXN0YXRpb24iLCAiZ3JvdXBSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcXVhbGl0eSIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiI3MDM3Y2Q2ZTk0YWY1Y2I4MDAyZGZjZWZjNzJlYjhkMDMyM2RmMDZjNTI2YzQxOGQzMTVmMGRhYWNjOWRjYzI2In0sICJuYW1lIjoic2JvbS1xdWFsaXR5IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcXVhbGl0eSJ9LCAibmFtZSI6InNib20tcHJlc2VudCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcHJlc2VudCIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiJlMmRhMDdjNGRhYjc2YTU4YjQ3NGM2OGEzYzVhNTQzM2JmNGEyOGVkODgxZDRjZWFhMTZlOTE3MTYxNjg0ZWEyIn0sICJuYW1lIjoic2JvbS1wcmVzZW50IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcHJlc2VudCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9zYm9tLXF1YWxpdHkiXSwgInNraXBwZWQiOmZhbHNlLCAidHlwZSI6IkFUVEVTVEFUSU9OIiwgInZpb2xhdGlvbnMiOlt7Im1lc3NhZ2UiOiJtaXNzaW5nIFNCT00gbWF0ZXJpYWwiLCAic3ViamVjdCI6InNib20tcHJlc2VudCJ9XX1dfSwgInBvbGljeUhhc1Zpb2xhdGlvbnMiOnRydWUsICJydW5uZXJUeXBlIjoiUlVOTkVSX1RZUEVfVU5TUEVDSUZJRUQifX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "TUVVQ0lCR09pQkVOdDVGQ3l6OUp2d2tGaVRpZzgvQWN2YmJoNG0ycnJnd0trODg4QWlFQTA3YUdyMUk2MndaY0RaNGdWNTJBb0pCUXRUdVg3Mi9TRU4xOVdLZWdvNzA9" + } + ] + } +} \ No newline at end of file diff --git a/app/cli/pkg/action/testdata/bundle_valid.json b/app/cli/pkg/action/testdata/bundle_valid.json new file mode 100644 index 000000000..9dd87867d --- /dev/null +++ b/app/cli/pkg/action/testdata/bundle_valid.json @@ -0,0 +1,17 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.3", + "verificationMaterial": { + "certificate": { + "rawBytes": "MIIDtzCCAZ+gAwIBAgIUHfh44qKz/2gMfB9BpJea03EW5SowDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCRVMxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAoMCUNoYWlubG9vcDESMBAGA1UECwwJY2hhaW5sb29wMRIwEAYDVQQDDAljaGFpbmxvb3AwHhcNMjUwMjEyMDkxOTEzWhcNMjUwMjEyMDkyOTEzWjAvMS0wKwYDVQQKEyQxOGMzZjc4Mi00OTM2LTQ2MzAtYWI4ZC0yMGI1MTEzNjY2OTkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATKeTBItaVgTUP55i2Xh4ygIvWeeQGGezrb1xee1k/h3COyK37D/xR+uo8/wBnti02rWJmsZQtOq7CZwONU48n6o2cwZTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFN8DZs+da6GELfQB1YuAfNay2rXPMB8GA1UdIwQYMBaAFKDk5Tdk8eOOMf/+GDw+62HNH6XKMA0GCSqGSIb3DQEBCwUAA4ICAQBwFFmXUNEB5yvD96iFclWxPhiCC+nGSrZl/Mq3emOQD31jkSgvH+SBkoLOG+CUwN3GANmSP99hQjcQUZW7WTvlrScUNVKmhKoODhlwajjbZ/FHK4JcKRX+2JlCPAauRiP2Z5ZSTqQT0CySs/eDdffMxzG6bzRJN/+fQheKbdnHwFgIuF2QD3TDPhXj8D7mHalWNAf4Urq7tR+8GJj9LhQwz5ioqBLBN5FxngpVZGcKX+B+jbGXij/s8Zn8PXJFQ+YtFu65++AGVWS0HpPMUDH6n/M/oInmh/jYworVr0qCMYmohVRu1doyL7LXfndApWmUOgZHhxdBRIpCBM8xy90TRylYfnWiJ338ERfh9fc+luNPxmfBADTY8Tli9OkgQXHdOW55B9WFnZSfIGWWgBH9aPzj6+VbOe/2jHKS1Gg3WbqYGfVTLjwPqpRkisnWPbWY/istvX53x1bGWvzZT+MmDlmfXnPAS/sjbKOK3EVofjNw0ziYaEuh/85P/iKk0+kV5ufmedte7J52sivJYISUi/+zys5jUpiOuVcAQN/1PlwtILZ29H4e9CNjPtGcTZUgJj48bfSgjJSBPxIoKY7L+yHpSmx27AfCiYw7R/U+wWHmQ0FCDgLOIv02jvKGNn+8H9pw//gNs9CnKZUSIOsAY5UeZLnOvp0C8TZormG4GA==" + } + }, + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCAic3ViamVjdCI6W3sibmFtZSI6ImNoYWlubG9vcC53b3JrZmxvdy5teXdmIiwgImRpZ2VzdCI6eyJzaGEyNTYiOiIyNjNjOTM1MDMxMGI5YzBiM2U0OGIwY2Y1ZDg0ZDM4NDAxZDBkZTAxMzMxM2RhOTcxZGE2NWViOWRiYzViODQ1In19LCB7Im5hbWUiOiJnaXQuaGVhZCIsICJkaWdlc3QiOnsic2hhMSI6ImJlYjBmOGMzYjRlNTYxOWIxM2Y2NDY5MTA2NDIwOTlhMTQzZDk1YTYifSwgImFubm90YXRpb25zIjp7ImF1dGhvci5lbWFpbCI6ImppcGFyaXNAY2hhaW5sb29wLmRldiIsICJhdXRob3IubmFtZSI6Ikpvc2UgSS4gUGFyaXMiLCAiZGF0ZSI6IjIwMjUtMDItMTJUMDk6MTg6MTZaIiwgIm1lc3NhZ2UiOiJNZXJnZSBicmFuY2ggJ21haW4nIGludG8gcGZtLTIyODMtdmVyaWYtY2hhaW5sb29wXG5cblNpZ25lZC1vZmYtYnk6IEpvc2UgSS4gUGFyaXMgPGppcGFyaXNAY2hhaW5sb29wLmRldj5cbiIsICJyZW1vdGVzIjpbeyJuYW1lIjoib3JpZ2luIiwgInVybCI6ImdpdEBnaXRodWIuY29tOmppcGFyaXMvY2hhaW5sb29wLmdpdCJ9LCB7Im5hbWUiOiJ1cHN0cmVhbSIsICJ1cmwiOiJnaXRAZ2l0aHViLmNvbTpjaGFpbmxvb3AtZGV2L2NoYWlubG9vcC5naXQifSwgeyJuYW1lIjoibWlndWUiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL21pZ21hcnRyaS9jaGFpbmxvb3AvIn0sIHsibmFtZSI6ImphdmkiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL2phdmlybG4vY2hhaW5sb29wIn1dLCAic2lnbmF0dXJlIjoiLS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS1cblxuaVFJeUJBQUJDQUFkRmlFRWpINTFITVdrQ05ZMmRoWndvSXR5Q2srbTN0NEZBbWVzWjFvQUNna1FvSXR5Q2srbVxuM3Q2K2lRLzRzdnhhUWpRU1NEUVVzNU9iZkJieGpZWkViTkgyemd0b0NYZ3U3QWdXcXNHbzVRUFRPblFEVm1VQ1xuajZrZnViejZ4WW9ZUUg4UjhTcEdPbWlJMTRiVUoweHZhTUZzdUp4WXVCdkVpZm9aQ2F0Mk1mM2RlUklYT2ZFRlxuSVdGdFlhb3B1blpSd05SOHNyYUZaaUtDcjNkVWJLbDZNeHdQek5JN2kxOEt5REcyUUsxcDdaUmxJK25ncGVaRVxuNU1XakM1UUFCTEhVMkJsMlpPSjFJa212RENTaXJKY3Arakw4eHk4TXJhOWQvSFVyNng2TlFDTmtYVEM5U2svNVxucHlPYWVBNVpaZExkaWgremc1cmNTL2RBYlJYMktueHdXVktJMzI2dWx0by9lN0tYVWZJNEwzRkNSQXNPWjJ0cVxuV2p0Y3JxWkhqRkVZOWdQVEU2TGppaU9uNkZMRXFmdk5ySHY1M0NNS0wxNnZLMTJEdUZrN3dMemU1eEpYMVRhdVxuQjc0dnFra21tcjVXSDBOeGhyS0p0UVhXZ291R2lJY1MyOFByV1F2QmtDdE90cDM2djRrUTBEOWdFOTVXcFVSNFxuRU9hTHB2WHpJdDBPR2IwMWRHZ2pkbm9kZFZRNVQvSDM1N3BwREtLZ254THhBWHFqRDRIejFmM0RoS1gvODZxOVxueGlLeWVZVXZDbGorOHkzQkFMcE4weitROWlNTjE4VFlRbnFsTE9kdG10ejM1TjdlQTdSSzRTc3l0eEsrNnVTZ1xuT2Z5SGQwZjd3T1AwUU1PczBiOTdyeWRvTFAxUU9kc1VjekhNQ2xkVy81aEZwbXp5SytWQ3B3Yi9pNGU1a0wyWVxuN293WFNOcUNRMTgyQWhoV3crZ1A0bzhhaVVCRytGcG41Y2FtNGhHaG1Ha0p4WDJrbEE9PVxuPXA3N1Fcbi0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLVxuIn19XSwgInByZWRpY2F0ZVR5cGUiOiJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjIiLCAicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6ImNoYWlubG9vcC5kZXYvd29ya2Zsb3dydW4vdjAuMSIsICJidWlsZGVyIjp7ImlkIjoiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1NjpkY2M0OGE5OGRlNjlkYTQwZDQ4NjgwYjBmMmM0YjQ1YWFhODNjYzY1YmIzMGE1MjBhYzk3MTRjYmZmZDFmZDdhIn0sICJtZXRhZGF0YSI6eyJjb250cmFjdE5hbWUiOiJteXByb2plY3QtbXl3ZiIsICJjb250cmFjdFZlcnNpb24iOiI5MyIsICJmaW5pc2hlZEF0IjoiMjAyNS0wMi0xMlQwOToxOToxMy43NzIyMTNaIiwgImluaXRpYWxpemVkQXQiOiIyMDI1LTAyLTEyVDA5OjE4OjUwLjU2ODEyWiIsICJuYW1lIjoibXl3ZiIsICJvcmdhbml6YXRpb24iOiJteS1vcmciLCAicHJvamVjdCI6Im15cHJvamVjdCIsICJwcm9qZWN0VmVyc2lvbiI6InYwLjE2Mi4wIiwgInByb2plY3RWZXJzaW9uUHJlcmVsZWFzZSI6dHJ1ZSwgInRlYW0iOiIiLCAid29ya2Zsb3dJRCI6Ijk1ZjFiNTk3LTIwZTEtNDk4Yy1iOTMxLWZjNmQ3MzMxODYwZSIsICJ3b3JrZmxvd1J1bklEIjoiNWZlZDk5ZWUtYzQ4ZS00NzVlLWFiOWQtMjk1NGMxZjliNDk1In0sICJwb2xpY3lBdHRCbG9ja2VkIjpmYWxzZSwgInBvbGljeUJsb2NrQnlwYXNzRW5hYmxlZCI6ZmFsc2UsICJwb2xpY3lDaGVja0Jsb2NraW5nU3RyYXRlZ3kiOiJBRFZJU09SWSIsICJwb2xpY3lFdmFsdWF0aW9ucyI6eyJDSEFJTkxPT1AuQVRURVNUQVRJT04iOlt7ImRlc2NyaXB0aW9uIjoiVmVyaWZpZXMgdGhhdCB0aGUgYXR0ZXN0YXRpb24gZXhwbGljaXRseSByZWZlcmVuY2VzIGEgc3BlY2lmaWMgR2l0IGNvbW1pdCIsICJuYW1lIjoic291cmNlLWNvbW1pdCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNvdXJjZS1jb21taXQiLCAib3JnYW5pemF0aW9uIjoiIn0sICJkaWdlc3QiOnsic2hhMjU2IjoiMTY0YmMzMjJiZjM1MGQ2YmI0NTc2OGM4NmRiMTgwZjUzMTUwODVjNzkxYWRlYmFmMDk0OTBmOWNjZTk5NTI4YyJ9LCAibmFtZSI6InNvdXJjZS1jb21taXQiLCAidXJpIjoiY2hhaW5sb29wOi8vbG9jYWxob3N0OjgwMDIvc291cmNlLWNvbW1pdCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9jb21taXQtc2lnbmVkIl0sICJza2lwcGVkIjpmYWxzZSwgInR5cGUiOiJBVFRFU1RBVElPTiIsICJ3aXRoIjp7ImNoZWNrX3NpZ25hdHVyZSI6InRydWUifX0sIHsiYW5ub3RhdGlvbnMiOnsiY2F0ZWdvcnkiOiJzYm9tIn0sICJkZXNjcmlwdGlvbiI6IkNoZWNrcyB0aGF0IGVpdGhlciBhIFNQRFggb3IgQ3ljbG9uZURYIFNCT00gbWF0ZXJpYWwgaXMgcHJlc2VudCBpbiB0aGUgYXR0ZXN0YXRpb24iLCAiZ3JvdXBSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcXVhbGl0eSIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiI3MDM3Y2Q2ZTk0YWY1Y2I4MDAyZGZjZWZjNzJlYjhkMDMyM2RmMDZjNTI2YzQxOGQzMTVmMGRhYWNjOWRjYzI2In0sICJuYW1lIjoic2JvbS1xdWFsaXR5IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcXVhbGl0eSJ9LCAibmFtZSI6InNib20tcHJlc2VudCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcHJlc2VudCIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiJlMmRhMDdjNGRhYjc2YTU4YjQ3NGM2OGEzYzVhNTQzM2JmNGEyOGVkODgxZDRjZWFhMTZlOTE3MTYxNjg0ZWEyIn0sICJuYW1lIjoic2JvbS1wcmVzZW50IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcHJlc2VudCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9zYm9tLXF1YWxpdHkiXSwgInNraXBwZWQiOmZhbHNlLCAidHlwZSI6IkFUVEVTVEFUSU9OIiwgInZpb2xhdGlvbnMiOlt7Im1lc3NhZ2UiOiJtaXNzaW5nIFNCT00gbWF0ZXJpYWwiLCAic3ViamVjdCI6InNib20tcHJlc2VudCJ9XX1dfSwgInBvbGljeUhhc1Zpb2xhdGlvbnMiOnRydWUsICJydW5uZXJUeXBlIjoiUlVOTkVSX1RZUEVfVU5TUEVDSUZJRUQifX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "TUVVQ0lRQ1JjOFVmeFVSMnBkL2UxVW1pVmhkRE5BZUxVRi83ODZ2WGxCT1VWM0dJcUFJZ0NqOTROczZwbzFQSzJjTW95MzBpOVB3Smx5c1E0R1RmTGI4TnZ5WlB1ckk9" + } + ] + } +} \ No newline at end of file diff --git a/app/cli/pkg/action/testdata/bundle_valid_nomaterial.json b/app/cli/pkg/action/testdata/bundle_valid_nomaterial.json new file mode 100644 index 000000000..4dffea464 --- /dev/null +++ b/app/cli/pkg/action/testdata/bundle_valid_nomaterial.json @@ -0,0 +1,12 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.3", + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCAic3ViamVjdCI6W3sibmFtZSI6ImNoYWlubG9vcC53b3JrZmxvdy5teXdmIiwgImRpZ2VzdCI6eyJzaGEyNTYiOiIyNjNjOTM1MDMxMGI5YzBiM2U0OGIwY2Y1ZDg0ZDM4NDAxZDBkZTAxMzMxM2RhOTcxZGE2NWViOWRiYzViODQ1In19LCB7Im5hbWUiOiJnaXQuaGVhZCIsICJkaWdlc3QiOnsic2hhMSI6ImJlYjBmOGMzYjRlNTYxOWIxM2Y2NDY5MTA2NDIwOTlhMTQzZDk1YTYifSwgImFubm90YXRpb25zIjp7ImF1dGhvci5lbWFpbCI6ImppcGFyaXNAY2hhaW5sb29wLmRldiIsICJhdXRob3IubmFtZSI6Ikpvc2UgSS4gUGFyaXMiLCAiZGF0ZSI6IjIwMjUtMDItMTJUMDk6MTg6MTZaIiwgIm1lc3NhZ2UiOiJNZXJnZSBicmFuY2ggJ21haW4nIGludG8gcGZtLTIyODMtdmVyaWYtY2hhaW5sb29wXG5cblNpZ25lZC1vZmYtYnk6IEpvc2UgSS4gUGFyaXMgPGppcGFyaXNAY2hhaW5sb29wLmRldj5cbiIsICJyZW1vdGVzIjpbeyJuYW1lIjoib3JpZ2luIiwgInVybCI6ImdpdEBnaXRodWIuY29tOmppcGFyaXMvY2hhaW5sb29wLmdpdCJ9LCB7Im5hbWUiOiJ1cHN0cmVhbSIsICJ1cmwiOiJnaXRAZ2l0aHViLmNvbTpjaGFpbmxvb3AtZGV2L2NoYWlubG9vcC5naXQifSwgeyJuYW1lIjoibWlndWUiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL21pZ21hcnRyaS9jaGFpbmxvb3AvIn0sIHsibmFtZSI6ImphdmkiLCAidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL2phdmlybG4vY2hhaW5sb29wIn1dLCAic2lnbmF0dXJlIjoiLS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS1cblxuaVFJeUJBQUJDQUFkRmlFRWpINTFITVdrQ05ZMmRoWndvSXR5Q2srbTN0NEZBbWVzWjFvQUNna1FvSXR5Q2srbVxuM3Q2K2lRLzRzdnhhUWpRU1NEUVVzNU9iZkJieGpZWkViTkgyemd0b0NYZ3U3QWdXcXNHbzVRUFRPblFEVm1VQ1xuajZrZnViejZ4WW9ZUUg4UjhTcEdPbWlJMTRiVUoweHZhTUZzdUp4WXVCdkVpZm9aQ2F0Mk1mM2RlUklYT2ZFRlxuSVdGdFlhb3B1blpSd05SOHNyYUZaaUtDcjNkVWJLbDZNeHdQek5JN2kxOEt5REcyUUsxcDdaUmxJK25ncGVaRVxuNU1XakM1UUFCTEhVMkJsMlpPSjFJa212RENTaXJKY3Arakw4eHk4TXJhOWQvSFVyNng2TlFDTmtYVEM5U2svNVxucHlPYWVBNVpaZExkaWgremc1cmNTL2RBYlJYMktueHdXVktJMzI2dWx0by9lN0tYVWZJNEwzRkNSQXNPWjJ0cVxuV2p0Y3JxWkhqRkVZOWdQVEU2TGppaU9uNkZMRXFmdk5ySHY1M0NNS0wxNnZLMTJEdUZrN3dMemU1eEpYMVRhdVxuQjc0dnFra21tcjVXSDBOeGhyS0p0UVhXZ291R2lJY1MyOFByV1F2QmtDdE90cDM2djRrUTBEOWdFOTVXcFVSNFxuRU9hTHB2WHpJdDBPR2IwMWRHZ2pkbm9kZFZRNVQvSDM1N3BwREtLZ254THhBWHFqRDRIejFmM0RoS1gvODZxOVxueGlLeWVZVXZDbGorOHkzQkFMcE4weitROWlNTjE4VFlRbnFsTE9kdG10ejM1TjdlQTdSSzRTc3l0eEsrNnVTZ1xuT2Z5SGQwZjd3T1AwUU1PczBiOTdyeWRvTFAxUU9kc1VjekhNQ2xkVy81aEZwbXp5SytWQ3B3Yi9pNGU1a0wyWVxuN293WFNOcUNRMTgyQWhoV3crZ1A0bzhhaVVCRytGcG41Y2FtNGhHaG1Ha0p4WDJrbEE9PVxuPXA3N1Fcbi0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLVxuIn19XSwgInByZWRpY2F0ZVR5cGUiOiJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjIiLCAicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6ImNoYWlubG9vcC5kZXYvd29ya2Zsb3dydW4vdjAuMSIsICJidWlsZGVyIjp7ImlkIjoiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1NjpkY2M0OGE5OGRlNjlkYTQwZDQ4NjgwYjBmMmM0YjQ1YWFhODNjYzY1YmIzMGE1MjBhYzk3MTRjYmZmZDFmZDdhIn0sICJtZXRhZGF0YSI6eyJjb250cmFjdE5hbWUiOiJteXByb2plY3QtbXl3ZiIsICJjb250cmFjdFZlcnNpb24iOiI5MyIsICJmaW5pc2hlZEF0IjoiMjAyNS0wMi0xMlQwOToxOToxMy43NzIyMTNaIiwgImluaXRpYWxpemVkQXQiOiIyMDI1LTAyLTEyVDA5OjE4OjUwLjU2ODEyWiIsICJuYW1lIjoibXl3ZiIsICJvcmdhbml6YXRpb24iOiJteS1vcmciLCAicHJvamVjdCI6Im15cHJvamVjdCIsICJwcm9qZWN0VmVyc2lvbiI6InYwLjE2Mi4wIiwgInByb2plY3RWZXJzaW9uUHJlcmVsZWFzZSI6dHJ1ZSwgInRlYW0iOiIiLCAid29ya2Zsb3dJRCI6Ijk1ZjFiNTk3LTIwZTEtNDk4Yy1iOTMxLWZjNmQ3MzMxODYwZSIsICJ3b3JrZmxvd1J1bklEIjoiNWZlZDk5ZWUtYzQ4ZS00NzVlLWFiOWQtMjk1NGMxZjliNDk1In0sICJwb2xpY3lBdHRCbG9ja2VkIjpmYWxzZSwgInBvbGljeUJsb2NrQnlwYXNzRW5hYmxlZCI6ZmFsc2UsICJwb2xpY3lDaGVja0Jsb2NraW5nU3RyYXRlZ3kiOiJBRFZJU09SWSIsICJwb2xpY3lFdmFsdWF0aW9ucyI6eyJDSEFJTkxPT1AuQVRURVNUQVRJT04iOlt7ImRlc2NyaXB0aW9uIjoiVmVyaWZpZXMgdGhhdCB0aGUgYXR0ZXN0YXRpb24gZXhwbGljaXRseSByZWZlcmVuY2VzIGEgc3BlY2lmaWMgR2l0IGNvbW1pdCIsICJuYW1lIjoic291cmNlLWNvbW1pdCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNvdXJjZS1jb21taXQiLCAib3JnYW5pemF0aW9uIjoiIn0sICJkaWdlc3QiOnsic2hhMjU2IjoiMTY0YmMzMjJiZjM1MGQ2YmI0NTc2OGM4NmRiMTgwZjUzMTUwODVjNzkxYWRlYmFmMDk0OTBmOWNjZTk5NTI4YyJ9LCAibmFtZSI6InNvdXJjZS1jb21taXQiLCAidXJpIjoiY2hhaW5sb29wOi8vbG9jYWxob3N0OjgwMDIvc291cmNlLWNvbW1pdCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9jb21taXQtc2lnbmVkIl0sICJza2lwcGVkIjpmYWxzZSwgInR5cGUiOiJBVFRFU1RBVElPTiIsICJ3aXRoIjp7ImNoZWNrX3NpZ25hdHVyZSI6InRydWUifX0sIHsiYW5ub3RhdGlvbnMiOnsiY2F0ZWdvcnkiOiJzYm9tIn0sICJkZXNjcmlwdGlvbiI6IkNoZWNrcyB0aGF0IGVpdGhlciBhIFNQRFggb3IgQ3ljbG9uZURYIFNCT00gbWF0ZXJpYWwgaXMgcHJlc2VudCBpbiB0aGUgYXR0ZXN0YXRpb24iLCAiZ3JvdXBSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcXVhbGl0eSIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiI3MDM3Y2Q2ZTk0YWY1Y2I4MDAyZGZjZWZjNzJlYjhkMDMyM2RmMDZjNTI2YzQxOGQzMTVmMGRhYWNjOWRjYzI2In0sICJuYW1lIjoic2JvbS1xdWFsaXR5IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcXVhbGl0eSJ9LCAibmFtZSI6InNib20tcHJlc2VudCIsICJwb2xpY3lSZWZlcmVuY2UiOnsiYW5ub3RhdGlvbnMiOnsibmFtZSI6InNib20tcHJlc2VudCIsICJvcmdhbml6YXRpb24iOiIifSwgImRpZ2VzdCI6eyJzaGEyNTYiOiJlMmRhMDdjNGRhYjc2YTU4YjQ3NGM2OGEzYzVhNTQzM2JmNGEyOGVkODgxZDRjZWFhMTZlOTE3MTYxNjg0ZWEyIn0sICJuYW1lIjoic2JvbS1wcmVzZW50IiwgInVyaSI6ImNoYWlubG9vcDovL2xvY2FsaG9zdDo4MDAyL3Nib20tcHJlc2VudCJ9LCAicmVxdWlyZW1lbnRzIjpbImNoYWlubG9vcC1iZXN0LXByYWN0aWNlcy9zYm9tLXF1YWxpdHkiXSwgInNraXBwZWQiOmZhbHNlLCAidHlwZSI6IkFUVEVTVEFUSU9OIiwgInZpb2xhdGlvbnMiOlt7Im1lc3NhZ2UiOiJtaXNzaW5nIFNCT00gbWF0ZXJpYWwiLCAic3ViamVjdCI6InNib20tcHJlc2VudCJ9XX1dfSwgInBvbGljeUhhc1Zpb2xhdGlvbnMiOnRydWUsICJydW5uZXJUeXBlIjoiUlVOTkVSX1RZUEVfVU5TUEVDSUZJRUQifX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "TUVVQ0lRQ1JjOFVmeFVSMnBkL2UxVW1pVmhkRE5BZUxVRi83ODZ2WGxCT1VWM0dJcUFJZ0NqOTROczZwbzFQSzJjTW95MzBpOVB3Smx5c1E0R1RmTGI4TnZ5WlB1ckk9" + } + ] + } +} \ No newline at end of file