Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent double sanitize #16386

Merged
merged 8 commits into from
Nov 19, 2021
Merged
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
104 changes: 45 additions & 59 deletions modules/markup/markdown/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,8 @@ var urlPrefixKey = parser.NewContextKey()
var isWikiKey = parser.NewContextKey()
var renderMetasKey = parser.NewContextKey()

type closesWithError interface {
io.WriteCloser
CloseWithError(err error) error
}

type limitWriter struct {
w closesWithError
w io.Writer
sum int64
limit int64
}
Expand All @@ -55,24 +50,13 @@ func (l *limitWriter) Write(data []byte) (int, error) {
if err != nil {
return n, err
}
_ = l.w.Close()
return n, fmt.Errorf("Rendered content too large - truncating render")
}
n, err := l.w.Write(data)
l.sum += int64(n)
return n, err
}

// Close closes the writer
func (l *limitWriter) Close() error {
return l.w.Close()
}

// CloseWithError closes the writer
func (l *limitWriter) CloseWithError(err error) error {
return l.w.CloseWithError(err)
}

// newParserContext creates a parser.Context with the render context set
func newParserContext(ctx *markup.RenderContext) parser.Context {
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
Expand Down Expand Up @@ -153,66 +137,54 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)

})

rd, wr := io.Pipe()
defer func() {
_ = rd.Close()
_ = wr.Close()
}()

lw := &limitWriter{
w: wr,
w: output,
limit: setting.UI.MaxDisplayFileSize * 3,
}

// FIXME: should we include a timeout that closes the pipe to abort the renderer and sanitizer if it takes too long?
go func() {
defer func() {
err := recover()
if err == nil {
return
}

log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
if log.IsDebug() {
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
}
_ = lw.CloseWithError(fmt.Errorf("%v", err))
}()

// FIXME: Don't read all to memory, but goldmark doesn't support
pc := newParserContext(ctx)
buf, err := io.ReadAll(input)
if err != nil {
log.Error("Unable to ReadAll: %v", err)
// FIXME: should we include a timeout to abort the renderer if it takes too long?
defer func() {
err := recover()
if err == nil {
return
}
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
log.Error("Unable to render: %v", err)
_ = lw.CloseWithError(err)
return

log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
if log.IsDebug() {
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
}
_ = lw.Close()
}()
buf := markup.SanitizeReader(rd, "")
_, err := io.Copy(output, buf)
return err

// FIXME: Don't read all to memory, but goldmark doesn't support
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cliff hanger here, reading it for the first time doesn't immediately hint me what goldmark doesn't support.

pc := newParserContext(ctx)
buf, err := io.ReadAll(input)
if err != nil {
log.Error("Unable to ReadAll: %v", err)
return err
}
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
log.Error("Unable to render: %v", err)
return err
}

return nil
}

// Note: The output of this method must get sanitized.
func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
KN4CK3R marked this conversation as resolved.
Show resolved Hide resolved
defer func() {
err := recover()
if err == nil {
return
}

log.Warn("Unable to render markdown due to panic in goldmark - will return sanitized raw bytes")
log.Warn("Unable to render markdown due to panic in goldmark - will return raw bytes")
if log.IsDebug() {
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
}
ret := markup.SanitizeReader(input, "")
_, err = io.Copy(output, ret)
_, err = io.Copy(output, input)
if err != nil {
log.Error("SanitizeReader failed: %v", err)
log.Error("io.Copy failed: %v", err)
}
}()
return actualRender(ctx, input, output)
Expand Down Expand Up @@ -255,8 +227,8 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri

// Render renders Markdown to HTML with all specific handling stuff.
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
if ctx.Filename == "" {
ctx.Filename = "a.md"
if ctx.Type == "" {
ctx.Type = MarkupName
}
return markup.Render(ctx, input, output)
}
Expand All @@ -272,7 +244,21 @@ func RenderString(ctx *markup.RenderContext, content string) (string, error) {

// RenderRaw renders Markdown to HTML without handling special links.
func RenderRaw(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
return render(ctx, input, output)
rd, wr := io.Pipe()
defer func() {
_ = rd.Close()
_ = wr.Close()
}()

go func() {
if err := render(ctx, input, wr); err != nil {
_ = wr.CloseWithError(err)
return
}
_ = wr.Close()
}()

return markup.SanitizeReader(rd, "", output)
}

// RenderRawString renders Markdown to HTML without handling special links and return string
Expand Down
3 changes: 1 addition & 2 deletions modules/markup/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr

wg.Add(1)
go func() {
buf := SanitizeReader(pr2, renderer.Name())
_, err = io.Copy(output, buf)
err = SanitizeReader(pr2, renderer.Name(), output)
_ = pr2.Close()
wg.Done()
}()
Expand Down
5 changes: 2 additions & 3 deletions modules/markup/sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package markup

import (
"bytes"
"io"
"regexp"
"sync"
Expand Down Expand Up @@ -149,11 +148,11 @@ func Sanitize(s string) string {
}

// SanitizeReader sanitizes a Reader
func SanitizeReader(r io.Reader, renderer string) *bytes.Buffer {
func SanitizeReader(r io.Reader, renderer string, w io.Writer) error {
NewSanitizer()
policy, exist := sanitizer.rendererPolicies[renderer]
if !exist {
policy = sanitizer.defaultPolicy
}
return policy.SanitizeReader(r)
return policy.SanitizeReaderToWriter(r, w)
}