From a57d0891f3a2cb25cd7824d2e0a7d620f6288864 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Sun, 26 Oct 2025 10:07:57 -0700 Subject: Strings must be valid UTF-8 --- asspb.go | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) (limited to 'asspb.go') diff --git a/asspb.go b/asspb.go index 624f3ca..35cdc51 100644 --- a/asspb.go +++ b/asspb.go @@ -34,13 +34,15 @@ // # Strings // // Strings are written with " or ' and any sequence of intermediate bytes (with -// the exception of escape sequences which are described below). "any bytes" -// means that strings can contain newline without needing an escape sequence +// the exception of escape sequences which are described below). Strings must be +// valid UTF-8 after escape sequences are expanded. // // 'asdf' // "that's cool" // "\tall\n\tyour\n\tfavorite\n\tescape\n\tsequences" // +// Note that strings can contain newline without needing an escape sequence +// // 'a multiline // string' // @@ -64,7 +66,7 @@ // \nnn 3-digit octal value nnn // \xnn 2-digit hex value nn // \unnnn unicode code point U+nnnn -// \Unnnnnnnn unicode code point U+nnnnnnnn +// \Unnnnnnnn unicode code point U+nnnnnnnn (UTF8) // // As an extension to the C11 escapes, a backslash immediately before a newline // character (0x0a) will remove the newline character from the resulting string @@ -259,28 +261,44 @@ func unescape(idx int, rawStr []byte) ([]byte, error) { case "\\\n", "\\\r\n": return nil } - if bytes.HasPrefix(escape, []byte(`\x`)) || bytes.HasPrefix(escape, []byte(`\u`)) || bytes.HasPrefix(escape, []byte(`\U`)) { + switch { + case bytes.HasPrefix(escape, []byte(`\x`)): + var n uint64 + if n, err = strconv.ParseUint(string(escape[2:]), 16, 8); err != nil { + err = fmt.Errorf("%d: syntax error: invalid hex escape %q: %s", idx, escape, err) + return nil + } + return []byte{byte(n)} + case bytes.HasPrefix(escape, []byte(`\u`)), bytes.HasPrefix(escape, []byte(`\U`)): var n int64 if n, err = strconv.ParseInt(string(escape[2:]), 16, 32); err != nil { - err = fmt.Errorf("%d: syntax error: invalid hex escape %q: %s", idx, escape, err) + err = fmt.Errorf("%d: syntax error: invalid unicode escape %q: %s", idx, escape, err) return nil } return utf8.AppendRune(nil, rune(n)) + default: + if len(escape) != 4 { + err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape) + return nil + } + var n int64 + if n, err = strconv.ParseInt(string(escape[1:]), 8, 32); err != nil { + err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape) + return nil + } + if n > 255 { + err = fmt.Errorf("%d: invalid octal escape %q %d > 255", idx, escape, n) + return nil + } + return []byte{byte(n)} } - if len(escape) != 4 { - err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape) - return nil - } - var n int64 - if n, err = strconv.ParseInt(string(escape[1:]), 8, 32); err != nil { - err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape) - return nil - } - return utf8.AppendRune(nil, rune(n)) }) if err != nil { return nil, err } + if !utf8.Valid(escaped) { + return nil, fmt.Errorf("%d: syntax error: string %q is not UTF-8 encoded", idx, escaped) + } return escaped, nil } -- cgit v1.3.1