Skip to content

Commit a09b40d

Browse files
authored
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.
1 parent 381e131 commit a09b40d

File tree

3 files changed

+48
-64
lines changed

3 files changed

+48
-64
lines changed

modules/markup/markdown/markdown.go

+45-59
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,8 @@ var urlPrefixKey = parser.NewContextKey()
3535
var isWikiKey = parser.NewContextKey()
3636
var renderMetasKey = parser.NewContextKey()
3737

38-
type closesWithError interface {
39-
io.WriteCloser
40-
CloseWithError(err error) error
41-
}
42-
4338
type limitWriter struct {
44-
w closesWithError
39+
w io.Writer
4540
sum int64
4641
limit int64
4742
}
@@ -55,24 +50,13 @@ func (l *limitWriter) Write(data []byte) (int, error) {
5550
if err != nil {
5651
return n, err
5752
}
58-
_ = l.w.Close()
5953
return n, fmt.Errorf("Rendered content too large - truncating render")
6054
}
6155
n, err := l.w.Write(data)
6256
l.sum += int64(n)
6357
return n, err
6458
}
6559

66-
// Close closes the writer
67-
func (l *limitWriter) Close() error {
68-
return l.w.Close()
69-
}
70-
71-
// CloseWithError closes the writer
72-
func (l *limitWriter) CloseWithError(err error) error {
73-
return l.w.CloseWithError(err)
74-
}
75-
7660
// newParserContext creates a parser.Context with the render context set
7761
func newParserContext(ctx *markup.RenderContext) parser.Context {
7862
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
@@ -153,66 +137,54 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
153137

154138
})
155139

156-
rd, wr := io.Pipe()
157-
defer func() {
158-
_ = rd.Close()
159-
_ = wr.Close()
160-
}()
161-
162140
lw := &limitWriter{
163-
w: wr,
141+
w: output,
164142
limit: setting.UI.MaxDisplayFileSize * 3,
165143
}
166144

167-
// FIXME: should we include a timeout that closes the pipe to abort the renderer and sanitizer if it takes too long?
168-
go func() {
169-
defer func() {
170-
err := recover()
171-
if err == nil {
172-
return
173-
}
174-
175-
log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
176-
if log.IsDebug() {
177-
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
178-
}
179-
_ = lw.CloseWithError(fmt.Errorf("%v", err))
180-
}()
181-
182-
// FIXME: Don't read all to memory, but goldmark doesn't support
183-
pc := newParserContext(ctx)
184-
buf, err := io.ReadAll(input)
185-
if err != nil {
186-
log.Error("Unable to ReadAll: %v", err)
145+
// FIXME: should we include a timeout to abort the renderer if it takes too long?
146+
defer func() {
147+
err := recover()
148+
if err == nil {
187149
return
188150
}
189-
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
190-
log.Error("Unable to render: %v", err)
191-
_ = lw.CloseWithError(err)
192-
return
151+
152+
log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
153+
if log.IsDebug() {
154+
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
193155
}
194-
_ = lw.Close()
195156
}()
196-
buf := markup.SanitizeReader(rd, "")
197-
_, err := io.Copy(output, buf)
198-
return err
157+
158+
// FIXME: Don't read all to memory, but goldmark doesn't support
159+
pc := newParserContext(ctx)
160+
buf, err := io.ReadAll(input)
161+
if err != nil {
162+
log.Error("Unable to ReadAll: %v", err)
163+
return err
164+
}
165+
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
166+
log.Error("Unable to render: %v", err)
167+
return err
168+
}
169+
170+
return nil
199171
}
200172

173+
// Note: The output of this method must get sanitized.
201174
func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
202175
defer func() {
203176
err := recover()
204177
if err == nil {
205178
return
206179
}
207180

208-
log.Warn("Unable to render markdown due to panic in goldmark - will return sanitized raw bytes")
181+
log.Warn("Unable to render markdown due to panic in goldmark - will return raw bytes")
209182
if log.IsDebug() {
210183
log.Debug("Panic in markdown: %v\n%s", err, string(log.Stack(2)))
211184
}
212-
ret := markup.SanitizeReader(input, "")
213-
_, err = io.Copy(output, ret)
185+
_, err = io.Copy(output, input)
214186
if err != nil {
215-
log.Error("SanitizeReader failed: %v", err)
187+
log.Error("io.Copy failed: %v", err)
216188
}
217189
}()
218190
return actualRender(ctx, input, output)
@@ -255,8 +227,8 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
255227

256228
// Render renders Markdown to HTML with all specific handling stuff.
257229
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
258-
if ctx.Filename == "" {
259-
ctx.Filename = "a.md"
230+
if ctx.Type == "" {
231+
ctx.Type = MarkupName
260232
}
261233
return markup.Render(ctx, input, output)
262234
}
@@ -272,7 +244,21 @@ func RenderString(ctx *markup.RenderContext, content string) (string, error) {
272244

273245
// RenderRaw renders Markdown to HTML without handling special links.
274246
func RenderRaw(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
275-
return render(ctx, input, output)
247+
rd, wr := io.Pipe()
248+
defer func() {
249+
_ = rd.Close()
250+
_ = wr.Close()
251+
}()
252+
253+
go func() {
254+
if err := render(ctx, input, wr); err != nil {
255+
_ = wr.CloseWithError(err)
256+
return
257+
}
258+
_ = wr.Close()
259+
}()
260+
261+
return markup.SanitizeReader(rd, "", output)
276262
}
277263

278264
// RenderRawString renders Markdown to HTML without handling special links and return string

modules/markup/renderer.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
144144

145145
wg.Add(1)
146146
go func() {
147-
buf := SanitizeReader(pr2, renderer.Name())
148-
_, err = io.Copy(output, buf)
147+
err = SanitizeReader(pr2, renderer.Name(), output)
149148
_ = pr2.Close()
150149
wg.Done()
151150
}()

modules/markup/sanitizer.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package markup
77

88
import (
9-
"bytes"
109
"io"
1110
"regexp"
1211
"sync"
@@ -149,11 +148,11 @@ func Sanitize(s string) string {
149148
}
150149

151150
// SanitizeReader sanitizes a Reader
152-
func SanitizeReader(r io.Reader, renderer string) *bytes.Buffer {
151+
func SanitizeReader(r io.Reader, renderer string, w io.Writer) error {
153152
NewSanitizer()
154153
policy, exist := sanitizer.rendererPolicies[renderer]
155154
if !exist {
156155
policy = sanitizer.defaultPolicy
157156
}
158-
return policy.SanitizeReader(r)
157+
return policy.SanitizeReaderToWriter(r, w)
159158
}

0 commit comments

Comments
 (0)