Skip to content

Commit 24faf84

Browse files
authored
Merge pull request #1023 from kiwix/suggestions_with_control_characters
Control characters are escaped in suggestions JSON
2 parents a959800 + 571c09e commit 24faf84

File tree

2 files changed

+22
-20
lines changed

2 files changed

+22
-20
lines changed

src/tools/otherTools.cpp

+13-11
Original file line numberDiff line numberDiff line change
@@ -330,17 +330,19 @@ std::string kiwix::render_template(const std::string& template_str, kainjow::mus
330330
namespace
331331
{
332332

333-
std::string escapeBackslashes(const std::string& s)
333+
std::string escapeForJSON(const std::string& s)
334334
{
335-
std::string es;
336-
es.reserve(s.size());
335+
std::ostringstream oss;
337336
for (char c : s) {
338337
if ( c == '\\' ) {
339-
es.push_back('\\');
338+
oss << "\\\\";
339+
} else if ( unsigned(c) < 0x20U ) {
340+
oss << "\\u" << std::setw(4) << std::setfill('0') << unsigned(c);
341+
} else {
342+
oss << c;
340343
}
341-
es.push_back(c);
342344
}
343-
return es;
345+
return oss.str();
344346
}
345347

346348
std::string makeFulltextSearchSuggestion(const std::string& lang,
@@ -368,10 +370,10 @@ void kiwix::Suggestions::add(const zim::SuggestionItem& suggestion)
368370
? suggestion.getSnippet()
369371
: suggestion.getTitle();
370372

371-
result.set("label", escapeBackslashes(label));
372-
result.set("value", escapeBackslashes(suggestion.getTitle()));
373+
result.set("label", escapeForJSON(label));
374+
result.set("value", escapeForJSON(suggestion.getTitle()));
373375
result.set("kind", "path");
374-
result.set("path", escapeBackslashes(suggestion.getPath()));
376+
result.set("path", escapeForJSON(suggestion.getPath()));
375377
result.set("first", m_data.is_empty_list());
376378
m_data.push_back(result);
377379
}
@@ -381,8 +383,8 @@ void kiwix::Suggestions::addFTSearchSuggestion(const std::string& uiLang,
381383
{
382384
kainjow::mustache::data result;
383385
const std::string label = makeFulltextSearchSuggestion(uiLang, queryString);
384-
result.set("label", escapeBackslashes(label));
385-
result.set("value", escapeBackslashes(queryString + " "));
386+
result.set("label", escapeForJSON(label));
387+
result.set("value", escapeForJSON(queryString + " "));
386388
result.set("kind", "pattern");
387389
result.set("first", m_data.is_empty_list());
388390
m_data.push_back(result);

test/otherTools.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ TEST(Suggestions, specialCharHandling)
100100
{
101101
// HTML special symbols (<, >, &, ", and ') must be HTML-escaped
102102
// Backslash symbols (\) must be duplicated.
103-
const std::string SYMBOLS(R"(\<>&'"~!@#$%^*()_+`-=[]{}|:;,.?)");
103+
const std::string SYMBOLS("\t\n\r" R"(\<>&'"~!@#$%^*()_+`-=[]{}|:;,.?)");
104104
{
105105
kiwix::Suggestions s;
106106
s.add(zim::SuggestionItem("Title with " + SYMBOLS,
@@ -110,10 +110,10 @@ TEST(Suggestions, specialCharHandling)
110110
CHECK_SUGGESTIONS(s.getJSON(),
111111
R"EXPECTEDJSON([
112112
{
113-
"value" : "Title with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
114-
"label" : "Snippet with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
113+
"value" : "Title with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
114+
"label" : "Snippet with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
115115
"kind" : "path"
116-
, "path" : "Path with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?"
116+
, "path" : "Path with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?"
117117
}
118118
]
119119
)EXPECTEDJSON"
@@ -128,10 +128,10 @@ R"EXPECTEDJSON([
128128
CHECK_SUGGESTIONS(s.getJSON(),
129129
R"EXPECTEDJSON([
130130
{
131-
"value" : "Snippetless title with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
132-
"label" : "Snippetless title with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
131+
"value" : "Snippetless title with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
132+
"label" : "Snippetless title with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?",
133133
"kind" : "path"
134-
, "path" : "Path with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?"
134+
, "path" : "Path with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?"
135135
}
136136
]
137137
)EXPECTEDJSON"
@@ -145,8 +145,8 @@ R"EXPECTEDJSON([
145145
CHECK_SUGGESTIONS(s.getJSON(),
146146
R"EXPECTEDJSON([
147147
{
148-
"value" : "text with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.? ",
149-
"label" : "containing &apos;text with \\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?&apos;...",
148+
"value" : "text with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.? ",
149+
"label" : "containing &apos;text with \u0009\u0010\u0013\\&lt;&gt;&amp;&apos;&quot;~!@#$%^*()_+`-=[]{}|:;,.?&apos;...",
150150
"kind" : "pattern"
151151
//EOLWHITESPACEMARKER
152152
}

0 commit comments

Comments
 (0)