Fix of SPE-1630 crash when referencing an invalid variable name

using the old placeholder parser syntax.
Also implemented unescaping of \r\n\"\\ in string syntax.
This commit is contained in:
Vojtech Bubnik 2023-04-06 10:40:07 +02:00
parent b1081d7ac3
commit 030cfaf4b3
2 changed files with 52 additions and 4 deletions

View File

@ -1,6 +1,7 @@
#include "PlaceholderParser.hpp"
#include "Exception.hpp"
#include "Flow.hpp"
#include "Utils.hpp"
#include <cstring>
#include <ctime>
#include <iomanip>
@ -204,6 +205,7 @@ namespace client
explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; }
explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(std::string &&s) : m_type(TYPE_STRING) { m_data.s = new std::string(std::move(s)); }
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
@ -904,10 +906,13 @@ namespace client
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
if (opt == nullptr) {
// Check whether the opt_key ends with '_'.
if (opt_key_str.back() == '_')
if (opt_key_str.back() == '_') {
opt_key_str.resize(opt_key_str.size() - 1);
opt = ctx->resolve_symbol(opt_key_str);
}
if (opt == nullptr)
ctx->throw_exception("Variable does not exist", opt_key);
}
if (! opt->is_vector())
ctx->throw_exception("Trying to index a scalar variable", opt_key);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
@ -1790,8 +1795,49 @@ namespace client
if (ctx->skipping()) {
out.reset();
out.it_range = it_range;
} else
out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end());
} else {
// Unescape the string, UTF-8 safe.
std::string s;
auto begin = std::next(it_range.begin());
auto end = std::prev(it_range.end());
assert(begin <= end);
{
// 1) Get the size of the string after unescaping.
size_t len = 0;
for (auto it = begin; it != end;) {
if (*it == '\\') {
if (++ it == end ||
(*it != 'r' && *it != 'n' && *it != '"' && *it != '\\'))
ctx->throw_exception("Invalid escape sequence", {std::prev(it), std::next(it) });
++ len;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
len += n;
it += n;
}
}
// and reserve the string.
s.reserve(len);
}
// 2) Copy & unescape the string.
for (auto it = begin; it != end;) {
if (*it == '\\') {
char c = *(++ it);
if (c == 'r')
c = '\r';
else if (c == 'n')
c = '\n';
s += c;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
s.append(&*it, n);
it += n;
}
}
out = expr(std::move(s), it_range.begin(), it_range.end());
}
}
static void expr_(expr &value, Iterator &end_pos, expr &out)
{ auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); }

View File

@ -44,6 +44,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); }
SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); }
SECTION("parsing string with escaped characters") { REQUIRE(parser.process("{\"hu\\nha\\\\\\\"ha\\\"\"}") == "hu\nha\\\"ha\""); }
// Test the math expressions.
SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); }