--- phase: 01-data-integrity plan: 02 subsystem: api tags: [go, http, security, testing, maxbytesreader, body-size-limit] # Dependency graph requires: - phase: 01-data-integrity plan 01 provides: UPSERT fix and FK enforcement already applied; test file structure established provides: - HTTP 413 response for oversized request bodies (>1MB) on WebhookHandler, TagsHandler POST, TagAssignmentHandler PUT/DELETE - maxBodyBytes constant (1 << 20) and MaxBytesReader + errors.As pattern - t.Fatalf at all 6 test setup call sites (no more silent test pass-on-setup-failure) - 3 new oversized-body tests proving DATA-03 fixed affects: - phase-02 (database refactor — handlers are now correct and hardened, test suite is reliable) - any future handler additions that accept a body (pattern established) # Tech tracking tech-stack: added: [] patterns: - "MaxBytesReader + errors.As(*http.MaxBytesError) pattern for request body size limiting in handlers" - "JSON-prefix oversized body test: use valid JSON opening so decoder reads past limit before MaxBytesReader triggers" key-files: created: [] modified: - pkg/diunwebhook/diunwebhook.go - pkg/diunwebhook/diunwebhook_test.go key-decisions: - "Use MaxBytesReader wrapping r.Body before each JSON decode; distinguish 413 from 400 via errors.As on *http.MaxBytesError" - "Oversized body test bodies must use valid JSON prefix (e.g. {\"image\":\") + padding — all-x bodies trigger JSON parse error before MaxBytesReader limit is reached" patterns-established: - "MaxBytesReader body guard pattern: r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes) before decode, errors.As for 413 vs 400" - "Test setup errors must use t.Fatalf, never silent return" requirements-completed: - DATA-03 - DATA-04 # Metrics duration: 7min completed: 2026-03-23 --- # Phase 01 Plan 02: Body Size Limits and Test Setup Hardening Summary **Request body size limits (1MB cap, HTTP 413) added to four handler paths; six silent test-setup returns replaced with t.Fatalf to surface setup failures in CI** ## Performance - **Duration:** 7 min - **Started:** 2026-03-23T20:17:30Z - **Completed:** 2026-03-23T20:24:37Z - **Tasks:** 2 - **Files modified:** 2 ## Accomplishments - Added `maxBodyBytes` constant and `errors` import to `diunwebhook.go`; applied `http.MaxBytesReader` + `errors.As(*http.MaxBytesError)` guard before JSON decode in WebhookHandler, TagsHandler POST, TagAssignmentHandler PUT and DELETE — returns HTTP 413 on body > 1MB - Replaced 6 silent `if err != nil { return }` test setup patterns with `t.Fatalf(...)` so failing setup always fails the test, not silently passes - Added 3 new oversized-body tests (TestWebhookHandler_OversizedBody, TestTagsHandler_OversizedBody, TestTagAssignmentHandler_OversizedBody); all pass with 413 ## Task Commits Each task was committed atomically: 1. **RED: add failing 413 tests** - `311e91d` (test) 2. **Task 1: MaxBytesReader in handlers + GREEN test fix** - `98dfd76` (feat) 3. **Task 2: Replace silent returns with t.Fatalf** - `7bdfc5f` (fix) **Plan metadata:** (docs commit — see below) _Note: TDD task 1 has a RED commit followed by a combined feat commit covering the implementation and the test body correction._ ## Files Created/Modified - `pkg/diunwebhook/diunwebhook.go` — added `errors` import, `maxBodyBytes` constant, MaxBytesReader guards in 4 handler paths - `pkg/diunwebhook/diunwebhook_test.go` — 3 new oversized-body tests; 6 t.Fatalf replacements ## Decisions Made - `http.MaxBytesReader` is applied per-handler (not via middleware) to match the existing no-middleware architecture - Body limit set at 1MB (`1 << 20`) matching the plan spec - Oversized body test bodies use a valid JSON prefix (`{"image":"` + padding) rather than all-`x` bytes — the JSON decoder reads only 1 byte of invalid content before failing, so all-`x` never triggers MaxBytesReader; a JSON string value causes the decoder to read the full field before the limit fires ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Oversized body tests used all-x bytes; fixed to use valid JSON prefix** - **Found during:** Task 1 GREEN phase - **Issue:** Test body `make([]byte, 1<<20+1)` filled with `'x'` causes JSON decoder to fail at byte 1 with "invalid character" — MaxBytesReader never triggers because the read count never reaches the limit - **Fix:** Changed test bodies to `{"image":"` (or `{"name":"`) + `bytes.Repeat([]byte("x"), 1<<20+1)` so the decoder reads past 1MB before encountering an unterminated string - **Files modified:** pkg/diunwebhook/diunwebhook_test.go - **Verification:** All 3 oversized-body tests now pass with HTTP 413 - **Committed in:** 98dfd76 (Task 1 feat commit) --- **Total deviations:** 1 auto-fixed (Rule 1 - test bug) **Impact on plan:** The fix is necessary for tests to validate what they claim. No scope creep; the handler implementation is exactly as specified. ## Issues Encountered None beyond the test body deviation documented above. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - DATA-03 and DATA-04 fixed; all Phase 01 plans complete - Full test suite passes with 0 failures - Handler hardening pattern (MaxBytesReader + errors.As) established for future handlers - Ready to transition to Phase 02 (database refactor / PostgreSQL support) --- *Phase: 01-data-integrity* *Completed: 2026-03-23*