diff --git a/CHANGES b/CHANGES index cf4c7f9..7807904 100644 --- a/CHANGES +++ b/CHANGES @@ -16,7 +16,8 @@ Released XXXX-XX-XX `json_string_setn()` and `json_string_setn_nocheck()`, and a function for getting string's length `json_string_length()`. - - Support ``\u0000`` escapes in the decoder. + - Support ``\u0000`` escapes in the decoder. The support can be + enabled by using the ``JSON_ALLOW_NUL`` decoding flag. * Bug fixes: diff --git a/src/jansson.h b/src/jansson.h index e76d6c3..27c0c74 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -244,6 +244,7 @@ json_t *json_deep_copy(const json_t *value); #define JSON_DISABLE_EOF_CHECK 0x2 #define JSON_DECODE_ANY 0x4 #define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); diff --git a/src/load.c b/src/load.c index 894ee10..2adaee8 100644 --- a/src/load.c +++ b/src/load.c @@ -802,7 +802,17 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) switch(lex->token) { case TOKEN_STRING: { - json = jsonp_stringn_nocheck_own(lex->value.string.val, lex->value.string.len); + const char *value = lex->value.string.val; + size_t len = lex->value.string.len; + + if(!(flags & JSON_ALLOW_NUL)) { + if(memchr(value, '\0', len)) { + error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL"); + return NULL; + } + } + + json = jsonp_stringn_nocheck_own(value, len); if(json) { lex->value.string.val = NULL; lex->value.string.len = 0; diff --git a/test/suites/api/test_dump.c b/test/suites/api/test_dump.c index 64c0863..89e73a7 100644 --- a/test/suites/api/test_dump.c +++ b/test/suites/api/test_dump.c @@ -180,6 +180,20 @@ static void escape_slashes() json_decref(json); } +static void encode_nul_byte() +{ + json_t *json; + char *result; + + json = json_stringn("nul byte \0 in string", 20); + result = json_dumps(json, JSON_ENCODE_ANY); + if(!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27)) + fail("json_dumps failed to dump an embedded NUL byte"); + + free(result); + json_decref(json); +} + static void run_tests() { encode_null(); @@ -187,4 +201,5 @@ static void run_tests() circular_references(); encode_other_than_array_or_object(); escape_slashes(); + encode_nul_byte(); } diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 944b4d8..eb52323 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -115,6 +115,26 @@ static void decode_int_as_real() #endif } +static void allow_nul() +{ + const char *text = "\"nul byte \\u0000 in string\""; + const char *expected = "nul byte \0 in string"; + size_t len = 20; + json_t *json; + + json = json_loads(text, JSON_ALLOW_NUL | JSON_DECODE_ANY, NULL); + if(!json || !json_is_string(json)) + fail("unable to decode embedded NUL byte"); + + if(json_string_length(json) != len) + fail("decoder returned wrong string length"); + + if(memcmp(json_string_value(json), expected, len + 1)) + fail("decoder returned wrong string content"); + + json_decref(json); +} + static void load_wrong_args() { json_t *json; @@ -161,6 +181,7 @@ static void run_tests() disable_eof_check(); decode_any(); decode_int_as_real(); + allow_nul(); load_wrong_args(); position(); } diff --git a/test/suites/valid/escaped-null-byte-in-string/input b/test/suites/valid/escaped-null-byte-in-string/input deleted file mode 100644 index 20bc990..0000000 --- a/test/suites/valid/escaped-null-byte-in-string/input +++ /dev/null @@ -1 +0,0 @@ -["null char \u0000 in string"] \ No newline at end of file diff --git a/test/suites/valid/escaped-null-byte-in-string/output b/test/suites/valid/escaped-null-byte-in-string/output deleted file mode 100644 index 20bc990..0000000 --- a/test/suites/valid/escaped-null-byte-in-string/output +++ /dev/null @@ -1 +0,0 @@ -["null char \u0000 in string"] \ No newline at end of file