From: Dmitry Volyntsev Date: Fri, 29 May 2026 22:58:34 +0000 (-0700) Subject: Fetch: reject unsafe request methods X-Git-Tag: 1.0.0~34 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/stylesheets/stylesheet.css?a=commitdiff_plain;h=5ee6e0763e6a16a89d429edf8d8d587c691ed5cc;p=njs.git Fetch: reject unsafe request methods Reject empty methods, C0 control bytes, space, and DEL before a fetch request can be serialized. Preserve existing forbidden-method checks, common-method normalization, and valid server-side extension methods. --- diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index ab1ea0c5..10219545 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -898,6 +898,15 @@ ngx_js_method_process(njs_vm_t *vm, ngx_js_request_t *request) str.start = request->method.data; str.length = request->method.len; + if (request->method.len == 0 + || ngx_js_check_request_line_component(request->method.data, + request->method.len) + != NGX_OK) + { + njs_vm_error(vm, "invalid Request method"); + return NJS_ERROR; + } + for (m = &forbidden[0]; m->length != 0; m++) { if (njs_strstr_case_eq(&str, m)) { njs_vm_error(vm, "forbidden method: %V", m); diff --git a/nginx/ngx_js_http.c b/nginx/ngx_js_http.c index 28084a58..2df784fe 100644 --- a/nginx/ngx_js_http.c +++ b/nginx/ngx_js_http.c @@ -1768,6 +1768,26 @@ ngx_js_check_header_name(u_char *name, size_t len) } +ngx_int_t +ngx_js_check_request_line_component(u_char *value, size_t len) +{ + u_char *p, *end; + + p = value; + end = p + len; + + while (p < end) { + if (*p <= 0x20 || *p == 0x7f) { + return NGX_ERROR; + } + + p++; + } + + return NGX_OK; +} + + ngx_int_t ngx_js_check_header_value(u_char *value, size_t len) { diff --git a/nginx/ngx_js_http.h b/nginx/ngx_js_http.h index 0e88b68a..b9e4666c 100644 --- a/nginx/ngx_js_http.h +++ b/nginx/ngx_js_http.h @@ -182,6 +182,7 @@ void ngx_js_http_trim(u_char **value, size_t *len, int trim_c0_control_or_space); void ngx_js_http_trim_ows(u_char **value, size_t *len); ngx_int_t ngx_js_check_header_name(u_char *name, size_t len); +ngx_int_t ngx_js_check_request_line_component(u_char *value, size_t len); ngx_int_t ngx_js_check_header_value(u_char *value, size_t len); ngx_buf_t *ngx_js_chain_to_buf(ngx_pool_t *pool, njs_chb_t *chain); diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c index d63e98cb..3a41a78c 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -938,6 +938,15 @@ ngx_qjs_method_process(JSContext *cx, ngx_js_request_t *request) ngx_null_string, }; + if (request->method.len == 0 + || ngx_js_check_request_line_component(request->method.data, + request->method.len) + != NGX_OK) + { + JS_ThrowInternalError(cx, "invalid Request method"); + return NGX_ERROR; + } + for (m = &forbidden[0]; m->len != 0; m++) { if (request->method.len == m->len && ngx_strncasecmp(request->method.data, m->data, m->len) == 0) diff --git a/nginx/t/js_fetch_objects.t b/nginx/t/js_fetch_objects.t index 0f2fec08..1bc0ef93 100644 --- a/nginx/t/js_fetch_objects.t +++ b/nginx/t/js_fetch_objects.t @@ -349,20 +349,53 @@ $t->write_file('test.js', < { const methods = ['get', 'hEad', 'Post', 'OPTIONS', 'PUT', - 'DELETE', 'CONNECT']; - try { - methods.forEach(m => { - var r = new Request("http://nginx.org", {method: m}); - if (r.method != m.toUpperCase()) { - throw new Error(`r.method != \${m}`); + 'DELETE']; + const forbidden = ['CONNECT', 'TRACE', 'TRACK']; + const invalid = ['', 'GET ', ' GET', 'GE T', 'GE\\tT', + 'GE\\nT', 'GE\\rT', 'GE\\x00T', + 'GET\\r\\nX: y', + 'GE' + String.fromCharCode(0x7f) + 'T']; + const high = 'G' + String.fromCharCode(0xc9) + 'T'; + const extensions = ['PROPFIND', 'propfind', 'M-SEARCH', + high, 'FOO/BAR']; + + methods.forEach(m => { + var r = new Request("http://nginx.org", {method: m}); + if (r.method != m.toUpperCase()) { + throw new Error(`r.method != \${m}`); + } + }); + + forbidden.forEach(m => { + try { + new Request("http://nginx.org", {method: m}); + throw new Error('no error'); + + } catch (e) { + if (!e.message.startsWith(`forbidden method: \${m}`)) { + throw e; } - }) + } + }); - } catch (e) { - if (!e.message.startsWith('forbidden method: CONNECT')) { - throw e; + invalid.forEach(m => { + try { + new Request("http://nginx.org", {method: m}); + throw new Error('no error'); + + } catch (e) { + if (e.message != 'invalid Request method') { + throw e; + } } - } + }); + + extensions.forEach(m => { + var r = new Request("http://nginx.org", {method: m}); + if (r.method != m) { + throw new Error(`r.method != \${m}`); + } + }); return 'OK';