diff options
Diffstat (limited to 'go/internal/command')
-rw-r--r-- | go/internal/command/command.go | 16 | ||||
-rw-r--r-- | go/internal/command/command_test.go | 188 | ||||
-rw-r--r-- | go/internal/command/fallback/fallback.go | 57 | ||||
-rw-r--r-- | go/internal/command/fallback/fallback_test.go | 84 |
4 files changed, 70 insertions, 275 deletions
diff --git a/go/internal/command/command.go b/go/internal/command/command.go index 071cd1d..40d92e0 100644 --- a/go/internal/command/command.go +++ b/go/internal/command/command.go @@ -5,10 +5,10 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedprincipals" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover" - "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/fallback" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/lfsauthenticate" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/disallowedcommand" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadarchive" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadpack" @@ -30,7 +30,7 @@ func New(e *executable.Executable, arguments []string, config *config.Config, re return cmd, nil } - return &fallback.Command{Executable: e, RootDir: config.RootDir, Args: args}, nil + return nil, disallowedcommand.Error } func buildCommand(e *executable.Executable, args commandargs.CommandArgs, config *config.Config, readWriter *readwriter.ReadWriter) Command { @@ -47,10 +47,6 @@ func buildCommand(e *executable.Executable, args commandargs.CommandArgs, config } func buildShellCommand(args *commandargs.Shell, config *config.Config, readWriter *readwriter.ReadWriter) Command { - if !config.FeatureEnabled(string(args.CommandType)) { - return nil - } - switch args.CommandType { case commandargs.Discover: return &discover.Command{Config: config, Args: args, ReadWriter: readWriter} @@ -70,17 +66,9 @@ func buildShellCommand(args *commandargs.Shell, config *config.Config, readWrite } func buildAuthorizedKeysCommand(args *commandargs.AuthorizedKeys, config *config.Config, readWriter *readwriter.ReadWriter) Command { - if !config.FeatureEnabled(executable.AuthorizedKeysCheck) { - return nil - } - return &authorizedkeys.Command{Config: config, Args: args, ReadWriter: readWriter} } func buildAuthorizedPrincipalsCommand(args *commandargs.AuthorizedPrincipals, config *config.Config, readWriter *readwriter.ReadWriter) Command { - if !config.FeatureEnabled(executable.AuthorizedPrincipalsCheck) { - return nil - } - return &authorizedprincipals.Command{Config: config, Args: args, ReadWriter: readWriter} } diff --git a/go/internal/command/command_test.go b/go/internal/command/command_test.go index e7e37d8..59f22e2 100644 --- a/go/internal/command/command_test.go +++ b/go/internal/command/command_test.go @@ -1,6 +1,7 @@ package command import ( + "errors" "testing" "github.com/stretchr/testify/require" @@ -8,9 +9,9 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedkeys" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/authorizedprincipals" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover" - "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/fallback" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/lfsauthenticate" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/disallowedcommand" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadarchive" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadpack" @@ -19,154 +20,77 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper" ) +var ( + authorizedKeysExec = &executable.Executable{Name: executable.AuthorizedKeysCheck} + authorizedPrincipalsExec = &executable.Executable{Name: executable.AuthorizedPrincipalsCheck} + gitlabShellExec = &executable.Executable{Name: executable.GitlabShell} + + basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} +) + +func buildEnv(command string) map[string]string { + return map[string]string{ + "SSH_CONNECTION": "1", + "SSH_ORIGINAL_COMMAND": command, + } +} + func TestNew(t *testing.T) { testCases := []struct { desc string executable *executable.Executable - config *config.Config environment map[string]string arguments []string expectedType interface{} }{ { - desc: "it returns a Discover command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"discover"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "", - }, - arguments: []string{}, + desc: "it returns a Discover command", + executable: gitlabShellExec, + environment: buildEnv(""), expectedType: &discover.Command{}, }, { - desc: "it returns a Fallback command no feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: false}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "", - }, - arguments: []string{}, - expectedType: &fallback.Command{}, - }, - { - desc: "it returns a TwoFactorRecover command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"2fa_recovery_codes"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "2fa_recovery_codes", - }, - arguments: []string{}, + desc: "it returns a TwoFactorRecover command", + executable: gitlabShellExec, + environment: buildEnv("2fa_recovery_codes"), expectedType: &twofactorrecover.Command{}, }, { - desc: "it returns an LfsAuthenticate command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-lfs-authenticate"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "git-lfs-authenticate", - }, - arguments: []string{}, + desc: "it returns an LfsAuthenticate command", + executable: gitlabShellExec, + environment: buildEnv("git-lfs-authenticate"), expectedType: &lfsauthenticate.Command{}, }, { - desc: "it returns a ReceivePack command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-receive-pack"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "git-receive-pack", - }, - arguments: []string{}, + desc: "it returns a ReceivePack command", + executable: gitlabShellExec, + environment: buildEnv("git-receive-pack"), expectedType: &receivepack.Command{}, }, { - desc: "it returns an UploadPack command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-upload-pack"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "git-upload-pack", - }, - arguments: []string{}, + desc: "it returns an UploadPack command", + executable: gitlabShellExec, + environment: buildEnv("git-upload-pack"), expectedType: &uploadpack.Command{}, }, { - desc: "it returns an UploadArchive command if the feature is enabled", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-upload-archive"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "git-upload-archive", - }, - arguments: []string{}, + desc: "it returns an UploadArchive command", + executable: gitlabShellExec, + environment: buildEnv("git-upload-archive"), expectedType: &uploadarchive.Command{}, }, { - desc: "it returns a AuthorizedKeys command if the feature is enabled", - executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, - config: &config.Config{ - Migration: config.MigrationConfig{Enabled: true, Features: []string{"gitlab-shell-authorized-keys-check"}}, - }, - environment: map[string]string{}, + desc: "it returns a AuthorizedKeys command", + executable: authorizedKeysExec, arguments: []string{"git", "git", "key"}, expectedType: &authorizedkeys.Command{}, }, { - desc: "it returns a AuthorizedPrincipals command if the feature is enabled", - executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck}, - config: &config.Config{ - Migration: config.MigrationConfig{Enabled: true, Features: []string{"gitlab-shell-authorized-principals-check"}}, - }, - environment: map[string]string{}, + desc: "it returns a AuthorizedPrincipals command", + executable: authorizedPrincipalsExec, arguments: []string{"key", "principal"}, expectedType: &authorizedprincipals.Command{}, }, - { - desc: "it returns a Fallback command if the feature is unimplemented", - executable: &executable.Executable{Name: executable.GitlabShell}, - config: &config.Config{ - GitlabUrl: "http+unix://gitlab.socket", - Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-unimplemented-feature"}}, - }, - environment: map[string]string{ - "SSH_CONNECTION": "1", - "SSH_ORIGINAL_COMMAND": "git-unimplemented-feature", - }, - arguments: []string{}, - expectedType: &fallback.Command{}, - }, - { - desc: "it returns a Fallback command if executable is unknown", - executable: &executable.Executable{Name: "unknown"}, - config: &config.Config{}, - arguments: []string{}, - expectedType: &fallback.Command{}, - }, } for _, tc := range testCases { @@ -174,7 +98,7 @@ func TestNew(t *testing.T) { restoreEnv := testhelper.TempEnv(tc.environment) defer restoreEnv() - command, err := New(tc.executable, tc.arguments, tc.config, nil) + command, err := New(tc.executable, tc.arguments, basicConfig, nil) require.NoError(t, err) require.IsType(t, tc.expectedType, command) @@ -183,9 +107,33 @@ func TestNew(t *testing.T) { } func TestFailingNew(t *testing.T) { - t.Run("It returns an error parsing arguments failed", func(t *testing.T) { - _, err := New(&executable.Executable{Name: executable.GitlabShell}, []string{}, &config.Config{}, nil) + testCases := []struct { + desc string + executable *executable.Executable + environment map[string]string + expectedError error + }{ + { + desc: "Parsing environment failed", + executable: gitlabShellExec, + expectedError: errors.New("Only SSH allowed"), + }, + { + desc: "Unknown command given", + executable: gitlabShellExec, + environment: buildEnv("unknown"), + expectedError: disallowedcommand.Error, + }, + } - require.Error(t, err) - }) + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + restoreEnv := testhelper.TempEnv(tc.environment) + defer restoreEnv() + + command, err := New(tc.executable, []string{}, basicConfig, nil) + require.Nil(t, command) + require.Equal(t, tc.expectedError, err) + }) + } } diff --git a/go/internal/command/fallback/fallback.go b/go/internal/command/fallback/fallback.go deleted file mode 100644 index 781eda1..0000000 --- a/go/internal/command/fallback/fallback.go +++ /dev/null @@ -1,57 +0,0 @@ -package fallback - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "syscall" - - "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" - "gitlab.com/gitlab-org/gitlab-shell/go/internal/executable" -) - -type Command struct { - Executable *executable.Executable - RootDir string - Args commandargs.CommandArgs -} - -var ( - // execFunc is overridden in tests - execFunc = syscall.Exec - whitelist = []string{ - executable.GitlabShell, - executable.AuthorizedKeysCheck, - executable.AuthorizedPrincipalsCheck, - } -) - -func (c *Command) Execute() error { - if !c.isWhitelisted() { - return errors.New("Failed to execute unknown executable") - } - - rubyCmd := c.fallbackProgram() - - // Ensure rubyArgs[0] is the full path to gitlab-shell-ruby - rubyArgs := append([]string{rubyCmd}, c.Args.GetArguments()...) - - return execFunc(rubyCmd, rubyArgs, os.Environ()) -} - -func (c *Command) isWhitelisted() bool { - for _, item := range whitelist { - if c.Executable.Name == item { - return true - } - } - - return false -} - -func (c *Command) fallbackProgram() string { - fileName := fmt.Sprintf("%s-ruby", c.Executable.Name) - - return filepath.Join(c.RootDir, "bin", fileName) -} diff --git a/go/internal/command/fallback/fallback_test.go b/go/internal/command/fallback/fallback_test.go deleted file mode 100644 index 7485084..0000000 --- a/go/internal/command/fallback/fallback_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package fallback - -import ( - "errors" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" - "gitlab.com/gitlab-org/gitlab-shell/go/internal/executable" -) - -type fakeExec struct { - OldExec func(string, []string, []string) error - Error error - Called bool - - Filename string - Args []string - Env []string -} - -var ( - fakeArgs = &commandargs.GenericArgs{Arguments: []string{"foo", "bar"}} -) - -func (f *fakeExec) Exec(filename string, args []string, env []string) error { - f.Called = true - - f.Filename = filename - f.Args = args - f.Env = env - - return f.Error -} - -func (f *fakeExec) Setup() { - f.OldExec = execFunc - execFunc = f.Exec -} - -func (f *fakeExec) Cleanup() { - execFunc = f.OldExec -} - -func TestExecuteExecsCommandSuccesfully(t *testing.T) { - cmd := &Command{Executable: &executable.Executable{Name: executable.GitlabShell}, RootDir: "/tmp", Args: fakeArgs} - - // Override the exec func - fake := &fakeExec{} - fake.Setup() - defer fake.Cleanup() - - require.NoError(t, cmd.Execute()) - require.True(t, fake.Called) - require.Equal(t, fake.Filename, "/tmp/bin/gitlab-shell-ruby") - require.Equal(t, fake.Args, []string{"/tmp/bin/gitlab-shell-ruby", "foo", "bar"}) - require.Equal(t, fake.Env, os.Environ()) -} - -func TestExecuteExecsUnknownExecutable(t *testing.T) { - cmd := &Command{Executable: &executable.Executable{Name: "unknown"}, RootDir: "/test"} - - require.Error(t, cmd.Execute()) -} - -func TestExecuteExecsCommandOnError(t *testing.T) { - cmd := &Command{Executable: &executable.Executable{Name: executable.GitlabShell}, RootDir: "/test", Args: fakeArgs} - - // Override the exec func - fake := &fakeExec{Error: errors.New("Test error")} - fake.Setup() - defer fake.Cleanup() - - require.Error(t, cmd.Execute()) - require.True(t, fake.Called) -} - -func TestExecuteGivenNonexistentCommand(t *testing.T) { - cmd := &Command{Executable: &executable.Executable{Name: executable.GitlabShell}, RootDir: "/tmp/does/not/exist", Args: fakeArgs} - - require.Error(t, cmd.Execute()) -} |