Skip to content

Commit 4d014e7

Browse files
committed
encoding/xml: handle leading, trailing, or double colons in names
Before this change, <:name> would parse as <name>, which could cause issues in applications that rely on the parse-encode cycle to round-trip. Similarly, <x name:=""> would parse as expected but then have the attribute dropped when serializing because its name was empty. Finally, <a:b:c> would parse and get serialized incorrectly. All these values are invalid XML, but to minimize the impact of this change, we parse them whole into Name.Local. This issue was reported by Juho Nurminen of Mattermost as it leads to round-trip mismatches. See #43168. It's not being fixed in a security release because round-trip stability is not a currently supported security property of encoding/xml, and we don't believe these fixes would be sufficient to reliably guarantee it in the future. Fixes CVE-2020-29509 Fixes CVE-2020-29511 Updates #43168 Change-Id: I68321c4d867305046f664347192948a889af3c7f Reviewed-on: https://go-review.googlesource.com/c/go/+/277892 Run-TryBot: Filippo Valsorda <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Filippo Valsorda <[email protected]> Reviewed-by: Katie Hockman <[email protected]>
1 parent cc4e616 commit 4d014e7

File tree

2 files changed

+59
-2
lines changed

2 files changed

+59
-2
lines changed

src/encoding/xml/xml.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -1156,8 +1156,9 @@ func (d *Decoder) nsname() (name Name, ok bool) {
11561156
if !ok {
11571157
return
11581158
}
1159-
i := strings.Index(s, ":")
1160-
if i < 0 {
1159+
if strings.Count(s, ":") > 1 {
1160+
name.Local = s
1161+
} else if i := strings.Index(s, ":"); i < 1 || i > len(s)-2 {
11611162
name.Local = s
11621163
} else {
11631164
name.Space = s[0:i]

src/encoding/xml/xml_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,59 @@ func TestTokenUnmarshaler(t *testing.T) {
10031003
d := NewTokenDecoder(tokReader{})
10041004
d.Decode(&Failure{})
10051005
}
1006+
1007+
func testRoundTrip(t *testing.T, input string) {
1008+
d := NewDecoder(strings.NewReader(input))
1009+
var tokens []Token
1010+
var buf bytes.Buffer
1011+
e := NewEncoder(&buf)
1012+
for {
1013+
tok, err := d.Token()
1014+
if err == io.EOF {
1015+
break
1016+
}
1017+
if err != nil {
1018+
t.Fatalf("invalid input: %v", err)
1019+
}
1020+
if err := e.EncodeToken(tok); err != nil {
1021+
t.Fatalf("failed to re-encode input: %v", err)
1022+
}
1023+
tokens = append(tokens, CopyToken(tok))
1024+
}
1025+
if err := e.Flush(); err != nil {
1026+
t.Fatal(err)
1027+
}
1028+
1029+
d = NewDecoder(&buf)
1030+
for {
1031+
tok, err := d.Token()
1032+
if err == io.EOF {
1033+
break
1034+
}
1035+
if err != nil {
1036+
t.Fatalf("failed to decode output: %v", err)
1037+
}
1038+
if len(tokens) == 0 {
1039+
t.Fatalf("unexpected token: %#v", tok)
1040+
}
1041+
a, b := tokens[0], tok
1042+
if !reflect.DeepEqual(a, b) {
1043+
t.Fatalf("token mismatch: %#v vs %#v", a, b)
1044+
}
1045+
tokens = tokens[1:]
1046+
}
1047+
if len(tokens) > 0 {
1048+
t.Fatalf("lost tokens: %#v", tokens)
1049+
}
1050+
}
1051+
1052+
func TestRoundTrip(t *testing.T) {
1053+
tests := map[string]string{
1054+
"leading colon": `<::Test ::foo="bar"><:::Hello></:::Hello><Hello></Hello></::Test>`,
1055+
"trailing colon": `<foo abc:="x"></foo>`,
1056+
"double colon": `<x:y:foo></x:y:foo>`,
1057+
}
1058+
for name, input := range tests {
1059+
t.Run(name, func(t *testing.T) { testRoundTrip(t, input) })
1060+
}
1061+
}

0 commit comments

Comments
 (0)