6
6
package markdown
7
7
8
8
import (
9
- "bytes"
9
+ "fmt"
10
+ "io"
10
11
"strings"
11
12
"sync"
12
13
@@ -18,7 +19,7 @@ import (
18
19
19
20
chromahtml "github.com/alecthomas/chroma/formatters/html"
20
21
"github.com/yuin/goldmark"
21
- "github.com/yuin/goldmark-highlighting"
22
+ highlighting "github.com/yuin/goldmark-highlighting"
22
23
meta "github.com/yuin/goldmark-meta"
23
24
"github.com/yuin/goldmark/extension"
24
25
"github.com/yuin/goldmark/parser"
@@ -34,6 +35,44 @@ var urlPrefixKey = parser.NewContextKey()
34
35
var isWikiKey = parser .NewContextKey ()
35
36
var renderMetasKey = parser .NewContextKey ()
36
37
38
+ type closesWithError interface {
39
+ io.WriteCloser
40
+ CloseWithError (err error ) error
41
+ }
42
+
43
+ type limitWriter struct {
44
+ w closesWithError
45
+ sum int64
46
+ limit int64
47
+ }
48
+
49
+ // Write implements the standard Write interface:
50
+ func (l * limitWriter ) Write (data []byte ) (int , error ) {
51
+ leftToWrite := l .limit - l .sum
52
+ if leftToWrite < int64 (len (data )) {
53
+ n , err := l .w .Write (data [:leftToWrite ])
54
+ l .sum += int64 (n )
55
+ if err != nil {
56
+ return n , err
57
+ }
58
+ _ = l .w .Close ()
59
+ return n , fmt .Errorf ("Rendered content too large - truncating render" )
60
+ }
61
+ n , err := l .w .Write (data )
62
+ l .sum += int64 (n )
63
+ return n , err
64
+ }
65
+
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
+
37
76
// NewGiteaParseContext creates a parser.Context with the gitea context set
38
77
func NewGiteaParseContext (urlPrefix string , metas map [string ]string , isWiki bool ) parser.Context {
39
78
pc := parser .NewContext (parser .WithIDs (newPrefixedIDs ()))
@@ -43,8 +82,8 @@ func NewGiteaParseContext(urlPrefix string, metas map[string]string, isWiki bool
43
82
return pc
44
83
}
45
84
46
- // render renders Markdown to HTML without handling special links.
47
- func render (body []byte , urlPrefix string , metas map [string ]string , wikiMarkdown bool ) []byte {
85
+ // actualRender renders Markdown to HTML without handling special links.
86
+ func actualRender (body []byte , urlPrefix string , metas map [string ]string , wikiMarkdown bool ) []byte {
48
87
once .Do (func () {
49
88
converter = goldmark .New (
50
89
goldmark .WithExtensions (extension .Table ,
@@ -119,12 +158,57 @@ func render(body []byte, urlPrefix string, metas map[string]string, wikiMarkdown
119
158
120
159
})
121
160
122
- pc := NewGiteaParseContext (urlPrefix , metas , wikiMarkdown )
123
- var buf bytes.Buffer
124
- if err := converter .Convert (giteautil .NormalizeEOL (body ), & buf , parser .WithContext (pc )); err != nil {
125
- log .Error ("Unable to render: %v" , err )
161
+ rd , wr := io .Pipe ()
162
+ defer func () {
163
+ _ = rd .Close ()
164
+ _ = wr .Close ()
165
+ }()
166
+
167
+ lw := & limitWriter {
168
+ w : wr ,
169
+ limit : setting .UI .MaxDisplayFileSize * 3 ,
126
170
}
127
- return markup .SanitizeReader (& buf ).Bytes ()
171
+
172
+ // FIXME: should we include a timeout that closes the pipe to abort the parser and sanitizer if it takes too long?
173
+ go func () {
174
+ defer func () {
175
+ err := recover ()
176
+ if err == nil {
177
+ return
178
+ }
179
+
180
+ log .Warn ("Unable to render markdown due to panic in goldmark: %v" , err )
181
+ if log .IsDebug () {
182
+ log .Debug ("Panic in markdown: %v\n %s" , err , string (log .Stack (2 )))
183
+ }
184
+ _ = lw .CloseWithError (fmt .Errorf ("%v" , err ))
185
+ }()
186
+
187
+ pc := NewGiteaParseContext (urlPrefix , metas , wikiMarkdown )
188
+ if err := converter .Convert (giteautil .NormalizeEOL (body ), lw , parser .WithContext (pc )); err != nil {
189
+ log .Error ("Unable to render: %v" , err )
190
+ _ = lw .CloseWithError (err )
191
+ return
192
+ }
193
+ _ = lw .Close ()
194
+ }()
195
+ return markup .SanitizeReader (rd ).Bytes ()
196
+ }
197
+
198
+ func render (body []byte , urlPrefix string , metas map [string ]string , wikiMarkdown bool ) (ret []byte ) {
199
+ defer func () {
200
+ err := recover ()
201
+ if err == nil {
202
+ return
203
+ }
204
+
205
+ log .Warn ("Unable to render markdown due to panic in goldmark - will return sanitized raw bytes" )
206
+ if log .IsDebug () {
207
+ log .Debug ("Panic in markdown: %v\n %s" , err , string (log .Stack (2 )))
208
+ }
209
+ ret = markup .SanitizeBytes (body )
210
+ }()
211
+ return actualRender (body , urlPrefix , metas , wikiMarkdown )
128
212
}
129
213
130
214
var (
0 commit comments