← Back to PRs

#17678: fix(gateway): expose timeoutMs in readJsonBody to prevent Slowloris DoS

by mcrolly open 2026-02-16 02:44 View on GitHub →
gateway stale size: S
## Summary - **Problem:** `readJsonBody()` in `hooks.ts` did not expose the `timeoutMs` parameter, preventing callers from configuring the body read timeout for Slowloris DoS mitigation (CWE-400). - **Why it matters:** While the underlying `readJsonBodyWithLimit()` enforces a 30s default timeout (added in 3cbcba10c), the `readJsonBody()` wrapper hid this parameter, making it impossible for callers to customize the timeout for their specific use case. - **What changed:** Added optional `timeoutMs` parameter to `readJsonBody()` that passes through to `readJsonBodyWithLimit()`. Added 7 comprehensive tests covering timeout, size limits, race conditions, and connection close scenarios. - **What did NOT change:** No changes to the HTTP server config, no new dependencies, no breaking signature changes. The default timeout (30s) remains unchanged. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [x] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [ ] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes #5122 - Closes #6302 ## User-visible / Behavior Changes None. The default 30s timeout was already enforced by the lower layer. This change only exposes the parameter for callers who need to customize it. ## Security Impact (required) - New permissions/capabilities? No - Secrets/tokens handling changed? No - New/changed network calls? No - Command/tool execution surface changed? No - Data access scope changed? No ## Repro + Verification ### Environment - OS: Linux x86_64 - Runtime/container: Node.js v22.22.0 - Model/provider: N/A - Integration/channel: Gateway webhooks - Relevant config: Default hooks config ### Steps 1. Start gateway with hooks enabled 2. Send a slow POST to `/hooks/agent` (e.g., `curl --limit-rate 1 -X POST ...`) 3. Observe that the connection is terminated after 30s (default) or custom `timeoutMs` ### Expected - Slow requests terminated after timeout with `{ ok: false, error: "request body timeout" }` - Socket destroyed to free resources ### Actual - Matches expected behavior ## Evidence - [x] Failing test/log before + passing after ``` ✓ src/gateway/hooks-read-body.test.ts (7 tests) 165ms ✓ src/infra/http-body.test.ts (8 tests) 21ms ✓ src/gateway/hooks.test.ts (15 tests) 5ms ✓ src/gateway/server-http.hooks-request-timeout.test.ts (1 test) 4ms Test Files 4 passed (4) Tests 31 passed (31) ``` ## Human Verification (required) - Verified scenarios: timeout fires on stalled request, normal reads complete without timeout, size limit still enforced, custom timeoutMs respected, no double-resolution race condition, connection close handled - Edge cases checked: empty body, oversized body, slow trickle (Slowloris), connection close before end - What I did **not** verify: Manual integration test with live gateway (verified via unit tests only) ## Compatibility / Migration - Backward compatible? Yes — new parameter is optional with existing default - Config/env changes? No - Migration needed? No ## Failure Recovery (if this breaks) - How to disable/revert: Revert this commit; the underlying timeout still applies via `readJsonBodyWithLimit` defaults - Files/config to restore: `src/gateway/hooks.ts` - Known bad symptoms: N/A — additive-only change ## Risks and Mitigations None — this is a one-line parameter pass-through with tests. <!-- greptile_comment --> <h3>Greptile Summary</h3> Exposes optional `timeoutMs` parameter in `readJsonBody()` wrapper function to allow callers to customize Slowloris DoS protection timeout. The change is minimal (adding one optional parameter and passing it through to the underlying `readJsonBodyWithLimit()`) and maintains full backward compatibility since the default 30s timeout was already enforced at the lower layer. - Added optional `timeoutMs` parameter to `readJsonBody()` function signature in `src/gateway/hooks.ts:177-180` - Parameter correctly passed through to `readJsonBodyWithLimit()` call - New test file `src/gateway/hooks-read-body.test.ts` with 7 tests covering timeout behavior, size limits, race conditions, and edge cases - All existing callers remain unaffected since parameter is optional with existing default behavior <h3>Confidence Score: 5/5</h3> - Safe to merge with no risk - purely additive change with comprehensive test coverage - Simple parameter pass-through that exposes existing functionality without changing default behavior. Backward compatible (optional parameter), well-tested (7 new tests covering timeouts, size limits, and edge cases), and addresses legitimate security concern (Slowloris DoS mitigation CWE-400). All existing callers verified to work without changes. - No files require special attention <sub>Last reviewed commit: 298ce7e</sub> <!-- greptile_other_comments_section --> <sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub> <!-- /greptile_comment -->

Most Similar PRs