base: Implement JSON string escaping
We encode JSON in the KDC's audit logs, and soon in bx509d's /get-tgts. Therefore, we should be reasonable in terms of what we encode.
This commit is contained in:
@@ -244,26 +244,266 @@ test_json(void)
|
||||
};
|
||||
char *s;
|
||||
size_t i, k;
|
||||
heim_object_t o, o2;
|
||||
heim_object_t o, o2, o3;
|
||||
heim_string_t k1 = heim_string_create("k1");
|
||||
|
||||
o = heim_json_create("\"string\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/*
|
||||
* Test string escaping:
|
||||
*
|
||||
* - C-like must-escapes
|
||||
* - ASCII control character must-escapes
|
||||
* - surrogate pairs
|
||||
*
|
||||
* We test round-tripping. First we parse, then we serialize, then parse,
|
||||
* then compare the second parse to the first for equality.
|
||||
*
|
||||
* We do compare serialized forms in spite of their not being canonical.
|
||||
* That means that some changes to serialization can cause failures here.
|
||||
*/
|
||||
o = heim_json_create("\""
|
||||
"\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
|
||||
"\x1e" /* ASCII control character w/o C-like escape */
|
||||
"\\u00e1" /* á */
|
||||
"\\u07ff"
|
||||
"\\u0801"
|
||||
"\\u8001"
|
||||
"\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp(
|
||||
"\b\f\n\r\t"
|
||||
"\x1e"
|
||||
"\xc3\xa1"
|
||||
"\xdf\xbf"
|
||||
"\xe0\xA0\x81"
|
||||
"\xe8\x80\x81"
|
||||
"\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
|
||||
heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("\""
|
||||
"\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
|
||||
"\x1e" /* ASCII control character w/o C-like escape */
|
||||
"\xc3\xa1"
|
||||
"\xdf\xbf"
|
||||
"\xe0\xa0\x81"
|
||||
"\xE8\x80\x81"
|
||||
"\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp(
|
||||
"\b\f\n\r\t"
|
||||
"\x1e"
|
||||
"\xc3\xa1"
|
||||
"\xdf\xbf"
|
||||
"\xe0\xA0\x81"
|
||||
"\xe8\x80\x81"
|
||||
"\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
|
||||
heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test rejection of unescaped ASCII control characters */
|
||||
o = heim_json_create("\"\b\\f\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "strict parse accepted bad input");
|
||||
o = heim_json_create("\"\b\x1e\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "strict parse accepted bad input");
|
||||
|
||||
o = heim_json_create("\"\b\\f\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("\b\f", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\"\\b\\f\"", heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test bogus backslash escape */
|
||||
o = heim_json_create("\""
|
||||
"\\ "
|
||||
"\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "malformed string accepted");
|
||||
o = heim_json_create("\""
|
||||
"\\ "
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "malformed string rejected (not strict)");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp(" ", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\" \"", heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test truncated surrogate encoding (bottom code unit) */
|
||||
o = heim_json_create("\""
|
||||
"\xE8\x80\x81"
|
||||
"\\uD834\\udd"
|
||||
"\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "malformed string accepted");
|
||||
o = heim_json_create("\""
|
||||
"\xE8\x80\x81"
|
||||
"\\uD834\\udd"
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "malformed string rejected (not strict)");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp(
|
||||
"\xe8\x80\x81"
|
||||
"\\uD834\\udd", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\"老\\\\uD834\\\\udd\"",
|
||||
heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test truncated surrogate encodings (top code unit) */
|
||||
o = heim_json_create("\""
|
||||
"\xE8\x80\x81"
|
||||
"\\uD83"
|
||||
"\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "malformed string accepted");
|
||||
o = heim_json_create("\""
|
||||
"\xE8\x80\x81"
|
||||
"\\uD83"
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "malformed string rejected (not strict)");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp(
|
||||
"\xe8\x80\x81"
|
||||
"\\uD83", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(strcmp("\"老\\\\uD83\"",
|
||||
heim_string_get_utf8(o2)) == 0,
|
||||
"JSON encoding changed; please check that it is till valid");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/*
|
||||
* Test handling of truncated UTF-8 multi-byte sequences.
|
||||
*/
|
||||
o = heim_json_create("\""
|
||||
"\xE8\x80"
|
||||
"\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "malformed string rejected (not strict)");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("\xe8\x80",
|
||||
heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o2 == NULL, "malformed string serialized");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o3 == NULL, "malformed string accepted (not strict)");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(strcmp("\xe8\x80",
|
||||
heim_string_get_utf8(o3)) == 0, "wrong string");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test handling of unescaped / embedded newline */
|
||||
o = heim_json_create("\"\n\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o == NULL, "malformed string accepted (strict)");
|
||||
o = heim_json_create("\"\n\"", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "malformed string rejected (not strict)");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("\n", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o2 != NULL, "string not serialized");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o3 != NULL, "string not accepted");
|
||||
heim_assert(strcmp("\n", heim_string_get_utf8(o3)) == 0, "wrong string");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/* Test handling of embedded NULs (must decode as data, not string) */
|
||||
o = heim_json_create("\"\\u0000\"", 10, HEIM_JSON_F_STRICT, NULL);
|
||||
heim_assert(o != NULL, "string with NULs rejected");
|
||||
heim_assert(heim_get_tid(o) == heim_data_get_type_id(), "data-tid");
|
||||
heim_assert(heim_data_get_length(o) == 1, "wrong data length");
|
||||
heim_assert(((const char *)heim_data_get_ptr(o))[0] == '\0',
|
||||
"wrong data NUL");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
heim_assert(o2 != NULL, "data not serialized");
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10,
|
||||
HEIM_JSON_F_TRY_DECODE_DATA, NULL);
|
||||
heim_assert(o3 != NULL, "data not accepted");
|
||||
heim_assert(heim_data_get_length(o3) == 1, "wrong data length");
|
||||
heim_assert(((const char *)heim_data_get_ptr(o3))[0] == '\0',
|
||||
"wrong data NUL");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/*
|
||||
* Note that the trailing ']' is not part of the JSON text (which is just a
|
||||
* string).
|
||||
*/
|
||||
o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "string");
|
||||
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
|
||||
heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
/*
|
||||
* heim_json_eq() can't handle dicts with dicts as keys, so we don't check
|
||||
* for round-tripping here
|
||||
*/
|
||||
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
|
||||
"{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "dict");
|
||||
@@ -281,6 +521,11 @@ test_json(void)
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
|
||||
heim_release(o2);
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
|
||||
@@ -289,6 +534,11 @@ test_json(void)
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
|
||||
heim_release(o2);
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
|
||||
@@ -297,26 +547,51 @@ test_json(void)
|
||||
o2 = heim_dict_copy_value(o, k1);
|
||||
heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
|
||||
heim_release(o2);
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("-10", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "number");
|
||||
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create("99", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "number");
|
||||
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" [ 1 ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "array");
|
||||
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
o = heim_json_create(" [ -1 ]", 10, 0, NULL);
|
||||
heim_assert(o != NULL, "array");
|
||||
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
|
||||
for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
|
||||
@@ -325,6 +600,11 @@ test_json(void)
|
||||
fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
|
||||
return 1;
|
||||
}
|
||||
o2 = heim_json_copy_serialize(o, 0, NULL);
|
||||
o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
|
||||
heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
|
||||
heim_release(o3);
|
||||
heim_release(o2);
|
||||
heim_release(o);
|
||||
/* Simple fuzz test */
|
||||
for (k = strlen(j[i]) - 1; k > 0; k--) {
|
||||
|
Reference in New Issue
Block a user