Enforce the body size limit before CSRF parses the form (closes #90)
Some checks failed
check / check (push) Has been cancelled

CSRF ran before MaxBodySize, so the CSRF middleware parsed the form body
before any cap applied and an oversized request was read in full before
being rejected. MaxBodySize is now the first middleware in all four route
groups that parse forms, ahead of CSRF and RequireAuth.

An oversize request therefore gets 413 without the handler running and
without state changing, including the password-change route.

Note the ordering trade: an unauthenticated client now receives 413 rather
than an auth redirect on /user/{username}/password.
This commit was merged in pull request #91.
This commit is contained in:
2026-08-11 14:37:38 +02:00
parent 15a61173fc
commit d51cd0fd29
9 changed files with 662 additions and 48 deletions

View File

@@ -29,10 +29,8 @@ func (h *Handlers) HandleLoginPage() http.HandlerFunc {
// HandleLoginSubmit handles the login form submission (POST)
func (h *Handlers) HandleLoginSubmit() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Limit request body to prevent memory exhaustion
r.Body = http.MaxBytesReader(w, r.Body, 1<<maxBodyShift)
// Parse form data
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err := r.ParseForm()
if err != nil {
h.log.Error("failed to parse form", "error", err)

View File

@@ -31,9 +31,8 @@ func (h *Handlers) HandlePasswordChange() http.HandlerFunc {
return
}
// Limit request body to prevent memory exhaustion.
r.Body = http.MaxBytesReader(w, r.Body, 1<<maxBodyShift)
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err := r.ParseForm()
if err != nil {
h.log.Error("failed to parse form", "error", err)

View File

@@ -213,10 +213,8 @@ func (h *Handlers) HandleSourceCreateSubmit() http.HandlerFunc {
return
}
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err := r.ParseForm()
if err != nil {
http.Error(
@@ -485,10 +483,8 @@ func (h *Handlers) HandleSourceEditSubmit() http.HandlerFunc {
return
}
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err = r.ParseForm()
if err != nil {
http.Error(
@@ -508,10 +504,8 @@ func (h *Handlers) applyWebhookEdit(
r *http.Request,
webhook *database.Webhook,
) {
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize middleware,
// which runs before CSRF parses the form.
name := r.FormValue("name")
if name == "" {
data := map[string]any{
@@ -890,10 +884,8 @@ func (h *Handlers) HandleEntrypointCreate() http.HandlerFunc {
return
}
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err = r.ParseForm()
if err != nil {
http.Error(
@@ -950,10 +942,8 @@ func (h *Handlers) HandleTargetCreate() http.HandlerFunc {
return
}
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize
// middleware, which runs before CSRF parses the form.
err = r.ParseForm()
if err != nil {
http.Error(
@@ -973,10 +963,8 @@ func (h *Handlers) processTargetCreate(
r *http.Request,
webhook database.Webhook,
) {
r.Body = http.MaxBytesReader(
w, r.Body, 1<<maxBodyShift,
)
// The body size cap is enforced by the MaxBodySize middleware,
// which runs before CSRF parses the form.
name := r.FormValue("name")
targetType := database.TargetType(r.FormValue("type"))
targetURL := r.FormValue("url")