diff --git a/app/command.go b/app/command.go index 8f65843e2e..a44f2161ff 100644 --- a/app/command.go +++ b/app/command.go @@ -10,6 +10,7 @@ import ( "net/url" "strings" "sync" + "unicode" goi18n "github.com/mattermost/go-i18n/i18n" "github.com/mattermost/mattermost-server/v5/mlog" @@ -159,10 +160,20 @@ func (a *App) ListAllCommands(teamId string, T goi18n.TranslateFunc) ([]*model.C // @openTracingParams args func (a *App) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { - parts := strings.Split(args.Command, " ") - trigger := parts[0][1:] + trigger := "" + message := "" + index := strings.IndexFunc(args.Command, unicode.IsSpace) + if index != -1 { + trigger = args.Command[:index] + message = args.Command[index+1:] + } else { + trigger = args.Command + } trigger = strings.ToLower(trigger) - message := strings.Join(parts[1:], " ") + if !strings.HasPrefix(trigger, "/") { + return nil, model.NewAppError("command", "api.command.execute_command.format.app_error", map[string]interface{}{"Trigger": trigger}, "", http.StatusBadRequest) + } + trigger = strings.TrimPrefix(trigger, "/") clientTriggerId, triggerId, appErr := model.GenerateTriggerId(args.UserId, a.AsymmetricSigningKey()) if appErr != nil { diff --git a/app/command_test.go b/app/command_test.go index eae7bae5e6..18d3d6f745 100644 --- a/app/command_test.go +++ b/app/command_test.go @@ -75,6 +75,49 @@ func TestCreateCommandPost(t *testing.T) { require.Equal(t, err.Id, "api.context.invalid_param.app_error") } +func TestExecuteCommand(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + t.Run("valid tests with different whitespace characters", func(t *testing.T) { + TestCases := map[string]string{ + "/code happy path": " happy path", + "/code\nnewline path": " newline path", + "/code\n/nDouble newline path": " /nDouble newline path", + "/code double space": " double space", + "/code\ttab": " tab", + } + + for TestCase, result := range TestCases { + args := &model.CommandArgs{ + Command: TestCase, + T: func(s string, args ...interface{}) string { return s }, + } + resp, _ := th.App.ExecuteCommand(args) + + assert.Equal(t, resp.Text, result) + } + }) + + t.Run("missing slash character", func(t *testing.T) { + argsMissingSlashCharacter := &model.CommandArgs{ + Command: "missing leading slash character", + T: func(s string, args ...interface{}) string { return s }, + } + _, err := th.App.ExecuteCommand(argsMissingSlashCharacter) + require.Equal(t, "api.command.execute_command.format.app_error", err.Id) + }) + + t.Run("empty", func(t *testing.T) { + argsMissingSlashCharacter := &model.CommandArgs{ + Command: "", + T: func(s string, args ...interface{}) string { return s }, + } + _, err := th.App.ExecuteCommand(argsMissingSlashCharacter) + require.Equal(t, "api.command.execute_command.format.app_error", err.Id) + }) +} + func TestHandleCommandResponsePost(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/i18n/en.json b/i18n/en.json index 1ddfa5079b..ede7a064e9 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -499,6 +499,10 @@ "id": "api.command.execute_command.failed_resp.app_error", "translation": "Command with a trigger of '{{.Trigger}}' returned response {{.Status}}." }, + { + "id": "api.command.execute_command.format.app_error", + "translation": "Command trigger word is missing the leading slash character" + }, { "id": "api.command.execute_command.not_found.app_error", "translation": "Command with a trigger of '{{.Trigger}}' not found. To send a message beginning with \"/\", try adding an empty space at the beginning of the message."