@@ -47,10 +47,22 @@ func New(d Deps) (*Handlers, error) { |
| 47 | return &Handlers{d: d, q: usersdb.New()}, nil | 47 | return &Handlers{d: d, q: usersdb.New()}, nil |
| 48 | } | 48 | } |
| 49 | | 49 | |
| | 50 | +// apiMaxBodyBytes caps the request body for any /api/v1 handler. The |
| | 51 | +// largest documented payload today is a check-run create with a 64 KiB |
| | 52 | +// summary + 64 KiB text — comfortably below this. Tightening per-route |
| | 53 | +// is fine; widening should happen at the route group, not here. |
| | 54 | +// |
| | 55 | +// The auth-side cap (signup/login/reset) is a separate, lower limit |
| | 56 | +// (`MaxBodySize(8 KiB)`) wired in `auth_wiring.go`. This cap defends |
| | 57 | +// the same surface against a misbehaving PAT-bearing client shipping |
| | 58 | +// a 50 MB JSON blob to weaponize the parser. |
| | 59 | +const apiMaxBodyBytes = 256 * 1024 |
| | 60 | + |
| 50 | // Mount registers /api/v1/* on r. Caller is responsible for putting r | 61 | // Mount registers /api/v1/* on r. Caller is responsible for putting r |
| 51 | // in a CSRF-exempt group. | 62 | // in a CSRF-exempt group. |
| 52 | func (h *Handlers) Mount(r chi.Router) { | 63 | func (h *Handlers) Mount(r chi.Router) { |
| 53 | r.Group(func(r chi.Router) { | 64 | r.Group(func(r chi.Router) { |
| | 65 | + r.Use(middleware.MaxBodySize(apiMaxBodyBytes)) |
| 54 | r.Use(middleware.PATAuthMiddleware(middleware.PATConfig{ | 66 | r.Use(middleware.PATAuthMiddleware(middleware.PATConfig{ |
| 55 | Pool: h.d.Pool, | 67 | Pool: h.d.Pool, |
| 56 | Debouncer: h.d.Debouncer, | 68 | Debouncer: h.d.Debouncer, |