mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
detect invalid path expression (fix #862)
This commit is contained in:
72
execute.c
72
execute.c
@@ -34,6 +34,7 @@ struct jq_state {
|
||||
stack_ptr fork_top;
|
||||
|
||||
jv path;
|
||||
jv value_at_path;
|
||||
int subexp_nest;
|
||||
int debug_trace_enabled;
|
||||
int initial_execution;
|
||||
@@ -191,6 +192,7 @@ struct forkpoint {
|
||||
stack_ptr saved_data_stack;
|
||||
stack_ptr saved_curr_frame;
|
||||
int path_len, subexp_nest;
|
||||
jv value_at_path;
|
||||
uint16_t* return_address;
|
||||
};
|
||||
|
||||
@@ -210,20 +212,33 @@ void stack_save(jq_state *jq, uint16_t* retaddr, struct stack_pos sp){
|
||||
fork->saved_curr_frame = jq->curr_frame;
|
||||
fork->path_len =
|
||||
jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0;
|
||||
fork->value_at_path = jv_copy(jq->value_at_path);
|
||||
fork->subexp_nest = jq->subexp_nest;
|
||||
fork->return_address = retaddr;
|
||||
jq->stk_top = sp.saved_data_stack;
|
||||
jq->curr_frame = sp.saved_curr_frame;
|
||||
}
|
||||
|
||||
void path_append(jq_state* jq, jv component) {
|
||||
static int path_intact(jq_state *jq, jv curr) {
|
||||
if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {
|
||||
return jv_identical(curr, jv_copy(jq->value_at_path));
|
||||
} else {
|
||||
jv_free(curr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void path_append(jq_state* jq, jv component, jv value_at_path) {
|
||||
if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {
|
||||
int n1 = jv_array_length(jv_copy(jq->path));
|
||||
jq->path = jv_array_append(jq->path, component);
|
||||
int n2 = jv_array_length(jv_copy(jq->path));
|
||||
assert(n2 == n1 + 1);
|
||||
jv_free(jq->value_at_path);
|
||||
jq->value_at_path = value_at_path;
|
||||
} else {
|
||||
jv_free(component);
|
||||
jv_free(value_at_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +268,8 @@ uint16_t* stack_restore(jq_state *jq){
|
||||
} else {
|
||||
assert(path_len == 0);
|
||||
}
|
||||
jv_free(jq->value_at_path);
|
||||
jq->value_at_path = fork->value_at_path;
|
||||
jq->subexp_nest = fork->subexp_nest;
|
||||
jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));
|
||||
return retaddr;
|
||||
@@ -271,6 +288,8 @@ static void jq_reset(jq_state *jq) {
|
||||
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
|
||||
jv_free(jq->path);
|
||||
jq->path = jv_null();
|
||||
jv_free(jq->value_at_path);
|
||||
jq->value_at_path = jv_null();
|
||||
jq->subexp_nest = 0;
|
||||
}
|
||||
|
||||
@@ -536,17 +555,29 @@ jv jq_next(jq_state *jq) {
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
|
||||
stack_push(jq, jv_number(jq->subexp_nest));
|
||||
stack_push(jq, v);
|
||||
stack_push(jq, jq->value_at_path);
|
||||
stack_push(jq, jv_copy(v));
|
||||
|
||||
jq->path = jv_array();
|
||||
jq->value_at_path = v; // next INDEX operation must index into v
|
||||
jq->subexp_nest = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case PATH_END: {
|
||||
jv v = stack_pop(jq);
|
||||
// detect invalid path expression like path(.a | reverse)
|
||||
if (!path_intact(jq, jv_copy(v))) {
|
||||
char errbuf[30];
|
||||
jv msg = jv_string_fmt(
|
||||
"Invalid path expression with result %s",
|
||||
jv_dump_string_trunc(v, errbuf, sizeof(errbuf)));
|
||||
set_error(jq, jv_invalid_with_msg(msg));
|
||||
goto do_backtrack;
|
||||
}
|
||||
jv_free(v); // discard value, only keep path
|
||||
|
||||
jv old_value_at_path = stack_pop(jq);
|
||||
int old_subexp_nest = (int)jv_number_value(stack_pop(jq));
|
||||
|
||||
jv path = jq->path;
|
||||
@@ -558,6 +589,8 @@ jv jq_next(jq_state *jq) {
|
||||
|
||||
stack_push(jq, path);
|
||||
jq->subexp_nest = old_subexp_nest;
|
||||
jv_free(jq->value_at_path);
|
||||
jq->value_at_path = old_value_at_path;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -572,11 +605,23 @@ jv jq_next(jq_state *jq) {
|
||||
case INDEX_OPT: {
|
||||
jv t = stack_pop(jq);
|
||||
jv k = stack_pop(jq);
|
||||
path_append(jq, jv_copy(k));
|
||||
jv v = jv_get(t, k);
|
||||
// detect invalid path expression like path(reverse | .a)
|
||||
if (!path_intact(jq, jv_copy(t))) {
|
||||
char keybuf[15];
|
||||
char objbuf[30];
|
||||
jv msg = jv_string_fmt(
|
||||
"Invalid path expression near attempt to access element %s of %s",
|
||||
jv_dump_string_trunc(k, keybuf, sizeof(keybuf)),
|
||||
jv_dump_string_trunc(t, objbuf, sizeof(objbuf)));
|
||||
set_error(jq, jv_invalid_with_msg(msg));
|
||||
goto do_backtrack;
|
||||
}
|
||||
jv v = jv_get(t, jv_copy(k));
|
||||
if (jv_is_valid(v)) {
|
||||
path_append(jq, k, jv_copy(v));
|
||||
stack_push(jq, v);
|
||||
} else {
|
||||
jv_free(k);
|
||||
if (opcode == INDEX)
|
||||
set_error(jq, v);
|
||||
else
|
||||
@@ -605,9 +650,21 @@ jv jq_next(jq_state *jq) {
|
||||
}
|
||||
|
||||
case EACH:
|
||||
case EACH_OPT:
|
||||
case EACH_OPT: {
|
||||
jv container = stack_pop(jq);
|
||||
// detect invalid path expression like path(reverse | .[])
|
||||
if (!path_intact(jq, jv_copy(container))) {
|
||||
char errbuf[30];
|
||||
jv msg = jv_string_fmt(
|
||||
"Invalid path expression near attempt to iterate through %s",
|
||||
jv_dump_string_trunc(container, errbuf, sizeof(errbuf)));
|
||||
set_error(jq, jv_invalid_with_msg(msg));
|
||||
goto do_backtrack;
|
||||
}
|
||||
stack_push(jq, container);
|
||||
stack_push(jq, jv_number(-1));
|
||||
// fallthrough
|
||||
}
|
||||
case ON_BACKTRACK(EACH):
|
||||
case ON_BACKTRACK(EACH_OPT): {
|
||||
int idx = jv_number_value(stack_pop(jq));
|
||||
@@ -653,14 +710,14 @@ jv jq_next(jq_state *jq) {
|
||||
} else if (is_last) {
|
||||
// we don't need to make a backtrack point
|
||||
jv_free(container);
|
||||
path_append(jq, key);
|
||||
path_append(jq, key, jv_copy(value));
|
||||
stack_push(jq, value);
|
||||
} else {
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_push(jq, container);
|
||||
stack_push(jq, jv_number(idx));
|
||||
stack_save(jq, pc - 1, spos);
|
||||
path_append(jq, key);
|
||||
path_append(jq, key, jv_copy(value));
|
||||
stack_push(jq, value);
|
||||
}
|
||||
break;
|
||||
@@ -880,6 +937,7 @@ jq_state *jq_init(void) {
|
||||
|
||||
jq->attrs = jv_object();
|
||||
jq->path = jv_null();
|
||||
jq->value_at_path = jv_null();
|
||||
return jq;
|
||||
}
|
||||
|
||||
|
||||
31
jv.c
31
jv.c
@@ -137,10 +137,7 @@ static void jvp_invalid_free(jv x) {
|
||||
*/
|
||||
|
||||
jv jv_number(double x) {
|
||||
jv j;
|
||||
j.kind_flags = JV_KIND_NUMBER;
|
||||
j.size = 0;
|
||||
j.u.number = x;
|
||||
jv j = {JV_KIND_NUMBER, 0, 0, 0, {.number = x}};
|
||||
return j;
|
||||
}
|
||||
|
||||
@@ -1291,6 +1288,32 @@ int jv_equal(jv a, jv b) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int jv_identical(jv a, jv b) {
|
||||
int r;
|
||||
if (a.kind_flags != b.kind_flags
|
||||
|| a.offset != b.offset
|
||||
|| a.size != b.size) {
|
||||
r = 0;
|
||||
} else {
|
||||
switch (jv_get_kind(a)) {
|
||||
case JV_KIND_ARRAY:
|
||||
case JV_KIND_STRING:
|
||||
case JV_KIND_OBJECT:
|
||||
r = a.u.ptr == b.u.ptr;
|
||||
break;
|
||||
case JV_KIND_NUMBER:
|
||||
r = a.u.number == b.u.number;
|
||||
break;
|
||||
default:
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return r;
|
||||
}
|
||||
|
||||
int jv_contains(jv a, jv b) {
|
||||
int r = 1;
|
||||
if (jv_get_kind(a) != jv_get_kind(b)) {
|
||||
|
||||
1
jv.h
1
jv.h
@@ -46,6 +46,7 @@ void jv_free(jv);
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
int jv_equal(jv, jv);
|
||||
int jv_identical(jv, jv);
|
||||
int jv_contains(jv, jv);
|
||||
|
||||
jv jv_invalid(void);
|
||||
|
||||
@@ -695,6 +695,26 @@ path(.)
|
||||
42
|
||||
[]
|
||||
|
||||
try path(.a | map(select(.b == 0))) catch .
|
||||
{"a":[{"b":0}]}
|
||||
"Invalid path expression with result [{\"b\":0}]"
|
||||
|
||||
try path(.a | map(select(.b == 0)) | .[0]) catch .
|
||||
{"a":[{"b":0}]}
|
||||
"Invalid path expression near attempt to access element 0 of [{\"b\":0}]"
|
||||
|
||||
try path(.a | map(select(.b == 0)) | .c) catch .
|
||||
{"a":[{"b":0}]}
|
||||
"Invalid path expression near attempt to access element \"c\" of [{\"b\":0}]"
|
||||
|
||||
try path(.a | map(select(.b == 0)) | .[]) catch .
|
||||
{"a":[{"b":0}]}
|
||||
"Invalid path expression near attempt to iterate through [{\"b\":0}]"
|
||||
|
||||
path(.a[path(.b)[0]])
|
||||
{"a":{"b":0}}
|
||||
["a","b"]
|
||||
|
||||
[paths]
|
||||
[1,[[],{"a":2}]]
|
||||
[[0],[1],[1,0],[1,1],[1,1,"a"]]
|
||||
@@ -783,6 +803,22 @@ def inc(x): x |= .+1; inc(.[].a)
|
||||
{"foo":[11], "bar":42}
|
||||
{"foo":[11,null,{"bar":1}], "bar":42}
|
||||
|
||||
try ((map(select(.a == 1))[].b) = 10) catch .
|
||||
[{"a":0},{"a":1}]
|
||||
"Invalid path expression near attempt to iterate through [{\"a\":1}]"
|
||||
|
||||
try ((map(select(.a == 1))[].a) |= .+1) catch .
|
||||
[{"a":0},{"a":1}]
|
||||
"Invalid path expression near attempt to iterate through [{\"a\":1}]"
|
||||
|
||||
def x: .[1,2]; x=10
|
||||
[0,1,2]
|
||||
[0,10,10]
|
||||
|
||||
try (def x: reverse; x=10) catch .
|
||||
[0,1,2]
|
||||
"Invalid path expression with result [2,1,0]"
|
||||
|
||||
#
|
||||
# Conditionals
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user