Files
pulse/internal/api/security_request_body.go
T
2026-08-31 01:43:55 +01:00

47 lines
1.2 KiB
Go

package api
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
// Security requests carry only credentials and small control fields. Keeping a
// shared bound prevents public authentication and recovery routes from
// allocating attacker-controlled JSON strings without limit.
const maxSecurityRequestBodyBytes int64 = 16 * 1024
func decodeSecurityRequestBody(w http.ResponseWriter, r *http.Request, dst any) error {
if r == nil || r.Body == nil {
return io.EOF
}
r.Body = http.MaxBytesReader(w, r.Body, maxSecurityRequestBodyBytes)
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(dst); err != nil {
return err
}
// A second decode both rejects concatenated JSON documents and forces the
// bounded reader to consume trailing whitespace. Without it, a valid first
// object could hide an arbitrarily large unread request tail.
var trailing any
if err := decoder.Decode(&trailing); err != io.EOF {
if err == nil {
return fmt.Errorf("request body must contain a single JSON value")
}
return err
}
return nil
}
func securityRequestErrorStatus(err error) int {
var maxBytesErr *http.MaxBytesError
if errors.As(err, &maxBytesErr) {
return http.StatusRequestEntityTooLarge
}
return http.StatusBadRequest
}