Skip to content

Commit 065c8ee

Browse files
committed
Fixed control chars escaping in JSON serialization
Ticket: CFE-4616
1 parent 476b57f commit 065c8ee

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

libutils/json.c

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,14 @@ void JsonEncodeStringWriter(
10121012
WriterWriteChar(writer, 't');
10131013
break;
10141014
default:
1015-
WriterWriteChar(writer, *c);
1015+
if (CharIsPrintableAscii(*c))
1016+
{
1017+
WriterWriteChar(writer, *c);
1018+
}
1019+
else
1020+
{
1021+
WriterWriteF(writer, "\\u%04x", (unsigned char) *c);
1022+
}
10161023
}
10171024
}
10181025
}
@@ -1026,6 +1033,41 @@ char *JsonEncodeString(const char *const unescaped_string)
10261033
return StringWriterClose(writer);
10271034
}
10281035

1036+
static bool HexStringToChar(const char *hex_string, char *res)
1037+
{
1038+
assert(hex_string != NULL);
1039+
1040+
const int hex_len = 4;
1041+
if (strlen(hex_string) < hex_len)
1042+
{
1043+
return -1;
1044+
}
1045+
1046+
char tmp[hex_len + 1];
1047+
memcpy(tmp, hex_string, hex_len);
1048+
tmp[hex_len] = '\0';
1049+
1050+
for (int i = 0; i < hex_len; ++i)
1051+
{
1052+
if (!isdigit(tmp[i]) &&
1053+
!(tmp[i] >= 'A' && tmp[i] <= 'F') &&
1054+
!(tmp[i] >= 'a' && tmp[i] <= 'f'))
1055+
{
1056+
return -1;
1057+
}
1058+
}
1059+
1060+
char *end;
1061+
long c = strtol(tmp, &end, 16);
1062+
if ((*end != '\0') || (c > 255) || (c < 0))
1063+
{
1064+
return -1;
1065+
}
1066+
1067+
*res = (unsigned char) c;
1068+
return 0;
1069+
}
1070+
10291071
static void JsonDecodeStringWriter(
10301072
const char *const escaped_string, Writer *const w)
10311073
{
@@ -1062,6 +1104,21 @@ static void JsonDecodeStringWriter(
10621104
WriterWriteChar(w, '\t');
10631105
c++;
10641106
break;
1107+
case 'u':
1108+
if (c[2] == '\0')
1109+
{
1110+
break;
1111+
}
1112+
1113+
char d;
1114+
int err = HexStringToChar(c + 2, &d);
1115+
if (err == -1)
1116+
{
1117+
break;
1118+
}
1119+
WriterWriteF(w, "%c", (unsigned char) d);
1120+
c += 5;
1121+
break;
10651122
default:
10661123
WriterWriteChar(w, *c);
10671124
break;

tests/unit/json_test.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,33 @@ static void test_string_escape(void)
18461846
assert_json_strings_eq(
18471847
"Hello, world!\n'Blah', \"blah\".",
18481848
"Hello, world!\\n'Blah', \\\"blah\\\".");
1849+
assert_json_strings_eq(
1850+
"Hello blah",
1851+
"Hello \\u001bblah");
1852+
1853+
const char unescaped_short_hex[] = {
1854+
'B', 'l', 'a', 'h', '\\', 'u', '0', '0', '\0'};
1855+
const char escaped_short_hex[] = {
1856+
'B', 'l', 'a', 'h', '\\', '\\', 'u', '0', '0', '\0'};
1857+
assert_json_strings_eq(unescaped_short_hex, escaped_short_hex);
1858+
1859+
const char unescaped_invalid_u[] = {
1860+
'B', 'l', 'a', 'h', '\\', 'u', '\0'};
1861+
const char escaped_invalid_u[] = {
1862+
'B', 'l', 'a', 'h', '\\', '\\', 'u', '\0'};
1863+
assert_json_strings_eq(unescaped_invalid_u, escaped_invalid_u);
1864+
1865+
const char unescaped_valid_hex[] = {
1866+
'B', 'l', 'a', 'h', '\\', 'u', '0', '0', '1', 'b', '\0'};
1867+
const char escaped_valid_hex[] = {
1868+
'B', 'l', 'a', 'h', '\\', '\\', 'u', '0', '0', '1', 'b', '\0'};
1869+
assert_json_strings_eq(unescaped_valid_hex, escaped_valid_hex);
1870+
1871+
const char unescaped_invalid_hex[] = {
1872+
'B', 'l', 'a', 'h', '\\', 'u', '0', 'z', '1', 'b', '\0'};
1873+
const char escaped_invalid_hex[] = {
1874+
'B', 'l', 'a', 'h', '\\', '\\', 'u', '0', 'z', '1', 'b', '\0'};
1875+
assert_json_strings_eq(unescaped_invalid_hex, escaped_invalid_hex);
18491876
}
18501877

18511878
#define assert_json5_data_eq(_size, unescaped, escaped) \

0 commit comments

Comments
 (0)