From 73111cf2f0f007e9e709a6125462eb40eb3f6c6c Mon Sep 17 00:00:00 2001 From: Everett Badeaux Date: Sun, 18 Oct 2026 23:14:04 -0500 Subject: [PATCH] Update Sanitizer --- libs/meter/meter_id/meter_id.cpp | 58 +++++++++++++++++----- libs/meter/meter_id/meter_id.h | 1 - libs/meter/meter_id/test_meter_id.cpp | 18 +++++++ libs/utils/src/util.cpp | 1 + libs/writer/writer_wrapper/test_writer.cpp | 1 + spectator/registry.cpp | 2 + 6 files changed, 68 insertions(+), 13 deletions(-) diff --git a/libs/meter/meter_id/meter_id.cpp b/libs/meter/meter_id/meter_id.cpp index 32c1a73..b266556 100644 --- a/libs/meter/meter_id/meter_id.cpp +++ b/libs/meter/meter_id/meter_id.cpp @@ -1,12 +1,39 @@ #include #include + +#include #include namespace spectator { -// Define the static member -const std::regex INVALID_CHARS("[^-._A-Za-z0-9~^]"); +namespace { + +// spectatord accepts only -._A-Za-z0-9~^ in names, tag keys and tag values; every other byte becomes '_'. +// Built at compile time so sanitizing is one table lookup per byte (this replaces a std::regex_replace per string). +constexpr std::array MakeValidCharTable() +{ + std::array table{}; + for (char c = 'A'; c <= 'Z'; ++c) table[static_cast(c)] = true; + for (char c = 'a'; c <= 'z'; ++c) table[static_cast(c)] = true; + for (char c = '0'; c <= '9'; ++c) table[static_cast(c)] = true; + for (const char c : {'-', '.', '_', '~', '^'}) table[static_cast(c)] = true; + return table; +} + +constexpr std::array kValidChars = MakeValidCharTable(); + +// Appends src to dest with invalid characters replaced by '_'. Grows dest once instead of once per character. +void AppendSanitized(std::string& dest, const std::string& src) +{ + const size_t start = dest.size(); + dest.resize(start + src.size()); + char* out = dest.data() + start; + for (const char c : src) + { + *out++ = kValidChars[static_cast(c)] ? c : '_'; + } +} std::unordered_map ValidateTags(const std::unordered_map& tags) { @@ -22,22 +49,29 @@ std::unordered_map ValidateTags(const std::unordered_m return validTags; } -std::string ReplaceInvalidChars(const std::string& s) { return std::regex_replace(s, INVALID_CHARS, "_"); } - std::string ToSpectatorId(const std::string& name, const std::unordered_map& tags) { - std::ostringstream ss; - ss << ReplaceInvalidChars(name); - if (!tags.empty()) + size_t size = name.size(); + for (const auto& [key, value] : tags) { - for (const auto& [fst, snd] : tags) - { - ss << "," << ReplaceInvalidChars(fst) << "=" << ReplaceInvalidChars(snd); - } + size += key.size() + value.size() + 2; // ',' and '=' } - return ss.str(); + + std::string id; + id.reserve(size); + AppendSanitized(id, name); + for (const auto& [key, value] : tags) + { + id += ','; + AppendSanitized(id, key); + id += '='; + AppendSanitized(id, value); + } + return id; } +} // namespace + MeterId::MeterId(const std::string& name, const std::unordered_map& tags) : m_name(name), m_tags(ValidateTags(tags)) { diff --git a/libs/meter/meter_id/meter_id.h b/libs/meter/meter_id/meter_id.h index 22f133d..4eacc34 100644 --- a/libs/meter/meter_id/meter_id.h +++ b/libs/meter/meter_id/meter_id.h @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/libs/meter/meter_id/test_meter_id.cpp b/libs/meter/meter_id/test_meter_id.cpp index d672af2..8bc9110 100644 --- a/libs/meter/meter_id/test_meter_id.cpp +++ b/libs/meter/meter_id/test_meter_id.cpp @@ -31,6 +31,24 @@ TEST(MeterIdTest, IllegalCharsAreReplaced) EXPECT_EQ("test______^____-_~______________.___foo", id.GetSpectatordId()); } +TEST(MeterIdTest, IllegalCharsInTagsAreReplaced) +{ + const MeterId id("foo", {{"a b", "c/d"}}); + EXPECT_EQ("foo,a_b=c_d", id.GetSpectatordId()); +} + +TEST(MeterIdTest, ValidCharsAreKept) +{ + const MeterId id("azAZ09-._~^"); + EXPECT_EQ("azAZ09-._~^", id.GetSpectatordId()); +} + +TEST(MeterIdTest, EachNonAsciiByteIsReplaced) +{ + const MeterId id("caf\xc3\xa9"); + EXPECT_EQ("caf__", id.GetSpectatordId()); +} + TEST(MeterIdTest, LookupTags) { const MeterId id1("foo", {{"a", "1"}, {"b", "2"}, {"c", "3"}}); diff --git a/libs/utils/src/util.cpp b/libs/utils/src/util.cpp index 1761350..972b853 100644 --- a/libs/utils/src/util.cpp +++ b/libs/utils/src/util.cpp @@ -1,5 +1,6 @@ #include +#include #include #include diff --git a/libs/writer/writer_wrapper/test_writer.cpp b/libs/writer/writer_wrapper/test_writer.cpp index 68d743a..f6a8f37 100644 --- a/libs/writer/writer_wrapper/test_writer.cpp +++ b/libs/writer/writer_wrapper/test_writer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "../writer_types/test_utils/uds_server/uds_server.h" diff --git a/spectator/registry.cpp b/spectator/registry.cpp index 9f37f79..954f759 100644 --- a/spectator/registry.cpp +++ b/spectator/registry.cpp @@ -4,6 +4,8 @@ // header keeps spdlog and the spectator::Logger name out of consumers. #include +#include + namespace spectator { void SetLogLevel(spdlog::level::level_enum level)