diff --git a/main.go b/main.go index b2ce010..b09787b 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "micrach/utils" ) +const multipartBodyLimit = 13 * 1024 * 1024 + func skipRateLimit(isLocal bool, path string) bool { isRequestForStatic := strings.Contains(path, "/static") || strings.Contains(path, "/uploads") || @@ -61,7 +63,10 @@ func main() { engine.AddFunc("Iterate", templates.Iterate) engine.AddFunc("NotNil", templates.NotNil) - app := fiber.New(fiber.Config{Views: engine}) + app := fiber.New(fiber.Config{ + Views: engine, + BodyLimit: multipartBodyLimit, + }) app.Use(recover.New()) if config.App.IsRateLimiterEnabled { diff --git a/main_test.go b/main_test.go index fa3dcc2..c89c021 100644 --- a/main_test.go +++ b/main_test.go @@ -1,17 +1,33 @@ package main import ( + "bytes" "io" + "mime/multipart" "net/http" "net/http/httptest" + "net/textproto" "os" "path/filepath" "testing" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/limiter" + + "micrach/config" + "micrach/controllers" + "micrach/utils" ) +type mainTestViews struct{} + +func (mainTestViews) Load() error { return nil } + +func (mainTestViews) Render(out io.Writer, name string, _ any, _ ...string) error { + _, err := io.WriteString(out, name) + return err +} + func TestRegisterStaticRoutes(t *testing.T) { t.Chdir(t.TempDir()) @@ -109,3 +125,46 @@ func TestLimiterRejectsOrdinaryRemoteRequests(t *testing.T) { } } } + +func TestMultipartBodyLimitAllowsApplicationFileSizeValidation(t *testing.T) { + previousConfig := config.App + t.Cleanup(func() { + config.App = previousConfig + }) + config.App.IsCaptchaActive = false + + var body bytes.Buffer + writer := multipart.NewWriter(&body) + if err := writer.WriteField("title", "oversize"); err != nil { + t.Fatal(err) + } + if err := writer.WriteField("text", "oversize upload"); err != nil { + t.Fatal(err) + } + header := make(textproto.MIMEHeader) + header.Set("Content-Disposition", `form-data; name="files"; filename="oversize.png"`) + header.Set("Content-Type", "image/png") + part, err := writer.CreatePart(header) + if err != nil { + t.Fatal(err) + } + if _, err := part.Write(bytes.Repeat([]byte("x"), utils.FILE_SIZE_IN_BYTES+(1<<20))); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + + app := fiber.New(fiber.Config{Views: mainTestViews{}, BodyLimit: multipartBodyLimit}) + app.Post("/", controllers.CreateThread) + req := httptest.NewRequest(http.MethodPost, "/", &body) + req.Header.Set(fiber.HeaderContentType, writer.FormDataContentType()) + resp, err := app.Test(req) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != fiber.StatusBadRequest { + t.Fatalf("status = %d, want %d", resp.StatusCode, fiber.StatusBadRequest) + } +}