From 3f5ac135f9aee182fdd62ba3236e878440ce0e38 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 15 Sep 2026 15:45:50 -0700 Subject: [PATCH] Fetch: validate response body size Previously, a negative max_response_body_size could make a close-delimited response appear as a successful empty body. The fix is to reject the invalid option while parsing fetch arguments. --- nginx/ngx_js_fetch.c | 5 +++++ nginx/ngx_qjs_fetch.c | 5 +++++ nginx/t/js_fetch.t | 31 ++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index 06dff385..9af56821 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -586,6 +586,11 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, #endif } + if (http->max_response_body_size < 0) { + njs_vm_type_error(vm, "max_response_body_size must be non-negative"); + goto fail; + } + #if (NGX_SSL) if (http->ssl != NULL && !http->ssl_verify) { http->keepalive = 0; diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c index 3373c9a9..d4b2d536 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -336,6 +336,11 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc, #endif } + if (http->max_response_body_size < 0) { + JS_ThrowTypeError(cx, "max_response_body_size must be non-negative"); + goto fail; + } + #if (NGX_SSL) if (http->ssl != NULL && !http->ssl_verify) { http->keepalive = 0; diff --git a/nginx/t/js_fetch.t b/nginx/t/js_fetch.t index 4c23fcfb..76370ab4 100644 --- a/nginx/t/js_fetch.t +++ b/nginx/t/js_fetch.t @@ -64,6 +64,10 @@ http { js_content test.broken_catch; } + location /body_size { + js_content test.body_size; + } + location /body { js_content test.body; } @@ -273,6 +277,19 @@ $t->write_file('test.js', <write_file('test.js', <try_run('no njs.fetch'); -$t->plan(45); +$t->plan(46); $t->run_daemon(\&http_daemon, port(8082)); $t->waitforsocket('127.0.0.1:' . port(8082)); @@ -544,6 +562,9 @@ like(http_get('/broken'), qr/200/s, 'fetch broken'); like(http_get('/broken_response'), qr/200/s, 'fetch broken response'); like(http_get('/broken_catch'), qr/\["async","async","async"]$/s, 'fetch broken catch'); +like(http_get('/body_size'), + qr/501.*max_response_body_size must be non-negative/s, + 'fetch negative response body size'); like(http_get('/chunked_ok'), qr/200/s, 'fetch chunked ok'); like(http_get('/chunked_fail'), qr/200/s, 'fetch chunked fail'); like(http_get('/chain'), qr/200 OK.*SUCCESS$/s, 'fetch chain'); -- 2.47.3