diff --git a/src/jv_aux.c b/src/jv_aux.c index 018f380b..fd5ff966 100644 --- a/src/jv_aux.c +++ b/src/jv_aux.c @@ -365,6 +365,10 @@ static jv jv_dels(jv t, jv keys) { return t; } +#ifndef MAX_PATH_DEPTH +#define MAX_PATH_DEPTH (10000) +#endif + jv jv_setpath(jv root, jv path, jv value) { if (jv_get_kind(path) != JV_KIND_ARRAY) { jv_free(value); @@ -372,6 +376,12 @@ jv jv_setpath(jv root, jv path, jv value) { jv_free(path); return jv_invalid_with_msg(jv_string("Path must be specified as an array")); } + if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { + jv_free(value); + jv_free(root); + jv_free(path); + return jv_invalid_with_msg(jv_string("Path too deep")); + } if (!jv_is_valid(root)){ jv_free(value); jv_free(path); @@ -424,6 +434,11 @@ jv jv_getpath(jv root, jv path) { jv_free(path); return jv_invalid_with_msg(jv_string("Path must be specified as an array")); } + if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { + jv_free(root); + jv_free(path); + return jv_invalid_with_msg(jv_string("Path too deep")); + } if (!jv_is_valid(root)) { jv_free(path); return root; @@ -502,6 +517,12 @@ jv jv_delpaths(jv object, jv paths) { jv_free(elem); return err; } + if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) { + jv_free(object); + jv_free(paths); + jv_free(elem); + return jv_invalid_with_msg(jv_string("Path too deep")); + } jv_free(elem); } if (jv_array_length(jv_copy(paths)) == 0) { diff --git a/tests/jq.test b/tests/jq.test index 4a84e96c..0cd5198f 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -2568,3 +2568,28 @@ true reduce range(10001) as $_ ([];[.]) | tojson | contains("") null true + +# regression test for CVE-2026-33947 +setpath([range(10000) | 0]; 0) | flatten +null +[0] + +try setpath([range(10001) | 0]; 0) catch . +null +"Path too deep" + +getpath([range(10000) | 0]) +null +null + +try getpath([range(10001) | 0]) catch . +null +"Path too deep" + +delpaths([[range(10000) | 0]]) +null +null + +try delpaths([[range(10001) | 0]]) catch . +null +"Path too deep"