From a6aca6c33f36373b0051e61f65f65c45c3a80f38 Mon Sep 17 00:00:00 2001 From: YuMin Kim Date: Thu, 5 Nov 2026 14:00:47 +0900 Subject: [PATCH 1/3] parser_syslog: do not end RFC5424 structured data at an escaped "]" in string parser With `parser_engine string`, `parse_rfc5424` ended STRUCTURED-DATA at the first `] `. RFC 5424 section 6.3.3 requires `]` in a PARAM-VALUE to be escaped as `\]`, so a valid message such as `... ID [x@1 a="b\] c"] msg` put part of the structured data into `message`. Skip `] ` when the `]` is preceded by `\`, as the regexp parser does with `(? --- lib/fluent/plugin/parser_syslog.rb | 1 + test/plugin/test_parser_syslog.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/fluent/plugin/parser_syslog.rb b/lib/fluent/plugin/parser_syslog.rb index 02c622104c..3f9ff05621 100644 --- a/lib/fluent/plugin/parser_syslog.rb +++ b/lib/fluent/plugin/parser_syslog.rb @@ -522,6 +522,7 @@ def parse_rfc5424(text, &block) else start = cursor i = text.index('] '.freeze, cursor) + i = text.index('] '.freeze, i + 1) while i && text[i - 1] == '\\'.freeze # skip escaped ']' (RFC 5424 6.3.3) extradata = if i diff = i + 1 - start # calculate ']' position cursor += diff diff --git a/test/plugin/test_parser_syslog.rb b/test/plugin/test_parser_syslog.rb index 109065641b..f1659c8b97 100644 --- a/test/plugin/test_parser_syslog.rb +++ b/test/plugin/test_parser_syslog.rb @@ -762,6 +762,35 @@ def test_parse_with_rfc5424_message_includes_right_bracket(param) end end + data( + 'regexp/with message' => ['regexp', '[x@1 a="b\] c"]', 'msg'], + 'string/with message' => ['string', '[x@1 a="b\] c"]', 'msg'], + 'regexp/without message' => ['regexp', '[x@1 a="b\] c"]', nil], + 'string/without message' => ['string', '[x@1 a="b\] c"]', nil], + 'regexp/multiple structured data' => ['regexp', '[x@1 a="b\] c"][y@1 d="e"]', 'msg'], + 'string/multiple structured data' => ['string', '[x@1 a="b\] c"][y@1 d="e"]', 'msg'], + ) + def test_parse_with_rfc5424_structured_data_includes_escaped_right_bracket(data) + parser_engine, extradata, message = data + @parser.configure( + 'message_format' => 'rfc5424', + 'with_priority' => true, + 'parser_engine' => parser_engine + ) + text = "<14>1 2026-04-25T16:43:29.123Z host app 123 ID #{extradata}" + text += " #{message}" if message + + result = nil + @parser.instance.parse(text) do |time, record| + result = [time, record] + end + # string parser removes backslashes from extradata + extradata = extradata.delete('\\') if parser_engine == 'string' + expected = {'host' => 'host', 'ident' => 'app', 'pid' => '123', 'msgid' => 'ID', 'pri' => 14, 'extradata' => extradata} + expected['message'] = message if message + assert_equal([event_time('2026-04-25T16:43:29.123Z', format: '%Y-%m-%dT%H:%M:%S.%L%z'), expected], result) + end + data('regexp' => 'regexp', 'string' => 'string') def test_parse_with_rfc5424_empty_message(param) @parser.configure( From ec56def8bdd01c035b49ca1b3fb335bd3982c978 Mon Sep 17 00:00:00 2001 From: YuMin Kim Date: Thu, 5 Nov 2026 14:05:18 +0900 Subject: [PATCH 2/3] test_parser_syslog: cover escaped backslashes in RFC5424 structured data Signed-off-by: YuMin Kim --- test/plugin/test_parser_syslog.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/plugin/test_parser_syslog.rb b/test/plugin/test_parser_syslog.rb index f1659c8b97..8e44846c37 100644 --- a/test/plugin/test_parser_syslog.rb +++ b/test/plugin/test_parser_syslog.rb @@ -769,6 +769,10 @@ def test_parse_with_rfc5424_message_includes_right_bracket(param) 'string/without message' => ['string', '[x@1 a="b\] c"]', nil], 'regexp/multiple structured data' => ['regexp', '[x@1 a="b\] c"][y@1 d="e"]', 'msg'], 'string/multiple structured data' => ['string', '[x@1 a="b\] c"][y@1 d="e"]', 'msg'], + 'regexp/escaped backslash at end of value' => ['regexp', '[x@1 a="b\\\\"]', 'msg'], + 'string/escaped backslash at end of value' => ['string', '[x@1 a="b\\\\"]', 'msg'], + 'regexp/escaped backslash before right bracket' => ['regexp', '[x@1 a="b\\\\] c"]', 'msg'], + 'string/escaped backslash before right bracket' => ['string', '[x@1 a="b\\\\] c"]', 'msg'], ) def test_parse_with_rfc5424_structured_data_includes_escaped_right_bracket(data) parser_engine, extradata, message = data From c6c42e369232717420fe62ef578dce19d6a8ae0c Mon Sep 17 00:00:00 2001 From: YuMin Kim Date: Thu, 5 Nov 2026 19:01:47 +0900 Subject: [PATCH 3/3] parser_syslog: find the end of RFC5424 structured data in linear time The loop that skipped escaped `]` called `text.index` and `text[]` for each escaped `] `. On UTF-8 strings with non-ASCII characters, each of them counts characters from the start of the string, so a line with many escaped `] ` took quadratic time. After the first escaped `]`, walk the remaining characters once instead. Do not use a regexp search, which raises ArgumentError when MSG has an invalid byte. Suggested-by: Watson1978 in https://github.com/fluent/fluentd/pull/5591#discussion_r4478775823 Signed-off-by: YuMin Kim --- lib/fluent/plugin/parser_syslog.rb | 16 +++++++++++++++- test/plugin/test_parser_syslog.rb | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/fluent/plugin/parser_syslog.rb b/lib/fluent/plugin/parser_syslog.rb index 3f9ff05621..c5b7c738f0 100644 --- a/lib/fluent/plugin/parser_syslog.rb +++ b/lib/fluent/plugin/parser_syslog.rb @@ -522,7 +522,21 @@ def parse_rfc5424(text, &block) else start = cursor i = text.index('] '.freeze, cursor) - i = text.index('] '.freeze, i + 1) while i && text[i - 1] == '\\'.freeze # skip escaped ']' (RFC 5424 6.3.3) + if i && text[i - 1] == '\\'.freeze # skip escaped ']' (RFC 5424 6.3.3) + offset = i + previous = '\\'.freeze + candidate = false + i = nil + text.slice(offset, text.length).each_char do |char| + if candidate && char == SPLIT_CHAR + i = offset - 1 + break + end + candidate = char == ']'.freeze && previous != '\\'.freeze + previous = char + offset += 1 + end + end extradata = if i diff = i + 1 - start # calculate ']' position cursor += diff diff --git a/test/plugin/test_parser_syslog.rb b/test/plugin/test_parser_syslog.rb index 8e44846c37..1a2617f0e4 100644 --- a/test/plugin/test_parser_syslog.rb +++ b/test/plugin/test_parser_syslog.rb @@ -773,6 +773,7 @@ def test_parse_with_rfc5424_message_includes_right_bracket(param) 'string/escaped backslash at end of value' => ['string', '[x@1 a="b\\\\"]', 'msg'], 'regexp/escaped backslash before right bracket' => ['regexp', '[x@1 a="b\\\\] c"]', 'msg'], 'string/escaped backslash before right bracket' => ['string', '[x@1 a="b\\\\] c"]', 'msg'], + 'string/invalid byte in message' => ['string', '[x@1 a="b\] c"]', "m\xFFsg"], ) def test_parse_with_rfc5424_structured_data_includes_escaped_right_bracket(data) parser_engine, extradata, message = data