util/Exception: sanitize message strings
This should prevent leaking unsanitized strings from libraries.
This commit is contained in:
parent
abb23ba894
commit
7b938b4d14
|
@ -2,9 +2,40 @@
|
||||||
// author: Max Kellermann <max.kellermann@gmail.com>
|
// author: Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
|
||||||
#include "Exception.hxx"
|
#include "Exception.hxx"
|
||||||
|
#include "CharUtil.hxx"
|
||||||
|
#include "StringStrip.hxx"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the given C string to a std::string, with some
|
||||||
|
* sanitizations: strip whitespace at the beginning and the end and
|
||||||
|
* combine multiple whitespace to a single space.
|
||||||
|
*
|
||||||
|
* This shall compress strangely formatted multi-line messages (which
|
||||||
|
* may come from third-party libraries) to a single line.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendSanitize(std::string &dest, const char *src) noexcept
|
||||||
|
{
|
||||||
|
src = StripLeft(src);
|
||||||
|
|
||||||
|
bool space = false;
|
||||||
|
while (char ch = *src++) {
|
||||||
|
if (IsWhitespaceFast(ch)) {
|
||||||
|
space = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (space) {
|
||||||
|
space = false;
|
||||||
|
dest.push_back(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.push_back(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void
|
static void
|
||||||
AppendNestedMessage(std::string &result, T &&e,
|
AppendNestedMessage(std::string &result, T &&e,
|
||||||
|
@ -14,13 +45,13 @@ AppendNestedMessage(std::string &result, T &&e,
|
||||||
std::rethrow_if_nested(std::forward<T>(e));
|
std::rethrow_if_nested(std::forward<T>(e));
|
||||||
} catch (const std::exception &nested) {
|
} catch (const std::exception &nested) {
|
||||||
result += separator;
|
result += separator;
|
||||||
result += nested.what();
|
AppendSanitize(result, nested.what());
|
||||||
AppendNestedMessage(result, nested, fallback, separator);
|
AppendNestedMessage(result, nested, fallback, separator);
|
||||||
} catch (const std::nested_exception &ne) {
|
} catch (const std::nested_exception &ne) {
|
||||||
AppendNestedMessage(result, ne, fallback, separator);
|
AppendNestedMessage(result, ne, fallback, separator);
|
||||||
} catch (const char *s) {
|
} catch (const char *s) {
|
||||||
result += separator;
|
result += separator;
|
||||||
result += s;
|
AppendSanitize(result, s);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
result += separator;
|
result += separator;
|
||||||
result += fallback;
|
result += fallback;
|
||||||
|
@ -31,7 +62,8 @@ std::string
|
||||||
GetFullMessage(const std::exception &e,
|
GetFullMessage(const std::exception &e,
|
||||||
const char *fallback, const char *separator) noexcept
|
const char *fallback, const char *separator) noexcept
|
||||||
{
|
{
|
||||||
std::string result = e.what();
|
std::string result;
|
||||||
|
AppendSanitize(result, e.what());
|
||||||
AppendNestedMessage(result, e, fallback, separator);
|
AppendNestedMessage(result, e, fallback, separator);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using std::string_view_literals::operator""sv;
|
||||||
|
|
||||||
TEST(ExceptionTest, RuntimeError)
|
TEST(ExceptionTest, RuntimeError)
|
||||||
{
|
{
|
||||||
ASSERT_EQ(GetFullMessage(std::make_exception_ptr(std::runtime_error("Foo"))), "Foo");
|
ASSERT_EQ(GetFullMessage(std::make_exception_ptr(std::runtime_error("Foo"))), "Foo");
|
||||||
|
@ -22,6 +24,22 @@ TEST(ExceptionTest, DerivedError)
|
||||||
ASSERT_EQ(GetFullMessage(std::make_exception_ptr(DerivedError("Foo"))), "Foo");
|
ASSERT_EQ(GetFullMessage(std::make_exception_ptr(DerivedError("Foo"))), "Foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ExceptionTest, GetFullMessageSanitize)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
throw " ABC \n DEF ";
|
||||||
|
} catch (...) {
|
||||||
|
std::throw_with_nested(std::runtime_error{"foo\r\n\tbar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
FAIL();
|
||||||
|
} catch (...) {
|
||||||
|
ASSERT_EQ(GetFullMessage(std::current_exception()),
|
||||||
|
"foo bar; ABC DEF"sv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ExceptionTest, FindNestedDirect)
|
TEST(ExceptionTest, FindNestedDirect)
|
||||||
{
|
{
|
||||||
struct Foo {};
|
struct Foo {};
|
||||||
|
|
Loading…
Reference in New Issue