Skip to content

Commit

Permalink
Prevent panic on fuzzer provided string (#14405) (#14409)
Browse files Browse the repository at this point in the history
* Prevent panic on fuzzer provided string

The fuzzer has found that providing a <body> tag with an attribute to
PostProcess causes a panic. This PR removes any rendered html or body
tags from the output.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Placate lint

* placate lint again

Signed-off-by: Andrew Thornton <art27@cantab.net>

* minor cleanup

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
  • Loading branch information
6543 and zeripath authored Jan 20, 2021
1 parent 0c3f950 commit fb274ec
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 10 deletions.
38 changes: 28 additions & 10 deletions modules/markup/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,19 +298,16 @@ func RenderEmoji(
return ctx.postProcess(rawHTML)
}

var byteBodyTag = []byte("<body>")
var byteBodyTagClosing = []byte("</body>")

func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
if ctx.procs == nil {
ctx.procs = defaultProcessors
}

// give a generous extra 50 bytes
res := make([]byte, 0, len(rawHTML)+50)
res = append(res, byteBodyTag...)
res = append(res, "<html><body>"...)
res = append(res, rawHTML...)
res = append(res, byteBodyTagClosing...)
res = append(res, "</body></html>"...)

// parse the HTML
nodes, err := html.ParseFragment(bytes.NewReader(res), nil)
Expand All @@ -322,6 +319,31 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
ctx.visitNode(node, true)
}

newNodes := make([]*html.Node, 0, len(nodes))

for _, node := range nodes {
if node.Data == "html" {
node = node.FirstChild
for node != nil && node.Data != "body" {
node = node.NextSibling
}
}
if node == nil {
continue
}
if node.Data == "body" {
child := node.FirstChild
for child != nil {
newNodes = append(newNodes, child)
child = child.NextSibling
}
} else {
newNodes = append(newNodes, node)
}
}

nodes = newNodes

// Create buffer in which the data will be placed again. We know that the
// length will be at least that of res; to spare a few alloc+copy, we
// reuse res, resetting its length to 0.
Expand All @@ -334,12 +356,8 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
}
}

// remove initial parts - because Render creates a whole HTML page.
res = buf.Bytes()
res = res[bytes.Index(res, byteBodyTag)+len(byteBodyTag) : bytes.LastIndex(res, byteBodyTagClosing)]

// Everything done successfully, return parsed data.
return res, nil
return buf.Bytes(), nil
}

func (ctx *postProcessCtx) visitNode(node *html.Node, visitText bool) {
Expand Down
25 changes: 25 additions & 0 deletions modules/markup/html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,28 @@ func TestRender_ShortLinks(t *testing.T) {
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`,
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
}

func Test_ParseClusterFuzz(t *testing.T) {
setting.AppURL = AppURL
setting.AppSubURL = AppSubURL

var localMetas = map[string]string{
"user": "go-gitea",
"repo": "gitea",
}

data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "

val, err := PostProcess([]byte(data), "https://example.com", localMetas, false)

assert.NoError(t, err)
assert.NotContains(t, string(val), "<html")

data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "

val, err = PostProcess([]byte(data), "https://example.com", localMetas, false)

assert.NoError(t, err)

assert.NotContains(t, string(val), "<html")
}

0 comments on commit fb274ec

Please sign in to comment.