mirror of
https://github.com/kopia/kopia.git
synced 2026-09-12 21:30:14 -04:00
fix(server): limit allowed body size in server requests (#5561)
Rationale: avoid unbounded resource consumption.
This commit is contained in:
1 parent
b6bd722484
commit
b0138a6e40
2 files changed
+45
-2
No files matched your search
@@ -1,6 +1,8 @@
|
||||
package server_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -36,3 +38,33 @@ func TestPathsAPI(t *testing.T) {
|
||||
|
||||
require.Equal(t, env.LocalPathSourceInfo(dir0), resp.SourceInfo)
|
||||
}
|
||||
|
||||
func TestPathsAPI_RequestBodyTooLarge(t *testing.T) {
|
||||
ctx, env := repotesting.NewEnvironment(t, repotesting.FormatNotImportant)
|
||||
srvInfo := servertesting.StartServer(t, env, false)
|
||||
|
||||
cli, err := apiclient.NewKopiaAPIClient(apiclient.Options{
|
||||
BaseURL: srvInfo.BaseURL,
|
||||
TrustedServerCertificateFingerprint: srvInfo.TrustedServerCertificateFingerprint,
|
||||
Username: servertesting.TestUIUsername,
|
||||
Password: servertesting.TestUIPassword,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, cli.FetchCSRFTokenForTesting(ctx))
|
||||
|
||||
// the request body limit is 100_000 bytes, so a path much longer than that
|
||||
// must be rejected before it reaches the handler.
|
||||
req := &serverapi.ResolvePathRequest{
|
||||
Path: strings.Repeat("a", 105_000),
|
||||
}
|
||||
resp := &serverapi.ResolvePathResponse{}
|
||||
|
||||
err = cli.Post(ctx, "paths/resolve", req, resp)
|
||||
require.Error(t, err)
|
||||
|
||||
var hsr apiclient.HTTPStatusError
|
||||
|
||||
require.ErrorAs(t, err, &hsr)
|
||||
require.Equal(t, http.StatusRequestEntityTooLarge, hsr.HTTPStatusCode)
|
||||
}
|
||||
@@ -358,13 +358,24 @@ func (s *Server) handleUIPossiblyNotConnected(f apiRequestFunc) http.HandlerFunc
|
||||
}
|
||||
|
||||
func (s *Server) handleRequestPossiblyNotConnected(isAuthorized isAuthorizedFunc, checkCSRFToken csrfTokenOption, f apiRequestFunc) http.HandlerFunc {
|
||||
const maxRequestBodySizeBytes = 100_000
|
||||
|
||||
return s.requireAuth(checkCSRFToken, func(ctx context.Context, rc requestContext) {
|
||||
// we must pre-read request body before acquiring the lock as it sometimes leads to deadlock
|
||||
// in HTTP/2 server.
|
||||
// See https://github.com/golang/go/issues/40816
|
||||
body, berr := io.ReadAll(rc.req.Body)
|
||||
body, berr := io.ReadAll(http.MaxBytesReader(rc.w, rc.req.Body, maxRequestBodySizeBytes))
|
||||
if berr != nil {
|
||||
http.Error(rc.w, "error reading request body", http.StatusInternalServerError)
|
||||
errCode := http.StatusInternalServerError
|
||||
|
||||
var mbe *http.MaxBytesError
|
||||
|
||||
if errors.As(berr, &mbe) {
|
||||
errCode = http.StatusRequestEntityTooLarge
|
||||
}
|
||||
|
||||
http.Error(rc.w, "error reading request body", errCode)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user