parser: validate invalid ASCII control characters
This commit is contained in:
@@ -558,15 +558,11 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er
|
|||||||
return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
|
return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
i = escaped
|
|
||||||
|
|
||||||
var builder bytes.Buffer
|
var builder bytes.Buffer
|
||||||
// grow?
|
|
||||||
builder.Write(token[startIdx:i])
|
|
||||||
|
|
||||||
// The scanner ensures that the token starts and ends with quotes and that
|
// The scanner ensures that the token starts and ends with quotes and that
|
||||||
// escapes are balanced.
|
// escapes are balanced.
|
||||||
for ; i < len(token)-3; i++ {
|
for i < len(token)-3 {
|
||||||
c := token[i]
|
c := token[i]
|
||||||
|
|
||||||
//nolint:nestif
|
//nolint:nestif
|
||||||
@@ -584,7 +580,7 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,8 +619,14 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er
|
|||||||
default:
|
default:
|
||||||
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c)
|
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c)
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
} else {
|
} else {
|
||||||
builder.WriteByte(c)
|
size := utf8ValidNext(token[i:])
|
||||||
|
if size == 0 {
|
||||||
|
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c)
|
||||||
|
}
|
||||||
|
builder.Write(token[i : i+size])
|
||||||
|
i += size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,15 +733,13 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) {
|
|||||||
return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
|
return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
i := escaped
|
i := startIdx
|
||||||
|
|
||||||
var builder bytes.Buffer
|
var builder bytes.Buffer
|
||||||
// grow?
|
|
||||||
builder.Write(token[startIdx:i])
|
|
||||||
|
|
||||||
// The scanner ensures that the token starts and ends with quotes and that
|
// The scanner ensures that the token starts and ends with quotes and that
|
||||||
// escapes are balanced.
|
// escapes are balanced.
|
||||||
for ; i < len(token)-1; i++ {
|
for i < len(token)-1 {
|
||||||
c := token[i]
|
c := token[i]
|
||||||
if c == '\\' {
|
if c == '\\' {
|
||||||
i++
|
i++
|
||||||
@@ -777,8 +777,14 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c)
|
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c)
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
} else {
|
} else {
|
||||||
builder.WriteByte(c)
|
size := utf8ValidNext(token[i:])
|
||||||
|
if size == 0 {
|
||||||
|
return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c)
|
||||||
|
}
|
||||||
|
builder.Write(token[i : i+size])
|
||||||
|
i += size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2113,6 +2113,66 @@ world'`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestASCIIControlCharacters(t *testing.T) {
|
||||||
|
invalidCharacters := []byte{0x7F}
|
||||||
|
for c := byte(0x0); c <= 0x08; c++ {
|
||||||
|
invalidCharacters = append(invalidCharacters, c)
|
||||||
|
}
|
||||||
|
for c := byte(0x0B); c <= 0x0C; c++ {
|
||||||
|
invalidCharacters = append(invalidCharacters, c)
|
||||||
|
}
|
||||||
|
for c := byte(0x0E); c <= 0x1F; c++ {
|
||||||
|
invalidCharacters = append(invalidCharacters, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringType struct {
|
||||||
|
Delimiter string
|
||||||
|
CanEscape bool
|
||||||
|
}
|
||||||
|
|
||||||
|
stringTypes := map[string]stringType{
|
||||||
|
"basic": {Delimiter: "\"", CanEscape: true},
|
||||||
|
"basicMultiline": {Delimiter: "\"\"\"", CanEscape: true},
|
||||||
|
"literal": {Delimiter: "'", CanEscape: false},
|
||||||
|
"literalMultiline": {Delimiter: "'''", CanEscape: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
checkError := func(t *testing.T, input []byte) {
|
||||||
|
t.Helper()
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
err := toml.Unmarshal(input, &m)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
var de *toml.DecodeError
|
||||||
|
if !errors.As(err, &de) {
|
||||||
|
t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, st := range stringTypes {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
for _, c := range invalidCharacters {
|
||||||
|
name := fmt.Sprintf("%2X", c)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
data := []byte("A = " + st.Delimiter + string(c) + st.Delimiter)
|
||||||
|
checkError(t, data)
|
||||||
|
|
||||||
|
if st.CanEscape {
|
||||||
|
t.Run("withEscapeBefore", func(t *testing.T) {
|
||||||
|
data := []byte("A = " + st.Delimiter + "\\t" + string(c) + st.Delimiter)
|
||||||
|
checkError(t, data)
|
||||||
|
})
|
||||||
|
t.Run("withEscapeAfter", func(t *testing.T) {
|
||||||
|
data := []byte("A = " + st.Delimiter + string(c) + "\\t" + st.Delimiter)
|
||||||
|
checkError(t, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestLocalDateTime(t *testing.T) {
|
func TestLocalDateTime(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
@@ -2268,6 +2328,13 @@ xz_hash = "1a48f723fea1f17d786ce6eadd9d00914d38062d28fd9c455ed3c3801905b388"
|
|||||||
require.Equal(t, expected, dist)
|
require.Equal(t, expected, dist)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue631(t *testing.T) {
|
||||||
|
v := map[string]interface{}{}
|
||||||
|
|
||||||
|
err := toml.Unmarshal([]byte("\"\\b\u007f\"= 2"), &v)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestDecoderStrict(t *testing.T) {
|
func TestDecoderStrict(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user