Skip to content

Commit 0a3e582

Browse files
committed
parser/keyvalue: handle escaped quotes when parsing
Currently, the keyvalue parser does handle escaped quotes and instead will parse this as independent '\', '"' characters. This results in unexpected breakages between fields for strings like: > <key>="...\" ..." Here, the backslash will be appended to the result pair, while the (now un-)escaped quotation will result in the pair being terminated early. Add handling of escaped quotation marks (for both ", ') in the keyvalue parser along with a testcase to exercise this functionality. Signed-off-by: Ethan Adams <[email protected]>
1 parent b3d6a93 commit 0a3e582

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

internal/coreinternal/parseutils/parser.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func SplitString(input, delimiter string) ([]string, error) {
1818
current := ""
1919
delimiterLength := len(delimiter)
2020
quoteChar := "" // "" means we are not in quotes
21+
escaped := false
2122

2223
for i := 0; i < len(input); i++ {
2324
if quoteChar == "" && i+delimiterLength <= len(input) && input[i:i+delimiterLength] == delimiter { // delimiter
@@ -31,13 +32,19 @@ func SplitString(input, delimiter string) ([]string, error) {
3132
continue
3233
}
3334

34-
if quoteChar == "" && (input[i] == '"' || input[i] == '\'') { // start of quote
35-
quoteChar = string(input[i])
36-
continue
37-
}
38-
if string(input[i]) == quoteChar { // end of quote
39-
quoteChar = ""
40-
continue
35+
if !escaped { // consider quote termination so long as previous character wasn't backslash
36+
if quoteChar == "" && (input[i] == '"' || input[i] == '\'') { // start of quote
37+
quoteChar = string(input[i])
38+
continue
39+
}
40+
if string(input[i]) == quoteChar { // end of quote
41+
quoteChar = ""
42+
continue
43+
}
44+
// Only if we weren't escaped could the next character result in escaped state
45+
escaped = input[i] == '\\' // potentially escaping next character
46+
} else {
47+
escaped = false
4148
}
4249

4350
current += string(input[i])

pkg/stanza/operator/parser/keyvalue/parser_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,24 @@ key=value`,
688688
true,
689689
false,
690690
},
691+
{
692+
"containerd output",
693+
func(_ *Config) {},
694+
&entry.Entry{
695+
Body: `time="2024-11-01T12:38:17.992190505Z" level=warning msg="cleanup warnings time='2024-11-01T12:38:17Z' level=debug msg=\"starting signal loop\" namespace=moby-10000.10000 pid=1608080 runtime=io.containerd.runc.v2" namespace=moby-10000.10000`,
696+
},
697+
&entry.Entry{
698+
Attributes: map[string]any{
699+
"time": "2024-11-01T12:38:17.992190505Z",
700+
"level": "warning",
701+
"msg": `cleanup warnings time='2024-11-01T12:38:17Z' level=debug msg=\"starting signal loop\" namespace=moby-10000.10000 pid=1608080 runtime=io.containerd.runc.v2`,
702+
"namespace": "moby-10000.10000",
703+
},
704+
Body: `time="2024-11-01T12:38:17.992190505Z" level=warning msg="cleanup warnings time='2024-11-01T12:38:17Z' level=debug msg=\"starting signal loop\" namespace=moby-10000.10000 pid=1608080 runtime=io.containerd.runc.v2" namespace=moby-10000.10000`,
705+
},
706+
false,
707+
false,
708+
},
691709
}
692710

693711
for _, tc := range cases {

0 commit comments

Comments
 (0)