From 456b784f4ff742bf604340e62c11da47b46b2950 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Mon, 24 Nov 2025 17:43:44 -0800 Subject: Disallow control characters in strings --- ccl.go | 20 ++++++++++++++++---- ccl_test.go | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/ccl.go b/ccl.go index 1333119..92a1a3f 100644 --- a/ccl.go +++ b/ccl.go @@ -42,9 +42,9 @@ // // # Strings // -// Strings are written with " or ' and any sequence of intermediate bytes (with -// the exception of escape sequences which are described below). Strings must be -// valid UTF-8 after escape sequences are expanded. +// Strings are written with " or ' and a (possibly empty) sequence of +// intervening characters. Strings must be valid UTF-8 after expanding escape +// sequences (described below). // // 'asdf' // "that's cool" @@ -55,6 +55,9 @@ // 'a multiline // string' // +// Carriage returns (0x0d) are discarded from the string value. If you need a +// string to contain carriage return, use the \r escape sequence. +// // Backslash characters inside a string are interpreted as an escape sequence. // Any escape sequence not described below is an error. The escape sequences // are identical to C11, with the exception that \x always takes exactly 2 @@ -162,6 +165,7 @@ import ( "reflect" "strconv" "strings" + "unicode" "unicode/utf8" ) @@ -357,8 +361,16 @@ func (p *parser) parseFloat(nBytes []byte) (float64, error) { func (p *parser) unescape(rawStr []byte) ([]byte, error) { var escaped []byte for i := 0; i < len(rawStr); i++ { + if i+1 < len(rawStr) && rawStr[i] == '\r' && rawStr[i+1] == '\n' { + continue + } if rawStr[i] != '\\' { - escaped = append(escaped, rawStr[i]) + r, n := utf8.DecodeRune(rawStr[i:]) + if r != '\t' && r != '\n' && unicode.IsControl(r) { + return nil, p.error("control character %q must be escaped", r) + } + escaped = append(escaped, rawStr[i:i+n]...) + i += n - 1 continue } i++ diff --git a/ccl_test.go b/ccl_test.go index e377306..e7405f8 100644 --- a/ccl_test.go +++ b/ccl_test.go @@ -259,6 +259,14 @@ can just span multiple lines"`, desc: "StringOctal", msg: `string: '\033'`, want: message{String: "\033"}, + }, { + desc: "StringStripCarriageReturn", + msg: "string:'a\r\nb'", + want: message{String: "a\nb"}, + }, { + desc: "StringTab", + msg: "string:'\t'", + want: message{String: "\t"}, }, { desc: "Message", msg: `message { field: 10 }`, @@ -407,6 +415,12 @@ func TestUnmarshal_Invalid(t *testing.T) { }, { desc: "StringBadUnicode", msg: `string:"\ugggg"`, + }, { + desc: "StringControlCharacter", + msg: "string:'\a'", + }, { + desc: "StringCarriageReturnNotFollowedByNewline", + msg: "string:'\r'", }, { desc: "UnterminatedString", msg: `string: '`, -- cgit v1.3.1