diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d415e..6336322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.0.1 + +* bug: fix escaping in text formatter (#575) + # 1.0.0 * Officially changed name to lower-case diff --git a/text_formatter.go b/text_formatter.go index ba88854..26dcc15 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -174,16 +174,23 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) + b.WriteString(f.quoteString(value)) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) + b.WriteString(f.quoteString(errmsg)) } default: fmt.Fprint(b, value) } } + +func (f *TextFormatter) quoteString(v string) string { + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedValue := strings.Replace(v, f.QuoteCharacter, escapedQuote, -1) + + return fmt.Sprintf("%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) +} diff --git a/text_formatter_test.go b/text_formatter_test.go index 9793b5f..f02dc0c 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -53,6 +53,48 @@ func TestQuoting(t *testing.T) { checkQuoting(true, errors.New("invalid argument")) } +func TestEscaping_DefaultQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`ba"r`, `ba\"r`}, + {`ba'r`, `ba'r`}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + +func TestEscaping_CustomQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + quoteChar string + }{ + {`ba"r`, `ba"r`, `'`}, + {`ba'r`, `ba\'r`, `'`}, + {`ba^r`, `ba\^r`, `^`}, + {`ba'r`, `ba'r`, `^`}, + } + + for _, tc := range testCases { + tf.QuoteCharacter = tc.quoteChar + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + func TestTimestampFormat(t *testing.T) { checkTimeStr := func(format string) { customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}