Alternate Json string protection

.. when embedding json in xml element attributes the string
   decoding mangles results.

   this is because of the way \ and " are handled at each
   pass, causing combinations to be lost early when decoding.

   This is largely a non-issue, except the user chart config
   is stored in perspectives.xml and worse, when embedded on
   an overview is encoded as .gcchart (xml) before being
   encoded in the perspective.xml.

   To avoid this issue, specifically with user charts and
   to leave all other json string encoding and decoding
   unaffected (e.g. ridefile.json) a new protect/unprotect
   method has been introduced.

   Utils::jsonprotect2() and Utils::jsonunprotect2() will
   still decode older style encoding for backwards
   compatibility, but now will translate \ and " in the
   encoded string with :sl: and :qu:

   This means that, for example, the following substitutions
   will be made:

   newline (\n) -> :sl:n
   cr (\r) -> :sl:r
   \ -> :sl:
   " -> :qu:

   If users add text with these characters (e.g. :sl:) then
   it will be decoded to a slash, but it is highly unlikely
   they will do this, and if they do, it should be harmless
   as these text strings aren't part of the the datafilter
   syntax.

Fixes #3996
This commit is contained in:
Mark Liversedge
2021-08-06 07:28:21 +01:00
parent 62012f1990
commit db9623a201
3 changed files with 59 additions and 17 deletions

View File

@@ -324,8 +324,8 @@ UserChart::settings() const
out << "{ ";
// chartinfo
out << "\"title\": \"" << Utils::jsonprotect(chartinfo.title) << "\",\n";
out << "\"description\": \"" << Utils::jsonprotect(chartinfo.description) << "\",\n";
out << "\"title\": \"" << Utils::jsonprotect2(chartinfo.title) << "\",\n";
out << "\"description\": \"" << Utils::jsonprotect2(chartinfo.description) << "\",\n";
out << "\"type\": " << chartinfo.type << ",\n";
out << "\"animate\": " << (chartinfo.animate ? "true" : "false") << ",\n";
out << "\"legendpos\": " << chartinfo.legendpos << ",\n";
@@ -346,11 +346,11 @@ UserChart::settings() const
// out as a json object in the "SERIES" array
out << "{ ";
out << "\"name\": \"" << Utils::jsonprotect(series.name) << "\", ";
out << "\"group\": \"" << Utils::jsonprotect(series.group) << "\", ";
out << "\"xname\": \"" << Utils::jsonprotect(series.xname) << "\", ";
out << "\"yname\": \"" << Utils::jsonprotect(series.yname) << "\", ";
out << "\"program\": \"" << Utils::jsonprotect(series.string1) << "\", ";
out << "\"name\": \"" << Utils::jsonprotect2(series.name) << "\", ";
out << "\"group\": \"" << Utils::jsonprotect2(series.group) << "\", ";
out << "\"xname\": \"" << Utils::jsonprotect2(series.xname) << "\", ";
out << "\"yname\": \"" << Utils::jsonprotect2(series.yname) << "\", ";
out << "\"program\": \"" << Utils::jsonprotect2(series.string1) << "\", ";
out << "\"line\": " << series.line << ", ";
out << "\"symbol\": " << series.symbol << ", ";
out << "\"size\": " << series.size << ", ";
@@ -377,7 +377,7 @@ UserChart::settings() const
// out as a json object in the AXES array
out << "{ ";
out << "\"name\": \"" << Utils::jsonprotect(axis.name) << "\", ";
out << "\"name\": \"" << Utils::jsonprotect2(axis.name) << "\", ";
out << "\"type\": " << axis.type << ", ";
out << "\"orientation\": " << axis.orientation << ", ";
out << "\"align\": " << axis.align << ", ";
@@ -411,8 +411,8 @@ UserChart::applySettings(QString x)
QJsonObject obj = doc.object();
// chartinfo
chartinfo.title = Utils::jsonunprotect(obj["title"].toString());
chartinfo.description = Utils::jsonunprotect(obj["description"].toString());
chartinfo.title = Utils::jsonunprotect2(obj["title"].toString());
chartinfo.description = Utils::jsonunprotect2(obj["description"].toString());
chartinfo.type = obj["type"].toInt();
chartinfo.animate = obj["animate"].toBool();
chartinfo.legendpos = obj["legendpos"].toInt();
@@ -433,11 +433,11 @@ UserChart::applySettings(QString x)
QJsonObject series=it.toObject();
GenericSeriesInfo add;
add.name = Utils::jsonunprotect(series["name"].toString());
add.group = Utils::jsonunprotect(series["group"].toString());
add.xname = Utils::jsonunprotect(series["xname"].toString());
add.yname = Utils::jsonunprotect(series["yname"].toString());
add.string1 = Utils::jsonunprotect(series["program"].toString());
add.name = Utils::jsonunprotect2(series["name"].toString());
add.group = Utils::jsonunprotect2(series["group"].toString());
add.xname = Utils::jsonunprotect2(series["xname"].toString());
add.yname = Utils::jsonunprotect2(series["yname"].toString());
add.string1 = Utils::jsonunprotect2(series["program"].toString());
add.line = series["line"].toInt();
add.symbol = series["symbol"].toInt();
add.size = series["size"].toDouble();
@@ -463,7 +463,7 @@ UserChart::applySettings(QString x)
QJsonObject axis=it.toObject();
GenericAxisInfo add;
add.name = Utils::jsonunprotect(axis["name"].toString());
add.name = Utils::jsonunprotect2(axis["name"].toString());
add.type = static_cast<GenericAxisInfo::AxisInfoType>(axis["type"].toInt());
add.orientation = static_cast<Qt::Orientation>(axis["orientation"].toInt());
add.align = static_cast<Qt::AlignmentFlag>(axis["align"].toInt());

