1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
//go:build debian_no_fulcio
// +build debian_no_fulcio
package copy
import (
"context"
"errors"
"io"
"testing"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/imagedestination"
internalsig "github.com/containers/image/v5/internal/signature"
internalSigner "github.com/containers/image/v5/internal/signer"
"github.com/containers/image/v5/signature/signer"
"github.com/containers/image/v5/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// stubSignerImpl is a signer.SigningImplementation that allows us to check the signed identity, without the overhead of actually signing.
// We abuse internalsig.Sigstore to store the signed manifest and identity in the payload and MIME type fields, respectively.
type stubSignerImpl struct {
signingFailure error // if set, SignImageManifest returns this
}
func (s *stubSignerImpl) ProgressMessage() string {
return "Signing with stubSigner"
}
func (s *stubSignerImpl) SignImageManifest(ctx context.Context, m []byte, dockerReference reference.Named) (internalsig.Signature, error) {
if s.signingFailure != nil {
return nil, s.signingFailure
}
return internalsig.SigstoreFromComponents(dockerReference.String(), m, nil), nil
}
func (s *stubSignerImpl) Close() error {
return nil
}
func TestCreateSignatures(t *testing.T) {
stubSigner := internalSigner.NewSigner(&stubSignerImpl{})
defer stubSigner.Close()
manifestBlob := []byte("Something")
// Set up dir: and docker: destinations
tempDir := t.TempDir()
dirRef, err := directory.NewReference(tempDir)
require.NoError(t, err)
dirDest, err := dirRef.NewImageDestination(context.Background(), nil)
require.NoError(t, err)
defer dirDest.Close()
dockerRef, err := docker.ParseReference("//busybox")
require.NoError(t, err)
dockerDest, err := dockerRef.NewImageDestination(context.Background(),
&types.SystemContext{RegistriesDirPath: "/this/does/not/exist", DockerPerHostCertDirPath: "/this/does/not/exist"})
require.NoError(t, err)
defer dockerDest.Close()
workingOptions := Options{Signers: []*signer.Signer{stubSigner}}
for _, cc := range []struct {
name string
dest types.ImageDestination
options *Options
identity string
successWithNoSigs bool
successfullySignedIdentity string // Set to expect a successful signing with workingOptions
}{
{
name: "signing fails",
dest: dockerDest,
options: &Options{
Signers: []*signer.Signer{
internalSigner.NewSigner(&stubSignerImpl{signingFailure: errors.New("fails")}),
},
},
},
{
name: "second signing fails",
dest: dockerDest,
options: &Options{
Signers: []*signer.Signer{
stubSigner,
internalSigner.NewSigner(&stubSignerImpl{signingFailure: errors.New("fails")}),
},
},
},
{
name: "not a full reference",
dest: dockerDest,
identity: "myregistry.io/myrepo",
},
{
name: "dir: with no identity specified, but no signing request",
dest: dirDest,
options: &Options{},
successWithNoSigs: true,
},
{
name: "dir: with no identity specified",
dest: dirDest,
identity: "",
},
{
name: "dir: with overridden identity",
dest: dirDest,
identity: "myregistry.io/myrepo:mytag",
successfullySignedIdentity: "myregistry.io/myrepo:mytag",
},
{
name: "docker:// without overriding the identity",
dest: dockerDest,
identity: "",
successfullySignedIdentity: "docker.io/library/busybox:latest",
},
{
name: "docker:// with overidden identity",
dest: dockerDest,
identity: "myregistry.io/myrepo:mytag",
successfullySignedIdentity: "myregistry.io/myrepo:mytag",
},
} {
var identity reference.Named = nil
if cc.identity != "" {
i, err := reference.ParseNormalizedNamed(cc.identity)
require.NoError(t, err, cc.name)
identity = i
}
options := cc.options
if options == nil {
options = &workingOptions
}
c := &copier{
dest: imagedestination.FromPublic(cc.dest),
options: options,
reportWriter: io.Discard,
}
defer c.close()
err := c.setupSigners()
require.NoError(t, err, cc.name)
sigs, err := c.createSignatures(context.Background(), manifestBlob, identity)
switch {
case cc.successfullySignedIdentity != "":
require.NoError(t, err, cc.name)
require.Len(t, sigs, 1, cc.name)
stubSig, ok := sigs[0].(internalsig.Sigstore)
require.True(t, ok, cc.name)
// Compare how stubSignerImpl.SignImageManifest stuffs the signing parameters into these fields.
assert.Equal(t, manifestBlob, stubSig.UntrustedPayload(), cc.name)
assert.Equal(t, cc.successfullySignedIdentity, stubSig.UntrustedMIMEType(), cc.name)
case cc.successWithNoSigs:
require.NoError(t, err, cc.name)
require.Empty(t, sigs, cc.name)
default:
assert.Error(t, err, cc.name)
}
}
}
|