mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	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 <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>
This commit is contained in:
		@@ -317,9 +317,6 @@ func RenderEmoji(
 | 
				
			|||||||
	return ctx.postProcess(rawHTML)
 | 
						return ctx.postProcess(rawHTML)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var byteBodyTag = []byte("<body>")
 | 
					 | 
				
			||||||
var byteBodyTagClosing = []byte("</body>")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
					func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
				
			||||||
	if ctx.procs == nil {
 | 
						if ctx.procs == nil {
 | 
				
			||||||
		ctx.procs = defaultProcessors
 | 
							ctx.procs = defaultProcessors
 | 
				
			||||||
@@ -327,9 +324,9 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// give a generous extra 50 bytes
 | 
						// give a generous extra 50 bytes
 | 
				
			||||||
	res := make([]byte, 0, len(rawHTML)+50)
 | 
						res := make([]byte, 0, len(rawHTML)+50)
 | 
				
			||||||
	res = append(res, byteBodyTag...)
 | 
						res = append(res, "<html><body>"...)
 | 
				
			||||||
	res = append(res, rawHTML...)
 | 
						res = append(res, rawHTML...)
 | 
				
			||||||
	res = append(res, byteBodyTagClosing...)
 | 
						res = append(res, "</body></html>"...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// parse the HTML
 | 
						// parse the HTML
 | 
				
			||||||
	nodes, err := html.ParseFragment(bytes.NewReader(res), nil)
 | 
						nodes, err := html.ParseFragment(bytes.NewReader(res), nil)
 | 
				
			||||||
@@ -341,6 +338,31 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
				
			|||||||
		ctx.visitNode(node, true)
 | 
							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
 | 
						// 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
 | 
						// length will be at least that of res; to spare a few alloc+copy, we
 | 
				
			||||||
	// reuse res, resetting its length to 0.
 | 
						// reuse res, resetting its length to 0.
 | 
				
			||||||
@@ -353,12 +375,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.
 | 
						// Everything done successfully, return parsed data.
 | 
				
			||||||
	return res, nil
 | 
						return buf.Bytes(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ctx *postProcessCtx) visitNode(node *html.Node, visitText bool) {
 | 
					func (ctx *postProcessCtx) visitNode(node *html.Node, visitText bool) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>`,
 | 
				
			||||||
		`<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")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user