[njs] Fixed safe mode bypass in Function constructor.
Dmitry Volyntsev
xeioex at nginx.com
Tue Feb 2 15:21:09 UTC 2021
details: https://hg.nginx.org/njs/rev/a41cd80e2f3a
branches:
changeset: 1599:a41cd80e2f3a
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Mon Feb 01 02:16:51 2021 +0100
description:
Fixed safe mode bypass in Function constructor.
This closes #367 pull request on Github.
Thanks bux.patryk at gmail.com for prodding it.
diffstat:
src/njs_function.c | 82 ++++++++++++++++++++++++++++-------------------
src/test/njs_unit_test.c | 25 ++++++++++++++
2 files changed, 74 insertions(+), 33 deletions(-)
diffs (150 lines):
diff -r 8d4b7b3ae73d -r a41cd80e2f3a src/njs_function.c
--- a/src/njs_function.c Mon Feb 01 16:13:10 2021 +0000
+++ b/src/njs_function.c Mon Feb 01 02:16:51 2021 +0100
@@ -869,41 +869,31 @@ static njs_int_t
njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- njs_chb_t chain;
- njs_int_t ret;
- njs_str_t str, file;
- njs_uint_t i;
- njs_value_t *body;
- njs_lexer_t lexer;
- njs_parser_t *parser;
- njs_vm_code_t *code;
- njs_function_t *function;
- njs_generator_t generator;
- njs_parser_scope_t *scope;
- njs_function_lambda_t *lambda;
+ njs_chb_t chain;
+ njs_int_t ret;
+ njs_str_t str, file;
+ njs_uint_t i;
+ njs_lexer_t lexer;
+ njs_parser_t *parser;
+ njs_vm_code_t *code;
+ njs_function_t *function;
+ njs_generator_t generator;
+ njs_parser_node_t *node;
+ njs_parser_scope_t *scope;
+ njs_function_lambda_t *lambda;
+ const njs_token_type_t *type;
- if (!vm->options.unsafe) {
- body = njs_argument(args, nargs - 1);
- ret = njs_value_to_string(vm, body, body);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- njs_string_get(body, &str);
+ static const njs_token_type_t safe_ast[] = {
+ NJS_TOKEN_END,
+ NJS_TOKEN_FUNCTION_EXPRESSION,
+ NJS_TOKEN_STATEMENT,
+ NJS_TOKEN_RETURN,
+ NJS_TOKEN_THIS,
+ NJS_TOKEN_ILLEGAL
+ };
- /*
- * Safe mode exception:
- * "(new Function('return this'))" is often used to get
- * the global object in a portable way.
- */
-
- if (str.length != njs_length("return this")
- || memcmp(str.start, "return this", 11) != 0)
- {
- njs_type_error(vm, "function constructor is disabled"
- " in \"safe\" mode");
- return NJS_ERROR;
- }
+ if (!vm->options.unsafe && nargs != 2) {
+ goto fail;
}
njs_chb_init(&chain, vm->mem_pool);
@@ -960,6 +950,27 @@ njs_function_constructor(njs_vm_t *vm, n
return ret;
}
+ if (!vm->options.unsafe) {
+ /*
+ * Safe mode exception:
+ * "(new Function('return this'))" is often used to get
+ * the global object in a portable way.
+ */
+
+ node = parser->node;
+ type = &safe_ast[0];
+
+ for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
+ if (node == NULL || node->left != NULL) {
+ goto fail;
+ }
+
+ if (node->token_type != *type) {
+ goto fail;
+ }
+ }
+ }
+
scope = parser->scope;
ret = njs_variables_copy(vm, &scope->variables, vm->variables_hash);
@@ -998,6 +1009,11 @@ njs_function_constructor(njs_vm_t *vm, n
njs_set_function(&vm->retval, function);
return NJS_OK;
+
+fail:
+
+ njs_type_error(vm, "function constructor is disabled in \"safe\" mode");
+ return NJS_ERROR;
}
diff -r 8d4b7b3ae73d -r a41cd80e2f3a src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Mon Feb 01 16:13:10 2021 +0000
+++ b/src/test/njs_unit_test.c Mon Feb 01 02:16:51 2021 +0100
@@ -19518,6 +19518,25 @@ static njs_unit_test_t njs_test[] =
};
+static njs_unit_test_t njs_safe_test[] =
+{
+ { njs_str("(new Function('return this'))() === globalThis"),
+ njs_str("true") },
+
+ { njs_str("(new Function('return this;'))() === globalThis"),
+ njs_str("true") },
+
+ { njs_str("(new Function('return this '))() === globalThis"),
+ njs_str("true") },
+
+ { njs_str("(new Function('return thi'))()"),
+ njs_str("TypeError: function constructor is disabled in \"safe\" mode") },
+
+ { njs_str("(new Function('){return 1337})//', 'return this'))()"),
+ njs_str("TypeError: function constructor is disabled in \"safe\" mode") },
+};
+
+
static njs_unit_test_t njs_denormals_test[] =
{
{ njs_str("2.2250738585072014e-308"),
@@ -21777,6 +21796,12 @@ static njs_test_suite_t njs_suites[] =
njs_nitems(njs_test),
njs_unit_test },
+ { njs_str("safe script"),
+ { .repeat = 1},
+ njs_safe_test,
+ njs_nitems(njs_safe_test),
+ njs_unit_test },
+
{ njs_str("denormals"),
{ .repeat = 1, .unsafe = 1 },
njs_denormals_test,
More information about the nginx-devel
mailing list