[njs] HTTP: validating URI and args arguments in r.subrequest().

Dmitry Volyntsev xeioex at nginx.com
Tue Apr 2 16:13:44 UTC 2024


details:   https://hg.nginx.org/njs/rev/2d6d9d1e8b8f
branches:  
changeset: 2308:2d6d9d1e8b8f
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Apr 02 08:52:56 2024 -0700
description:
HTTP: validating URI and args arguments in r.subrequest().

diffstat:

 nginx/ngx_http_js_module.c |  37 +++++++++++++++++++++++++++++++++++++
 nginx/t/js_subrequests.t   |  40 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 75 insertions(+), 2 deletions(-)

diffs (138 lines):

diff -r 17af51d42ad9 -r 2d6d9d1e8b8f nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Mon Apr 01 23:13:25 2024 -0700
+++ b/nginx/ngx_http_js_module.c	Tue Apr 02 08:52:56 2024 -0700
@@ -305,6 +305,9 @@ static char *ngx_http_js_merge_loc_conf(
 static ngx_ssl_t *ngx_http_js_ssl(njs_vm_t *vm, ngx_http_request_t *r);
 static ngx_flag_t ngx_http_js_ssl_verify(njs_vm_t *vm, ngx_http_request_t *r);
 
+static ngx_int_t ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r,
+    njs_str_t *uri, njs_str_t *args);
+
 #if (NGX_HTTP_SSL)
 
 static ngx_conf_bitmask_t  ngx_http_js_ssl_protocols[] = {
@@ -3272,6 +3275,11 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm,
         }
     }
 
+    if (ngx_http_js_parse_unsafe_uri(r, &uri_arg, &args_arg) != NGX_OK) {
+        njs_vm_error(vm, "unsafe uri");
+        return NJS_ERROR;
+    }
+
     arg = njs_arg(args, nargs, 3);
 
     if (callback == NULL && !njs_value_is_undefined(arg)) {
@@ -4979,3 +4987,32 @@ ngx_http_js_ssl_verify(njs_vm_t *vm, ngx
     return 0;
 #endif
 }
+
+
+static ngx_int_t
+ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r, njs_str_t *uri,
+    njs_str_t *args)
+{
+    ngx_str_t   uri_arg, args_arg;
+    ngx_uint_t  flags;
+
+    flags = NGX_HTTP_LOG_UNSAFE;
+
+    uri_arg.data = uri->start;
+    uri_arg.len = uri->length;
+
+    args_arg.data = args->start;
+    args_arg.len = args->length;
+
+    if (ngx_http_parse_unsafe_uri(r, &uri_arg, &args_arg, &flags) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    uri->start = uri_arg.data;
+    uri->length = uri_arg.len;
+
+    args->start = args_arg.data;
+    args->length = args_arg.len;
+
+    return NGX_OK;
+}
diff -r 17af51d42ad9 -r 2d6d9d1e8b8f nginx/t/js_subrequests.t
--- a/nginx/t/js_subrequests.t	Mon Apr 01 23:13:25 2024 -0700
+++ b/nginx/t/js_subrequests.t	Tue Apr 02 08:52:56 2024 -0700
@@ -148,6 +148,10 @@ http {
             js_content test.sr_unavail_pr;
         }
 
+        location /sr_unsafe {
+            js_content test.sr_unsafe;
+        }
+
         location /sr_broken {
             js_content test.sr_broken;
         }
@@ -385,6 +389,11 @@ EOF
         subrequest_fn_pr(req, ['/unavail'], ['uri', 'status']);
     }
 
+    function sr_unsafe(r) {
+        r.subrequest('../dir');
+        r.return(200);
+    }
+
     function sr_broken(r) {
         r.subrequest('/daemon/unfinished', reply => {
             r.return(200, JSON.stringify({code:reply.status}));
@@ -498,13 +507,14 @@ EOF
                     sr_unavail_pr, sr_broken, sr_too_large, sr_in_sr,
                     sr_js_in_subrequest, sr_js_in_subrequest_pr, js_sub,
                     sr_in_sr_callback, sr_out_of_order, sr_except_not_a_func,
-                    sr_uri_except, sr_except_failed_to_convert_options_arg};
+                    sr_uri_except, sr_except_failed_to_convert_options_arg,
+                    sr_unsafe};
 
 EOF
 
 $t->write_file('t', '["SEE-THIS"]');
 
-$t->try_run('no njs available')->plan(32);
+$t->try_run('no njs available')->plan(33);
 $t->run_daemon(\&http_daemon);
 
 ###############################################################################
@@ -558,6 +568,13 @@ is(get_json('/sr_in_sr_callback'),
 	'{"e":"subrequest can only be created for the primary request"}',
 	'subrequest for non-primary request');
 
+TODO: {
+local $TODO = 'not yet' unless has_version('0.8.4');
+
+like(http_get('/sr_unsafe'), qr/500/s, 'unsafe subrequest uri');
+
+}
+
 $t->stop();
 
 ok(index($t->read_file('error.log'), 'callback is not a function') > 0,
@@ -634,3 +651,22 @@ sub http_daemon {
 }
 
 ###############################################################################
+
+sub has_version {
+	my $need = shift;
+
+	http_get('/njs') =~ /^([.0-9]+)$/m;
+
+	my @v = split(/\./, $1);
+	my ($n, $v);
+
+	for $n (split(/\./, $need)) {
+		$v = shift @v || 0;
+		return 0 if $n > $v;
+		return 1 if $v > $n;
+	}
+
+	return 1;
+}
+
+###############################################################################


More information about the nginx-devel mailing list