summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan.goodliffe@octal.co.uk>2026-03-27 11:43:18 +0000
committerDan Goodliffe <dan.goodliffe@octal.co.uk>2026-03-27 11:43:18 +0000
commit1e551e618a63c869fde6a4b327566b38696a5f45 (patch)
tree37df1211379db51e55e93c4519ea93d2e3b19d54
parent426a6a3d9309e3f7cf5fc89d6a687c7895785cc3 (diff)
downloadwebstat-0.3.1.tar.bz2
webstat-0.3.1.tar.xz
webstat-0.3.1.zip
Parse escaping in query stringswebstat-0.3.1
-rw-r--r--src/logTypes.cpp59
-rw-r--r--test/test-ingest.cpp11
2 files changed, 46 insertions, 24 deletions
diff --git a/src/logTypes.cpp b/src/logTypes.cpp
index 85c5f4b..d8b3a7e 100644
--- a/src/logTypes.cpp
+++ b/src/logTypes.cpp
@@ -1,10 +1,8 @@
#include "logTypes.hpp"
-namespace scn {
- scan_expected<typename ContextType::iterator>
- scanner<WebStat::QuotedString>::scan(WebStat::QuotedString & value, ContextType & ctx)
- {
- static constexpr auto BS_MAP = []() {
+namespace {
+ namespace {
+ constexpr auto BS_MAP = []() {
std::array<char, 128> map {};
map['f'] = '\f';
map['n'] = '\n';
@@ -16,18 +14,10 @@ namespace scn {
return map;
}();
- if (auto empty = scn::scan<>(ctx.range(), R"("")")) {
- return empty->begin();
- }
-
- auto simple = scn::scan<std::string>(ctx.range(), R"("{:[^\"]}")");
- if (simple) {
- value = std::move(simple->value());
- return simple->begin();
- }
-
- if (auto openQuote = scn::scan<>(ctx.range(), R"(")")) {
- ctx.advance_to(openQuote->begin());
+ scn::scan_expected<typename scn::ContextType::iterator>
+ parseEscapedString(std::string & value, scn::ContextType & ctx, const auto & start)
+ {
+ ctx.advance_to(start->begin());
while (true) {
if (auto closeQuote = scn::scan<>(ctx.range(), R"(")")) {
return closeQuote->begin();
@@ -45,9 +35,30 @@ namespace scn {
ctx.advance_to(escaped->begin());
}
else {
- return unexpected(simple.error());
+ return scn::unexpected(start.error());
}
}
+ return scn::unexpected(start.error());
+ }
+ }
+}
+
+namespace scn {
+ scan_expected<typename ContextType::iterator>
+ scanner<WebStat::QuotedString>::scan(WebStat::QuotedString & value, ContextType & ctx)
+ {
+ if (auto empty = scn::scan<>(ctx.range(), R"("")")) {
+ return empty->begin();
+ }
+
+ auto simple = scn::scan<std::string>(ctx.range(), R"("{:[^\"]}")");
+ if (simple) {
+ value = std::move(simple->value());
+ return simple->begin();
+ }
+
+ if (auto openQuote = scn::scan<>(ctx.range(), R"(")")) {
+ return parseEscapedString(value, ctx, openQuote);
}
return unexpected(simple.error());
}
@@ -59,17 +70,17 @@ namespace scn {
return null->begin();
}
- if (auto empty = scn::scan<>(ctx.range(), R"("?")")) {
+ auto empty = scn::scan<>(ctx.range(), R"("?")");
+ if (empty) {
value.emplace();
return empty->begin();
}
- auto result = scn::scan<std::string>(ctx.range(), R"("?{:[^"]}")");
- if (!result) {
- return unexpected(result.error());
+ if (auto openQuoteQM = scn::scan<>(ctx.range(), R"("?)")) {
+ value.emplace();
+ return parseEscapedString(*value, ctx, openQuoteQM);
}
- value = std::move(result->value());
- return result->begin();
+ return unexpected(empty.error());
}
scan_expected<typename ContextType::iterator>
diff --git a/test/test-ingest.cpp b/test/test-ingest.cpp
index 8c82764..5fc8195 100644
--- a/test/test-ingest.cpp
+++ b/test/test-ingest.cpp
@@ -202,6 +202,17 @@ BOOST_AUTO_TEST_CASE(ExtractFieldsEdgeCasesUnparsable3603068405)
BOOST_CHECK_EQUAL(std::get<4>(result->values()), R"LOG(/packages/dev-php/pecl-uploadprogress'yqFSRA<'">yuezhx)LOG");
}
+BOOST_AUTO_TEST_CASE(ExtractFieldsEdgeCasesUnparsable3482917779)
+{
+ const auto result = WebStat::Ingestor::scanLogLine(
+ R"LOG(195.166.151.214 62.171.174.195 1774536220216585 GET "/index.php" "?lang=../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/&/<?echo(md5(\"hi\"));?>+/tmp/index1.php" HTTP/1.1 404 376 5811 "-" "libredtail-http" "text/html")LOG");
+ BOOST_REQUIRE(result);
+ const auto queryString = std::get<5>(result->values());
+ BOOST_REQUIRE(queryString);
+ BOOST_CHECK_EQUAL(*queryString,
+ R"LOG(lang=../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/&/<?echo(md5("hi"));?>+/tmp/index1.php)LOG");
+}
+
class TestIngestor : public WebStat::Ingestor {
public:
TestIngestor() :