package trust import ( "fmt" "io/ioutil" "os" "path/filepath" "runtime" "testing" "github.com/docker/cli/cli/config" "github.com/docker/cli/internal/test" notaryfake "github.com/docker/cli/internal/test/notary" "github.com/theupdateframework/notary" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestTrustSignerAddErrors(t *testing.T) { testCases := []struct { name string args []string expectedError string }{ { name: "not-enough-args", expectedError: "requires at least 2 argument", }, { name: "no-key", args: []string{"foo", "bar"}, expectedError: "path to a public key must be provided using the `--key` flag", }, { name: "reserved-releases-signer-add", args: []string{"releases", "my-image", "--key", "/path/to/key"}, expectedError: "releases is a reserved keyword, please use a different signer name", }, { name: "disallowed-chars", args: []string{"ali/ce", "my-image", "--key", "/path/to/key"}, expectedError: "signer name \"ali/ce\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", }, { name: "no-upper-case", args: []string{"Alice", "my-image", "--key", "/path/to/key"}, expectedError: "signer name \"Alice\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", }, { name: "start-with-letter", args: []string{"_alice", "my-image", "--key", "/path/to/key"}, expectedError: "signer name \"_alice\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", }, } tmpDir, err := ioutil.TempDir("", "docker-sign-test-") assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) for _, tc := range testCases { cli := test.NewFakeCli(&fakeClient{}) cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs(tc.args) cmd.SetOut(ioutil.Discard) assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestSignerAddCommandNoTargetsKey(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) tmpfile, err := ioutil.TempFile("", "pemfile") assert.NilError(t, err) defer os.Remove(tmpfile.Name()) cli := test.NewFakeCli(&fakeClient{}) cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs([]string{"--key", tmpfile.Name(), "alice", "alpine", "linuxkit/alpine"}) cmd.SetOut(ioutil.Discard) assert.Error(t, cmd.Execute(), fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) } func TestSignerAddCommandBadKeyPath(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) cli := test.NewFakeCli(&fakeClient{}) cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs([]string{"--key", "/path/to/key.pem", "alice", "alpine"}) cmd.SetOut(ioutil.Discard) expectedError := "unable to read public key from file: open /path/to/key.pem: no such file or directory" if runtime.GOOS == "windows" { expectedError = "unable to read public key from file: open /path/to/key.pem: The system cannot find the path specified." } assert.Error(t, cmd.Execute(), expectedError) } func TestSignerAddCommandInvalidRepoName(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) pubKeyDir, err := ioutil.TempDir("", "key-load-test-pubkey-") assert.NilError(t, err) defer os.RemoveAll(pubKeyDir) pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") assert.NilError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) cli := test.NewFakeCli(&fakeClient{}) cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) cmd := newSignerAddCommand(cli) imageName := "870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd" cmd.SetArgs([]string{"--key", pubKeyFilepath, "alice", imageName}) cmd.SetOut(ioutil.Discard) assert.Error(t, cmd.Execute(), "Failed to add signer to: 870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd") expectedErr := fmt.Sprintf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings\n\n", imageName) assert.Check(t, is.Equal(expectedErr, cli.ErrBuffer().String())) } func TestIngestPublicKeys(t *testing.T) { // Call with a bad path _, err := ingestPublicKeys([]string{"foo", "bar"}) expectedError := "unable to read public key from file: open foo: no such file or directory" if runtime.GOOS == "windows" { expectedError = "unable to read public key from file: open foo: The system cannot find the file specified." } assert.Error(t, err, expectedError) // Call with real file path tmpfile, err := ioutil.TempFile("", "pemfile") assert.NilError(t, err) defer os.Remove(tmpfile.Name()) _, err = ingestPublicKeys([]string{tmpfile.Name()}) assert.Error(t, err, fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) }