MM-40469: Handle invalid response codes from plugins (#19136)
* MM-40469: Handle invalid response codes from plugins This was an interesting crash detected via Sentry. Typically, any HTTP status code outside 100-999 range will cause a crash in the HTTP server. And `ExecuteCommand` is the only plugin hook which returns a model.AppError instead of error. So an incorrect plugin implementation could return a status code of 0, and crash the server. We handle that by rewriting any illegal response code to 500. https://mattermost.atlassian.net/browse/MM-40469 ```release-note NONE ``` * added warning ```release-note NONE ```
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
052d99c362
Коммит
0e00afd65f
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
type PluginCommand struct {
|
||||
@@ -164,5 +165,12 @@ func (a *App) tryExecutePluginCommand(c *request.Context, args *model.CommandArg
|
||||
errMessage := fmt.Sprintf("err= Plugin %s crashed due to /%s command", matched.PluginId, trigger)
|
||||
return matched.Command, nil, model.NewAppError("ExecutePluginCommand", "model.plugin_command_crash.error.app_error", map[string]interface{}{"Command": trigger, "PluginId": matched.PluginId}, errMessage, http.StatusInternalServerError)
|
||||
}
|
||||
// This is a response from the plugin, which may set an incorrect status code;
|
||||
// e.g setting a status code of 0 will crash the server. So we always bucket everything under 500.
|
||||
if appErr != nil && (appErr.StatusCode < 100 || appErr.StatusCode > 999) {
|
||||
mlog.Warn("Invalid status code returned from plugin. Converting to internal server error.", mlog.String("plugin_id", matched.PluginId), mlog.Int("status_code", appErr.StatusCode))
|
||||
appErr.StatusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return matched.Command, response, appErr
|
||||
}
|
||||
|
||||
@@ -377,4 +377,65 @@ func TestPluginCommand(t *testing.T) {
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
})
|
||||
|
||||
t.Run("plugin returning status code 0", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.PluginSettings.Plugins["testloadpluginconfig"] = map[string]interface{}{
|
||||
"TeamId": args.TeamId,
|
||||
}
|
||||
})
|
||||
|
||||
tearDown, _, activationErrors := SetAppEnvironmentWithPlugins(t, []string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
type configuration struct {
|
||||
TeamId string
|
||||
}
|
||||
|
||||
type MyPlugin struct {
|
||||
plugin.MattermostPlugin
|
||||
|
||||
configuration configuration
|
||||
}
|
||||
|
||||
func (p *MyPlugin) OnActivate() error {
|
||||
err := p.API.RegisterCommand(&model.Command{
|
||||
TeamId: p.configuration.TeamId,
|
||||
Trigger: "plugin",
|
||||
DisplayName: "Plugin Command",
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: "autocomplete",
|
||||
})
|
||||
if err != nil {
|
||||
p.API.LogError("error", "err", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *MyPlugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
|
||||
return nil, &model.AppError{
|
||||
Message: "error",
|
||||
StatusCode: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
plugin.ClientMain(&MyPlugin{})
|
||||
}
|
||||
`}, th.App, th.NewPluginAPI)
|
||||
defer tearDown()
|
||||
require.Len(t, activationErrors, 1)
|
||||
require.Nil(t, nil, activationErrors[0])
|
||||
|
||||
args.Command = "/plugin"
|
||||
_, err := th.App.ExecuteCommand(th.Context, args)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, 500, err.StatusCode)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user