mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
html: impose open element stack size limit
The HTML specification contains a number of algorithms which are quadratic in complexity by design. Instead of adding complicated workarounds to prevent these cases from becoming extremely expensive in pathological cases, we impose a limit of 512 to the size of the stack of open elements. It is extremely unlikely that non-adversarial HTML documents will ever hit this limit (but if we see cases of this, we may want to make the limit configurable via a ParseOption). Thanks to Guido Vranken and Jakub Ciolek for both independently reporting this issue. Fixes CVE-2025-47911 Fixes golang/go#75682 Change-Id: I890517b189af4ffbf427d25d3fde7ad7ec3509ad Reviewed-on: https://go-review.googlesource.com/c/net/+/709876 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
@@ -299,7 +299,7 @@ func escape(w writer, s string) error {
|
||||
case '\r':
|
||||
esc = " "
|
||||
default:
|
||||
panic("unrecognized escape character")
|
||||
panic("html: unrecognized escape character")
|
||||
}
|
||||
s = s[i+1:]
|
||||
if _, err := w.WriteString(esc); err != nil {
|
||||
|
||||
@@ -231,7 +231,14 @@ func (p *parser) addChild(n *Node) {
|
||||
}
|
||||
|
||||
if n.Type == ElementNode {
|
||||
p.oe = append(p.oe, n)
|
||||
p.insertOpenElement(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) insertOpenElement(n *Node) {
|
||||
p.oe = append(p.oe, n)
|
||||
if len(p.oe) > 512 {
|
||||
panic("html: open stack of elements exceeds 512 nodes")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,7 +817,7 @@ func afterHeadIM(p *parser) bool {
|
||||
p.im = inFramesetIM
|
||||
return true
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
p.oe = append(p.oe, p.head)
|
||||
p.insertOpenElement(p.head)
|
||||
defer p.oe.remove(p.head)
|
||||
return inHeadIM(p)
|
||||
case a.Head:
|
||||
@@ -2324,9 +2331,13 @@ func (p *parser) parseCurrentToken() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parse() error {
|
||||
func (p *parser) parse() (err error) {
|
||||
defer func() {
|
||||
if panicErr := recover(); panicErr != nil {
|
||||
err = fmt.Errorf("%s", panicErr)
|
||||
}
|
||||
}()
|
||||
// Iterate until EOF. Any other error will cause an early return.
|
||||
var err error
|
||||
for err != io.EOF {
|
||||
// CDATA sections are allowed only in foreign content.
|
||||
n := p.oe.top()
|
||||
@@ -2355,6 +2366,8 @@ func (p *parser) parse() error {
|
||||
// <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
|
||||
// with no corresponding node in the resulting tree.
|
||||
//
|
||||
// Parse will reject HTML that is nested deeper than 512 elements.
|
||||
//
|
||||
// The input is assumed to be UTF-8 encoded.
|
||||
func Parse(r io.Reader) (*Node, error) {
|
||||
return ParseWithOptions(r)
|
||||
|
||||
@@ -517,3 +517,28 @@ func TestIssue70179(t *testing.T) {
|
||||
t.Fatalf("unexpected failure: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepthLimit(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
input string
|
||||
succeed bool
|
||||
}{
|
||||
// Not we don't use 512 as the limit here, because the parser will
|
||||
// insert implied <html> and <body> tags, increasing the size of the
|
||||
// stack by two before we start parsing the <dl>.
|
||||
{"above depth limit", strings.Repeat("<dl>", 511), false},
|
||||
{"below depth limit", strings.Repeat("<dl>", 510), true},
|
||||
{"above depth limit, interspersed elements", strings.Repeat("<dl><img />", 511), false},
|
||||
{"closing tags", strings.Repeat("</dl>", 512), true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := Parse(strings.NewReader(tc.input))
|
||||
if tc.succeed && err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if !tc.succeed && err == nil {
|
||||
t.Errorf("unexpected success")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user