Skip to content

Commit 4e91147

Browse files
committed
Fixing inproper escape handling of control characters in json library.
1 parent eb563d3 commit 4e91147

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

Release/src/json/json.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,11 @@ bool web::json::number::is_int64() const
340340

341341
bool web::json::details::_String::has_escape_chars(const _String &str)
342342
{
343-
static const auto escapes = U("\"\\\b\f\r\n\t");
344-
return str.m_string.find_first_of(escapes) != utility::string_t::npos;
343+
static const std::array<::utility::string_t::value_type, 33> escapes =
344+
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 0x00 - 0x1F */ '\"' };
345+
346+
// Provide the range otherwise find_first_of will think the first null terminator character is the end.
347+
return str.m_string.find_first_of(escapes.data(), 0, escapes.size()) != utility::string_t::npos;
345348
}
346349

347350
web::json::details::_Object::_Object(const _Object& other) :

Release/src/json/json_serialization.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ void web::json::value::format(std::basic_string<char>& string) const
7474
template<typename CharType>
7575
void web::json::details::append_escape_string(std::basic_string<CharType>& str, const std::basic_string<CharType>& escaped)
7676
{
77-
for (auto iter = escaped.begin(); iter != escaped.end(); ++iter)
77+
for (const auto &ch : escaped)
7878
{
79-
switch (*iter)
79+
switch (ch)
8080
{
8181
case '\"':
8282
str += '\\';
@@ -107,7 +107,22 @@ void web::json::details::append_escape_string(std::basic_string<CharType>& str,
107107
str += 't';
108108
break;
109109
default:
110-
str += *iter;
110+
111+
// If a control character then must unicode escaped.
112+
if (ch >= 0 && ch <= 0x1F)
113+
{
114+
static const std::array<CharType, 16> intToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
115+
str += '\\';
116+
str += 'u';
117+
str += '0';
118+
str += '0';
119+
str += intToHex[(ch & 0xF0) >> 4];
120+
str += intToHex[ch & 0x0F];
121+
}
122+
else
123+
{
124+
str += ch;
125+
}
111126
}
112127
}
113128
}

Release/tests/functional/json/parsing_tests.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
****/
2323

2424
#include "stdafx.h"
25+
2526
#include <array>
27+
#include <iomanip>
2628

2729
#if defined(_WIN32) || defined(__APPLE__)
2830
#include <regex>
@@ -221,6 +223,48 @@ TEST(escaped_unicode_string)
221223
VERIFY_PARSING_THROW(json::value::parse(U("\"\\u0klB\"")));
222224
}
223225

226+
TEST(escaping_control_characters)
227+
{
228+
for (int i = 0; i <= 0x1F; ++i)
229+
{
230+
::utility::stringstream_t ss;
231+
ss << U("\"\\u") << std::uppercase << std::setfill(U('0')) << std::setw(4) << std::hex << i << U("\"");
232+
const auto &str = ss.str();
233+
auto expectedStr = str;
234+
if (i == 0x08)
235+
{
236+
expectedStr = U("\"\\b\"");
237+
}
238+
else if (i == 0x09)
239+
{
240+
expectedStr = U("\"\\t\"");
241+
}
242+
else if (i == 0x0A)
243+
{
244+
expectedStr = U("\"\\n\"");
245+
}
246+
else if (i == 0x0C)
247+
{
248+
expectedStr = U("\"\\f\"");
249+
}
250+
else if (i == 0x0D)
251+
{
252+
expectedStr = U("\"\\r\"");
253+
}
254+
255+
// Try constructing a json string value directly.
256+
::utility::string_t schar;
257+
schar.push_back(static_cast<::utility::string_t::value_type>(i));
258+
const auto &sv = json::value::string(schar);
259+
VERIFY_ARE_EQUAL(expectedStr, sv.serialize());
260+
261+
// Try parsing a string
262+
const auto &v = json::value::parse(str);
263+
VERIFY_IS_TRUE(v.is_string());
264+
VERIFY_ARE_EQUAL(expectedStr, v.serialize());
265+
}
266+
}
267+
224268
TEST(comments_string)
225269
{
226270
// Nothing but a comment

0 commit comments

Comments
 (0)