mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-24 20:52:45 +01:00
Prevent double sanitize (#16386)
* Prevent double sanitize. * Use SanitizeReaderToWriter. At the moment `actualRender` uses `SanitizeReader` to sanitize the output. But `SanitizeReader` gets called in `markup.render` too so the output gets sanitized twice. I moved the `SanitizeReader` call into `RenderRaw` because this method does not use `markup.render`. I would like to remove the `RenderRaw`/`RenderRawString` methods too because they are only called from tests, the fuzzer and the `/markup/raw` api endpoint. This endpoint is not in use so I think we could remove them. If we really in the future need a method to render markdown without PostProcessing we could achieve this with a more flexible `renderer.NeedPostProcess` method.
This commit is contained in:
parent
381e131fc8
commit
a09b40de8d
3 changed files with 48 additions and 64 deletions
|
@ -35,13 +35,8 @@ var urlPrefixKey = parser.NewContextKey()
|
||||||
var isWikiKey = parser.NewContextKey()
|
var isWikiKey = parser.NewContextKey()
|
||||||
var renderMetasKey = parser.NewContextKey()
|
var renderMetasKey = parser.NewContextKey()
|
||||||
|
|
||||||
type closesWithError interface {
|
|
||||||
io.WriteCloser
|
|
||||||
CloseWithError(err error) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type limitWriter struct {
|
type limitWriter struct {
|
||||||
w closesWithError
|
w io.Writer
|
||||||
sum int64
|
sum int64
|
||||||
limit int64
|
limit int64
|
||||||
}
|
}
|
||||||
|
@ -55,7 +50,6 @@ func (l *limitWriter) Write(data []byte) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
_ = l.w.Close()
|
|
||||||
return n, fmt.Errorf("Rendered content too large - truncating render")
|
return n, fmt.Errorf("Rendered content too large - truncating render")
|
||||||
}
|
}
|
||||||
n, err := l.w.Write(data)
|
n, err := l.w.Write(data)
|
||||||
|
@ -63,16 +57,6 @@ func (l *limitWriter) Write(data []byte) (int, error) {
|
||||||
return n, err
|
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
|
// newParserContext creates a parser.Context with the render context set
|
||||||
func newParserContext(ctx *markup.RenderContext) parser.Context {
|
func newParserContext(ctx *markup.RenderContext) parser.Context {
|
||||||
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
||||||
|
@ -153,51 +137,40 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
rd, wr := io.Pipe()
|
|
||||||
defer func() {
|
|
||||||
_ = rd.Close()
|
|
||||||
_ = wr.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
lw := &limitWriter{
|
lw := &limitWriter{
|
||||||
w: wr,
|
w: output,
|
||||||
limit: setting.UI.MaxDisplayFileSize * 3,
|
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?
|
// FIXME: should we include a timeout to abort the renderer if it takes too long?
|
||||||
go func() {
|
defer func() {
|
||||||
defer func() {
|
err := recover()
|
||||||
err := recover()
|
if err == nil {
|
||||||
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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
|
|
||||||
log.Error("Unable to render: %v", err)
|
log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
|
||||||
_ = lw.CloseWithError(err)
|
if log.IsDebug() {
|
||||||
return
|
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
|
||||||
}
|
}
|
||||||
_ = lw.Close()
|
|
||||||
}()
|
}()
|
||||||
buf := markup.SanitizeReader(rd, "")
|
|
||||||
_, err := io.Copy(output, buf)
|
// FIXME: Don't read all to memory, but goldmark doesn't support
|
||||||
return err
|
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 {
|
func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
err := recover()
|
err := recover()
|
||||||
|
@ -205,14 +178,13 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
||||||
return
|
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() {
|
if log.IsDebug() {
|
||||||
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
|
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
|
||||||
}
|
}
|
||||||
ret := markup.SanitizeReader(input, "")
|
_, err = io.Copy(output, input)
|
||||||
_, err = io.Copy(output, ret)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("SanitizeReader failed: %v", err)
|
log.Error("io.Copy failed: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return actualRender(ctx, input, output)
|
return actualRender(ctx, input, output)
|
||||||
|
@ -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.
|
// Render renders Markdown to HTML with all specific handling stuff.
|
||||||
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||||
if ctx.Filename == "" {
|
if ctx.Type == "" {
|
||||||
ctx.Filename = "a.md"
|
ctx.Type = MarkupName
|
||||||
}
|
}
|
||||||
return markup.Render(ctx, input, output)
|
return markup.Render(ctx, input, output)
|
||||||
}
|
}
|
||||||
|
@ -272,7 +244,21 @@ func RenderString(ctx *markup.RenderContext, content string) (string, error) {
|
||||||
|
|
||||||
// RenderRaw renders Markdown to HTML without handling special links.
|
// RenderRaw renders Markdown to HTML without handling special links.
|
||||||
func RenderRaw(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
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
|
// RenderRawString renders Markdown to HTML without handling special links and return string
|
||||||
|
|
|
@ -144,8 +144,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
buf := SanitizeReader(pr2, renderer.Name())
|
err = SanitizeReader(pr2, renderer.Name(), output)
|
||||||
_, err = io.Copy(output, buf)
|
|
||||||
_ = pr2.Close()
|
_ = pr2.Close()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -149,11 +148,11 @@ func Sanitize(s string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SanitizeReader sanitizes a Reader
|
// 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()
|
NewSanitizer()
|
||||||
policy, exist := sanitizer.rendererPolicies[renderer]
|
policy, exist := sanitizer.rendererPolicies[renderer]
|
||||||
if !exist {
|
if !exist {
|
||||||
policy = sanitizer.defaultPolicy
|
policy = sanitizer.defaultPolicy
|
||||||
}
|
}
|
||||||
return policy.SanitizeReader(r)
|
return policy.SanitizeReaderToWriter(r, w)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue