security: hide router dispatch errors
Jaime Fournier <jaimef@linbsd.org>
6f6fb649bd28bc1003dfb70629bb2bc1d54697ae
diff --git a/docs/kimi3-security-recommmendations.md b/docs/kimi3-security-recommmendations.md
index 0ffee14..0ab10a4 100644
--- a/docs/kimi3-security-recommmendations.md
+++ b/docs/kimi3-security-recommmendations.md
@@ -791,8 +791,9 @@ they are build-time. Close the runtime loop.
unexpected tool-handler exceptions and framed/unframed JSON parse failures
while logging internal detail server-side. `jlsp` now returns opaque
references for request-handler failures and logs detailed read/handler
- failures server-side under the same reference. Remaining work: router
- integrations need opaque client error refs.
+ failures server-side under the same reference. `thread-httpd` router dispatch
+ now converts direct route-handler exceptions to opaque 500 responses with
+ references, covering the remaining router integration surface.
### K3-P1-11 — Parameterized-only SQL in the safe surface
**Serves:** G1. **Effort:** 2–3 days.
diff --git a/lib/std/net/thread-httpd.ss b/lib/std/net/thread-httpd.ss
index ea1b25b..a7592e1 100644
--- a/lib/std/net/thread-httpd.ss
+++ b/lib/std/net/thread-httpd.ss
@@ -360,8 +360,11 @@
[params (and (string=? (car route) method)
(match-path-pattern (cadr route) path))])
(if params
- (parameterize ([current-route-params params])
- ((caddr route) req))
+ (try
+ (parameterize ([current-route-params params])
+ ((caddr route) req))
+ (catch (exn)
+ (safe-http-error-response 'internal-error exn)))
(loop (cdr routes))))))))
(def (route-get r path handler) (router-add! r "GET" path handler))
diff --git a/tests/test-thread-httpd-errors.ss b/tests/test-thread-httpd-errors.ss
index b0cb14d..0c16e13 100644
--- a/tests/test-thread-httpd-errors.ss
+++ b/tests/test-thread-httpd-errors.ss
@@ -63,6 +63,25 @@
#f)))
(lambda ()
(thread-httpd-stop! server))))
+(let* ([secret "router-secret-token-789"]
+ [router (make-router)])
+ (router-add! router "GET" "/boom"
+ (lambda (req)
+ (error 'test-thread-httpd-router secret)))
+ (let* ([resp (router-dispatch
+ router
+ (make-request "GET" "/boom" "HTTP/1.1" '() ""))]
+ [body (response-body resp)])
+ (test "router-dispatch status" (response-status resp) 500)
+ (test "router-dispatch body has safe message"
+ (contains-substring? body "Internal server error")
+ #t)
+ (test "router-dispatch body has reference"
+ (contains-substring? body "Reference: ")
+ #t)
+ (test "router-dispatch hides handler secret"
+ (contains-substring? body secret)
+ #f)))
(printf "~%thread-httpd error tests: ~a passed, ~a failed~%" pass fail)
(when (> fail 0)