Skip to content

Commit 1722299

Browse files
authored
Prevent panic on fuzzer provided string (#14405)
* 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 <[email protected]> * Placate lint * placate lint again Signed-off-by: Andrew Thornton <[email protected]> * minor cleanup Signed-off-by: Andrew Thornton <[email protected]>
1 parent b708968 commit 1722299

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

modules/markup/html.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -317,19 +317,16 @@ func RenderEmoji(
317317
return ctx.postProcess(rawHTML)
318318
}
319319

320-
var byteBodyTag = []byte("<body>")
321-
var byteBodyTagClosing = []byte("</body>")
322-
323320
func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
324321
if ctx.procs == nil {
325322
ctx.procs = defaultProcessors
326323
}
327324

328325
// give a generous extra 50 bytes
329326
res := make([]byte, 0, len(rawHTML)+50)
330-
res = append(res, byteBodyTag...)
327+
res = append(res, "<html><body>"...)
331328
res = append(res, rawHTML...)
332-
res = append(res, byteBodyTagClosing...)
329+
res = append(res, "</body></html>"...)
333330

334331
// parse the HTML
335332
nodes, err := html.ParseFragment(bytes.NewReader(res), nil)
@@ -341,6 +338,31 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
341338
ctx.visitNode(node, true)
342339
}
343340

341+
newNodes := make([]*html.Node, 0, len(nodes))
342+
343+
for _, node := range nodes {
344+
if node.Data == "html" {
345+
node = node.FirstChild
346+
for node != nil && node.Data != "body" {
347+
node = node.NextSibling
348+
}
349+
}
350+
if node == nil {
351+
continue
352+
}
353+
if node.Data == "body" {
354+
child := node.FirstChild
355+
for child != nil {
356+
newNodes = append(newNodes, child)
357+
child = child.NextSibling
358+
}
359+
} else {
360+
newNodes = append(newNodes, node)
361+
}
362+
}
363+
364+
nodes = newNodes
365+
344366
// Create buffer in which the data will be placed again. We know that the
345367
// length will be at least that of res; to spare a few alloc+copy, we
346368
// reuse res, resetting its length to 0.
@@ -353,12 +375,8 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
353375
}
354376
}
355377

356-
// remove initial parts - because Render creates a whole HTML page.
357-
res = buf.Bytes()
358-
res = res[bytes.Index(res, byteBodyTag)+len(byteBodyTag) : bytes.LastIndex(res, byteBodyTagClosing)]
359-
360378
// Everything done successfully, return parsed data.
361-
return res, nil
379+
return buf.Bytes(), nil
362380
}
363381

364382
func (ctx *postProcessCtx) visitNode(node *html.Node, visitText bool) {

modules/markup/html_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,28 @@ func TestRender_ShortLinks(t *testing.T) {
383383
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`,
384384
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
385385
}
386+
387+
func Test_ParseClusterFuzz(t *testing.T) {
388+
setting.AppURL = AppURL
389+
setting.AppSubURL = AppSubURL
390+
391+
var localMetas = map[string]string{
392+
"user": "go-gitea",
393+
"repo": "gitea",
394+
}
395+
396+
data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
397+
398+
val, err := PostProcess([]byte(data), "https://example.com", localMetas, false)
399+
400+
assert.NoError(t, err)
401+
assert.NotContains(t, string(val), "<html")
402+
403+
data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
404+
405+
val, err = PostProcess([]byte(data), "https://example.com", localMetas, false)
406+
407+
assert.NoError(t, err)
408+
409+
assert.NotContains(t, string(val), "<html")
410+
}

0 commit comments

Comments
 (0)