View File

@@ -236,6 +236,47 @@ QString jsonunprotect(const QString &string)
return s;
}
// json protection with special handling for " and \
// used when embedding in xml
QString jsonprotect2(const QString &string)
{
QString s = string;
s.replace("\t", "\\t"); // tab
s.replace("\n", "\\n"); // newline
s.replace("\r", "\\r"); // carriage-return
s.replace("\b", "\\b"); // backspace
s.replace("\f", "\\f"); // formfeed
s.replace("/", "\\/"); // solidus
s.replace("\\", ":sl:"); // backslash
s.replace("\"", ":qu:"); // quote
// add a trailing space to avoid conflicting with GC special tokens
s += " ";
return s;
}
QString jsonunprotect2(const QString &string)
{
QString s = string;
// legacy- these should never be present
// but included to read older files
s.replace("\\\"", "\""); // quote
s.replace("\\\\", "\\"); // backslash
s.replace(":qu:", "\""); // quote
s.replace(":sl:", "\\"); // backslash
s.replace("\\t", "\t"); // tab
s.replace("\\n", "\n"); // newline
s.replace("\\r", "\r"); // carriage-return
s.replace("\\b", "\b"); // backspace
s.replace("\\f", "\f"); // formfeed
s.replace("\\/", "/"); // solidus
// those trailing spaces.
while (s.endsWith(" ")) s = s.mid(0,s.length()-1);
return s;
}
QStringList
searchPath(QString path, QString binary, bool isexec)
{
@@ -540,4 +581,3 @@ double myisinf(double x) { return isinf(x); } // math.h
double myisnan(double x) { return isnan(x); } // math.h
};

View File

@@ -42,6 +42,8 @@ namespace Utils
QString unescape(const QString &string); // simple string unescaping, used in datafilters
QString jsonprotect(const QString &buffer);
QString jsonunprotect(const QString &buffer);
QString jsonprotect2(const QString &buffer); // internal use- encodes / and " as :sl: :qu:
QString jsonunprotect2(const QString &buffer); // internal use- encodes / and " as :sl: :qu:
QStringList searchPath(QString path, QString binary, bool isexec=true);
QString removeDP(QString);
double number(QString);