mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
47 lines
1.2 KiB
Go
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
|
|
}
|