fix(hub): slice-3 follow-ups — /host-report 413 oversize + contract golden (v0.7.1)
- handleHostReport: read maxHostReportBytes+1 (4 MiB const) and reject oversize with 413 instead of silent LimitReader truncation. Controller handleReport (1 MiB) is unchanged. Test asserts 413. - contract: hub/internal/api/testdata/host-report.golden.json (byte-identical with felhom-agent's copy) + TestHostReport_GoldenContract drives the real handler and asserts 200 + denorm + both guests upserted. - CHANGELOG v0.7.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -226,6 +226,13 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
|
||||
// per-host override UI yet — that is a later slice).
|
||||
const defaultHostPollSeconds = 900
|
||||
|
||||
// maxHostReportBytes bounds a host-report body. Larger than the controller path's
|
||||
// 1 MiB because host reports carry the full guest list + (later) storage/backup
|
||||
// arrays. We read one byte past it and reject explicitly (413) rather than letting
|
||||
// LimitReader silently truncate — a truncated-but-valid JSON would otherwise be
|
||||
// accepted as a partial report, dropping guests from the mirror.
|
||||
const maxHostReportBytes = 4 << 20 // 4 MiB
|
||||
|
||||
// hostReportPayload is the subset of the agent host-report (slice-3 contract,
|
||||
// §3 / agent spec §4) the hub needs for denorm + guest reality. Unknown fields
|
||||
// (storage_targets/backups/restore_tests/pbs_snapshots/audit_tail) are ignored,
|
||||
@@ -258,13 +265,15 @@ func (h *Handler) handleHostReport(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// 4 MiB: host reports carry the full guest list + future storage/backup arrays;
|
||||
// the controller path's 1 MiB is too tight here.
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 4<<20))
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, maxHostReportBytes+1))
|
||||
if err != nil {
|
||||
http.Error(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(body) > maxHostReportBytes {
|
||||
http.Error(w, "Payload too large", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
var rep hostReportPayload
|
||||
if err := json.Unmarshal(body, &rep); err != nil || rep.HostID == "" {
|
||||
http.Error(w, "Invalid payload: host_id required", http.StatusBadRequest)
|
||||
|
||||
Reference in New Issue
Block a user