From: Dmitry Volyntsev Date: Fri, 29 May 2026 22:26:15 +0000 (-0700) Subject: Fetch: reject invalid header values X-Git-Tag: 1.0.0~35 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/stylesheets/stylesheet.css?a=commitdiff_plain;h=317fe7ff422ef8175c398449c8bb9b17446826ff;p=njs.git Fetch: reject invalid header values Reject control characters and DEL in Fetch Headers values while preserving OWS trimming and obs-text. --- diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index 3b6db4a2..ab1ea0c5 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -1520,13 +1520,12 @@ static njs_int_t ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value, size_t vlen) { - u_char *p, *end; ngx_int_t ret; ngx_uint_t i; ngx_js_tb_elt_t *h, **ph; ngx_list_part_t *part; - ngx_js_http_trim(&value, &vlen, 0); + ngx_js_http_trim_ows(&value, &vlen); ret = ngx_js_check_header_name(name, len); if (ret != NGX_OK) { @@ -1534,16 +1533,10 @@ ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers, return NJS_ERROR; } - p = value; - end = p + vlen; - - while (p < end) { - if (*p == '\0') { - njs_vm_error(vm, "invalid header value"); - return NJS_ERROR; - } - - p++; + ret = ngx_js_check_header_value(value, vlen); + if (ret != NGX_OK) { + njs_vm_error(vm, "invalid header value"); + return NJS_ERROR; } if (headers->guard == GUARD_IMMUTABLE) { diff --git a/nginx/ngx_js_http.c b/nginx/ngx_js_http.c index 32c59461..28084a58 100644 --- a/nginx/ngx_js_http.c +++ b/nginx/ngx_js_http.c @@ -1696,6 +1696,32 @@ 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) +{ + u_char *start, *end; + + start = *value; + end = start + *len; + + while (start < end && (*start == ' ' || *start == '\t')) { + start++; + } + + while (start < end) { + end--; + + if (*end != ' ' && *end != '\t') { + end++; + break; + } + } + + *value = start; + *len = end - start; +} + + static const uint32_t token_map[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ @@ -1742,6 +1768,26 @@ ngx_js_check_header_name(u_char *name, size_t len) } +ngx_int_t +ngx_js_check_header_value(u_char *value, size_t len) +{ + u_char *p, *end; + + p = value; + end = p + len; + + while (p < end) { + if (*p <= 0x08 || (*p >= 0x0a && *p <= 0x1f) || *p == 0x7f) { + return NGX_ERROR; + } + + p++; + } + + return NGX_OK; +} + + static ngx_int_t ngx_js_http_get_keepalive_connection(ngx_js_http_t *http) { diff --git a/nginx/ngx_js_http.h b/nginx/ngx_js_http.h index 93fa1966..0e88b68a 100644 --- a/nginx/ngx_js_http.h +++ b/nginx/ngx_js_http.h @@ -180,7 +180,9 @@ void ngx_js_http_resolve_done(ngx_js_http_t *http); void ngx_js_http_close_peer(ngx_js_http_t *http); 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_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 0d510adb..d63e98cb 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -1323,13 +1323,12 @@ static ngx_int_t ngx_qjs_headers_append(JSContext *cx, ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value, size_t vlen) { - u_char *p, *end; ngx_int_t ret; ngx_uint_t i; ngx_list_part_t *part; ngx_js_tb_elt_t *h, **ph; - ngx_js_http_trim(&value, &vlen, 0); + ngx_js_http_trim_ows(&value, &vlen); ret = ngx_js_check_header_name(name, len); if (ret != NGX_OK) { @@ -1337,16 +1336,10 @@ ngx_qjs_headers_append(JSContext *cx, ngx_js_headers_t *headers, return NGX_ERROR; } - p = value; - end = p + vlen; - - while (p < end) { - if (*p == '\0') { - JS_ThrowInternalError(cx, "invalid header value"); - return NGX_ERROR; - } - - p++; + ret = ngx_js_check_header_value(value, vlen); + if (ret != NGX_OK) { + JS_ThrowInternalError(cx, "invalid header value"); + return NGX_ERROR; } if (headers->guard == GUARD_IMMUTABLE) { diff --git a/nginx/t/js_fetch_objects.t b/nginx/t/js_fetch_objects.t index c9d04c49..0f2fec08 100644 --- a/nginx/t/js_fetch_objects.t +++ b/nginx/t/js_fetch_objects.t @@ -156,8 +156,42 @@ $t->write_file('test.js', < { - var h = new Headers({A: 'aa\x00a'}); - }, 'invalid header value'], + const invalid = [0, 1, 8, 10, 13, 31, 127]; + + for (var i = 0; i < invalid.length; i++) { + var c = String.fromCharCode(invalid[i]); + var values = [ + c, c + 'aa', 'aa' + c, 'aa' + c + 'a' + ]; + + for (var j = 0; j < values.length; j++) { + try { + new Headers({A: values[j]}); + throw new Error('no error'); + + } catch (e) { + if (e.message != 'invalid header value') { + throw e; + } + } + } + } + + return 'OK'; + + }, 'OK'], + ['valid header value', () => { + var obs = String.fromCharCode(0x80, 0xff); + var h = new Headers({A: '\t a\tb \t'}); + + h.append('A', obs); + + if (h.get('a') != 'a\tb, ' + obs) { + throw new Error('invalid header value normalization'); + } + + return 'OK'; + }, 'OK'], ['combine', () => { var h = new Headers({a: 'X', A: 'Z'}); return h.get('a');