Prominent payload limit error and configurable URL length limit (#27747)

* Added context error handler for MaxBytesError

* Made URL length limit configurable

* Added tests

* Removed an unused function

* Typo
Этот коммит содержится в:
Harshil Sharma
2024-07-25 14:39:58 +05:30
коммит произвёл GitHub
родитель a0a79b4575
Коммит 79480494d0
5 изменённых файлов: 238 добавлений и 43 удалений

Просмотреть файл

@@ -6,6 +6,7 @@ package web
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"reflect"
@@ -31,8 +32,7 @@ import (
)
const (
frameAncestors = "'self' teams.microsoft.com"
maxURLCharacters = 2048
frameAncestors = "'self' teams.microsoft.com"
)
func GetHandlerName(h func(*Context, http.ResponseWriter, *http.Request)) string {
@@ -144,20 +144,15 @@ func generateDevCSP(c Context) string {
return " " + strings.Join(devCSP, " ")
}
func (h Handler) basicSecurityChecks(w http.ResponseWriter, r *http.Request) *model.AppError {
func (h Handler) basicSecurityChecks(c *Context, w http.ResponseWriter, r *http.Request) {
maxURLCharacters := *c.App.Config().ServiceSettings.MaximumURLLength
if len(r.RequestURI) > maxURLCharacters {
return model.NewAppError("basicSecurityChecks", "basic_security_check.url.too_long_error", nil, "", http.StatusRequestURITooLong)
c.Err = model.NewAppError("basicSecurityChecks", "basic_security_check.url.too_long_error", nil, "", http.StatusRequestURITooLong)
return
}
return nil
}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if appErr := h.basicSecurityChecks(w, r); appErr != nil {
http.Error(w, appErr.Error(), appErr.StatusCode)
return
}
w = newWrappedWriter(w)
now := time.Now()
@@ -202,6 +197,12 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c.Params = ParamsFromRequest(r)
c.Logger = c.App.Log()
h.basicSecurityChecks(c, w, r)
if c.Err != nil {
h.handleContextError(c, w, r)
return
}
if *c.App.Config().ServiceSettings.EnableOpenTracing {
span, ctx := tracing.StartRootSpanByContext(context.Background(), "web:ServeHTTP")
carrier := opentracing.HTTPHeadersCarrier(r.Header)
@@ -378,38 +379,8 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Handle errors that have occurred
if c.Err != nil {
c.Err.RequestId = c.AppContext.RequestId()
c.LogErrorByCode(c.Err)
// The locale translation needs to happen after we have logged it.
// We don't want the server logs to be translated as per user locale.
c.Err.Translate(c.AppContext.T)
c.Err.Where = r.URL.Path
// Block out detailed error when not in developer mode
if !*c.App.Config().ServiceSettings.EnableDeveloper {
c.Err.WipeDetailed()
}
// Sanitize all 5xx error messages in hardened mode
if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode && c.Err.StatusCode >= 500 {
c.Err.Id = ""
c.Err.Message = "Internal Server Error"
c.Err.WipeDetailed()
c.Err.StatusCode = 500
c.Err.Where = ""
}
if IsAPICall(c.App, r) || IsWebhookCall(c.App, r) || IsOAuthAPICall(c.App, r) || r.Header.Get("X-Mobile-App") != "" {
w.WriteHeader(c.Err.StatusCode)
w.Write([]byte(c.Err.ToJSON()))
} else {
utils.RenderWebAppError(c.App.Config(), w, r, c.Err, c.App.AsymmetricSigningKey())
}
if c.App.Metrics() != nil {
c.App.Metrics().IncrementHTTPError()
}
h.handleContextError(c, w, r)
return
}
statusCode = strconv.Itoa(w.(*responseWriterWrapper).StatusCode())
@@ -429,6 +400,55 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
func (h Handler) handleContextError(c *Context, w http.ResponseWriter, r *http.Request) {
if c.Err == nil {
return
}
// We're handling payload limit error here because it needs to be handled globally.
var maxBytesErr *http.MaxBytesError
// check if error is a MaxBytesError error, which occurs when you read more bytes from buffer than configured
if ok := errors.As(c.Err, &maxBytesErr); ok {
// replace the context error with this error if so,
newErr := model.NewAppError(c.Err.Where, "api.context.request_body_too_large.app_error", nil, "Use the setting `MaximumPayloadSizeBytes` in Mattermost config to configure allowed payload limit. Learn more about this setting in Mattermost docs at https://docs.mattermost.com/configure/environment-configuration-settings.html#maximum-payload-size", http.StatusRequestEntityTooLarge)
c.Err = newErr
}
c.Err.RequestId = c.AppContext.RequestId()
c.LogErrorByCode(c.Err)
// The locale translation needs to happen after we have logged it.
// We don't want the server logs to be translated as per user locale.
c.Err.Translate(c.AppContext.T)
c.Err.Where = r.URL.Path
// Block out detailed error when not in developer mode
if !*c.App.Config().ServiceSettings.EnableDeveloper {
c.Err.WipeDetailed()
}
// Sanitize all 5xx error messages in hardened mode
if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode && c.Err.StatusCode >= 500 {
c.Err.Id = ""
c.Err.Message = "Internal Server Error"
c.Err.WipeDetailed()
c.Err.StatusCode = 500
c.Err.Where = ""
}
if IsAPICall(c.App, r) || IsWebhookCall(c.App, r) || IsOAuthAPICall(c.App, r) || r.Header.Get("X-Mobile-App") != "" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(c.Err.StatusCode)
w.Write([]byte(c.Err.ToJSON()))
} else {
utils.RenderWebAppError(c.App.Config(), w, r, c.Err, c.App.AsymmetricSigningKey())
}
if c.App.Metrics() != nil {
c.App.Metrics().IncrementHTTPError()
}
}
type OriginClient string
const (