Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/encoding/xml/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,9 @@ func (p *printer) writeStart(start *StartElement) error {
if start.Name.Local == "" {
return fmt.Errorf("xml: start tag with no name")
}
if !isNameString(start.Name.Local) {
return fmt.Errorf("xml: start tag with invalid name: %s", start.Name.Local)
}

p.tags = append(p.tags, start.Name)
p.markPrefix()
Expand All @@ -753,9 +756,16 @@ func (p *printer) writeStart(start *StartElement) error {
if name.Local == "" {
continue
}
if !isNameString(name.Local) {
return fmt.Errorf("xml: attribute with invalid name: %s", name.Local)
}
p.WriteByte(' ')
if name.Space != "" {
p.WriteString(p.createAttrPrefix(name.Space))
prefix := p.createAttrPrefix(name.Space)
if !isNameString(prefix) {
return fmt.Errorf("xml: attribute prefix with invalid name: %s", prefix)
}
p.WriteString(prefix)
p.WriteByte(':')
}
p.WriteString(name.Local)
Expand All @@ -771,6 +781,9 @@ func (p *printer) writeEnd(name Name) error {
if name.Local == "" {
return fmt.Errorf("xml: end tag with no name")
}
if !isNameString(name.Local) {
return fmt.Errorf("xml: end tag with invalid name: %s", name.Local)
}
if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" {
return fmt.Errorf("xml: end tag </%s> without start tag", name.Local)
}
Expand Down
74 changes: 74 additions & 0 deletions src/encoding/xml/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2588,3 +2588,77 @@ func TestClose(t *testing.T) {
})
}
}

func TestEncodeTokenInvalidNames(t *testing.T) {
tests := []struct {
name string
token Token
}{
{
name: "start element with angle bracket in name",
token: StartElement{Name: Name{Local: `div><script>alert(1)</script><div`}},
},
{
name: "start element with quote in name",
token: StartElement{Name: Name{Local: `x" onclick="alert(1)`}},
},
{
name: "start element with space in name",
token: StartElement{Name: Name{Local: "has space"}},
},
{
name: "start element with ampersand in name",
token: StartElement{Name: Name{Local: "a&b"}},
},
{
name: "start element with invalid attribute name",
token: StartElement{
Name: Name{Local: "div"},
Attr: []Attr{{Name: Name{Local: `x"><injected y="`}, Value: "val"}},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf)

err := enc.EncodeToken(tt.token)
if err == nil {
t.Errorf("EncodeToken(%#v) succeeded, want error for invalid XML name", tt.token)
t.Logf("produced output: %s", buf.String())
}
})
}
}

func TestMarshalInvalidXMLName(t *testing.T) {
type Item struct {
XMLName Name
Value string `xml:",chardata"`
}

tests := []struct {
name string
input Item
}{
{
name: "element name with script injection",
input: Item{XMLName: Name{Local: `x"><script>alert(1)</script><y`}, Value: "data"},
},
{
name: "element name with angle brackets",
input: Item{XMLName: Name{Local: "foo>bar"}, Value: "data"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := Marshal(tt.input)
if err == nil {
t.Errorf("Marshal with invalid XMLName.Local=%q succeeded, want error", tt.input.XMLName.Local)
}
})
}
}