+3
-1
appview/pages/funcmap.go
+3
-1
appview/pages/funcmap.go
···
145
145
},
146
146
"markdown": func(text string) template.HTML {
147
147
rctx := &markup.RenderContext{RendererType: markup.RendererTypeDefault}
148
-
return template.HTML(bluemonday.UGCPolicy().Sanitize(rctx.RenderMarkdown(text)))
148
+
return template.HTML(
149
+
rctx.RenderMarkdown(text, bluemonday.UGCPolicy().Sanitize),
150
+
)
149
151
},
150
152
"isNil": func(t any) bool {
151
153
// returns false for other "zero" values
+10
-3
appview/pages/markup/markdown.go
+10
-3
appview/pages/markup/markdown.go
···
44
44
RendererType RendererType
45
45
}
46
46
47
-
func (rctx *RenderContext) RenderMarkdown(source string) string {
47
+
// RenderMarkdown renders the given markdown source into sanitized HTML. sanitizer is used to sanitize the HTML output.
48
+
func (rctx *RenderContext) RenderMarkdown(source string, sanitizer func(string) string) string {
48
49
md := goldmark.New(
49
50
goldmark.WithExtensions(extension.GFM,
50
51
highlighting.NewHighlighting(
···
74
75
return source
75
76
}
76
77
78
+
sanitizedHtml := sanitizer(buf.String())
79
+
77
80
var processed strings.Builder
78
-
if err := postProcess(rctx, strings.NewReader(buf.String()), &processed); err != nil {
81
+
if err := postProcessSanitizedHtml(rctx, strings.NewReader(sanitizedHtml), &processed); err != nil {
79
82
return source
80
83
}
81
84
82
85
return processed.String()
83
86
}
84
87
85
-
func postProcess(ctx *RenderContext, input io.Reader, output io.Writer) error {
88
+
// postProcessSanitizedHtml processes the HTML output from the markdown renderer.
89
+
// WARNING: Do not insert raw HTML from user-controlled input. Sanitization already happened beforehand at this point.
90
+
func postProcessSanitizedHtml(ctx *RenderContext, input io.Reader, output io.Writer) error {
86
91
node, err := htmlparse.Parse(io.MultiReader(
87
92
strings.NewReader("<html><body>"),
88
93
input,
···
127
132
return nil
128
133
}
129
134
135
+
// visitNode is called on every node of a SANITIZED html document.
136
+
// WARNING: Do not insert raw HTML from user-controlled input. Sanitization already happened beforehand at this point.
130
137
func visitNode(ctx *RenderContext, node *htmlparse.Node) {
131
138
switch node.Type {
132
139
case htmlparse.ElementNode:
+4
-4
appview/pages/pages.go
+4
-4
appview/pages/pages.go
···
432
432
ext := filepath.Ext(params.ReadmeFileName)
433
433
switch ext {
434
434
case ".md", ".markdown", ".mdown", ".mkdn", ".mkd":
435
-
htmlString = p.rctx.RenderMarkdown(params.Readme)
435
+
htmlString = p.rctx.RenderMarkdown(params.Readme, p.rctx.Sanitize)
436
436
params.Raw = false
437
-
params.HTMLReadme = template.HTML(p.rctx.Sanitize(htmlString))
437
+
params.HTMLReadme = template.HTML(htmlString)
438
438
default:
439
439
htmlString = string(params.Readme)
440
440
params.Raw = true
···
564
564
case markup.FormatMarkdown:
565
565
p.rctx.RepoInfo = params.RepoInfo
566
566
p.rctx.RendererType = markup.RendererTypeRepoMarkdown
567
-
htmlString := p.rctx.RenderMarkdown(params.Contents)
568
-
params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString))
567
+
htmlString := p.rctx.RenderMarkdown(params.Contents, p.rctx.Sanitize)
568
+
params.RenderedContents = template.HTML(htmlString)
569
569
}
570
570
}
571
571