From 86f45a8581d379fc445a020d3ad9381c6920e9fa Mon Sep 17 00:00:00 2001 From: Gwenn Le Bihan Date: Wed, 21 May 2025 21:06:08 +0200 Subject: [PATCH] deps: add goldmark-highlighting --- go.mod | 3 ++- go.sum | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index be38114..4ba144c 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,8 @@ require ( github.com/resend/resend-go/v2 v2.15.0 github.com/sethvargo/go-envconfig v1.1.0 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e - github.com/yuin/goldmark v1.4.13 + github.com/yuin/goldmark v1.4.15 + github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc golang.org/x/net v0.39.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 ) diff --git a/go.sum b/go.sum index 48fa30b..ba0b443 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,10 @@ github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGh github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= +github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -55,6 +57,8 @@ github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dr github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -279,8 +283,11 @@ github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.4.15 h1:CFa84T0goNn/UIXYS+dmjjVxMyTAvpOmzld40N/nfK0= +github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= -- 2.43.0 From c1694d3fca3e3f3696b622f1ef4d617e83c2cc5f Mon Sep 17 00:00:00 2001 From: Gwenn Le Bihan Date: Wed, 21 May 2025 21:06:32 +0200 Subject: [PATCH] appview: highlight rendered markdown code blocks --- appview/pages/markup/markdown.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go index 1186108..21d3624 100644 --- a/appview/pages/markup/markdown.go +++ b/appview/pages/markup/markdown.go @@ -9,8 +9,10 @@ import ( "path" "strings" + "github.com/alecthomas/chroma/v2/styles" "github.com/microcosm-cc/bluemonday" "github.com/yuin/goldmark" + "github.com/yuin/goldmark-highlighting/v2" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" @@ -44,7 +46,13 @@ type RenderContext struct { func (rctx *RenderContext) RenderMarkdown(source string) string { md := goldmark.New( - goldmark.WithExtensions(extension.GFM), + goldmark.WithExtensions(extension.GFM, + highlighting.NewHighlighting( + highlighting.WithCustomStyle( + styles.Get("catppuccin-latte"), + ), + ), + ), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), -- 2.43.0 From bea3f741c8e317a5b43cdb0755fe4146cf08ef23 Mon Sep 17 00:00:00 2001 From: Gwenn Le Bihan Date: Wed, 21 May 2025 23:21:27 +0200 Subject: [PATCH] appview: post-process rendered markdown after sanitization also ensures that every (RunContext).RenderMarkdown callsite gets back a sanitized value (or very loudly decides not to sanitize by passing e.g. func(s string) string { return s } as the sanitizer argument) --- appview/pages/funcmap.go | 4 +++- appview/pages/markup/markdown.go | 13 ++++++++++--- appview/pages/pages.go | 8 ++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go index 038e745..cbde4cf 100644 --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -145,7 +145,9 @@ func funcMap() template.FuncMap { }, "markdown": func(text string) template.HTML { rctx := &markup.RenderContext{RendererType: markup.RendererTypeDefault} - return template.HTML(bluemonday.UGCPolicy().Sanitize(rctx.RenderMarkdown(text))) + return template.HTML( + rctx.RenderMarkdown(text, bluemonday.UGCPolicy().Sanitize), + ) }, "isNil": func(t any) bool { // returns false for other "zero" values diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go index 21d3624..d63882d 100644 --- a/appview/pages/markup/markdown.go +++ b/appview/pages/markup/markdown.go @@ -44,7 +44,8 @@ type RenderContext struct { RendererType RendererType } -func (rctx *RenderContext) RenderMarkdown(source string) string { +// RenderMarkdown renders the given markdown source into sanitized HTML. sanitizer is used to sanitize the HTML output. +func (rctx *RenderContext) RenderMarkdown(source string, sanitizer func(string) string) string { md := goldmark.New( goldmark.WithExtensions(extension.GFM, highlighting.NewHighlighting( @@ -74,15 +75,19 @@ func (rctx *RenderContext) RenderMarkdown(source string) string { return source } + sanitizedHtml := sanitizer(buf.String()) + var processed strings.Builder - if err := postProcess(rctx, strings.NewReader(buf.String()), &processed); err != nil { + if err := postProcessSanitizedHtml(rctx, strings.NewReader(sanitizedHtml), &processed); err != nil { return source } return processed.String() } -func postProcess(ctx *RenderContext, input io.Reader, output io.Writer) error { +// postProcessSanitizedHtml processes the HTML output from the markdown renderer. +// WARNING: Do not insert raw HTML from user-controlled input. Sanitization already happened beforehand at this point. +func postProcessSanitizedHtml(ctx *RenderContext, input io.Reader, output io.Writer) error { node, err := htmlparse.Parse(io.MultiReader( strings.NewReader(""), input, @@ -127,6 +132,8 @@ func postProcess(ctx *RenderContext, input io.Reader, output io.Writer) error { return nil } +// visitNode is called on every node of a SANITIZED html document. +// WARNING: Do not insert raw HTML from user-controlled input. Sanitization already happened beforehand at this point. func visitNode(ctx *RenderContext, node *htmlparse.Node) { switch node.Type { case htmlparse.ElementNode: diff --git a/appview/pages/pages.go b/appview/pages/pages.go index bfd04c2..f64acdf 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -432,9 +432,9 @@ func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error { ext := filepath.Ext(params.ReadmeFileName) switch ext { case ".md", ".markdown", ".mdown", ".mkdn", ".mkd": - htmlString = p.rctx.RenderMarkdown(params.Readme) + htmlString = p.rctx.RenderMarkdown(params.Readme, p.rctx.Sanitize) params.Raw = false - params.HTMLReadme = template.HTML(p.rctx.Sanitize(htmlString)) + params.HTMLReadme = template.HTML(htmlString) default: htmlString = string(params.Readme) params.Raw = true @@ -564,8 +564,8 @@ func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error { case markup.FormatMarkdown: p.rctx.RepoInfo = params.RepoInfo p.rctx.RendererType = markup.RendererTypeRepoMarkdown - htmlString := p.rctx.RenderMarkdown(params.Contents) - params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString)) + htmlString := p.rctx.RenderMarkdown(params.Contents, p.rctx.Sanitize) + params.RenderedContents = template.HTML(htmlString) } } -- 2.43.0 From 5e5ce5ad378e34b57f6abc04945a7c927d032a82 Mon Sep 17 00:00:00 2001 From: Gwenn Le Bihan Date: Wed, 21 May 2025 23:23:59 +0200 Subject: [PATCH] appview: hide [aria-hidden="true"] elements --- input.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/input.css b/input.css index 8ffc7ff..7e912d7 100644 --- a/input.css +++ b/input.css @@ -104,6 +104,11 @@ } } +/* Hidden elements */ +[aria-hidden="true"] { + display: none ; +} + /* Background */ .bg { color: #4c4f69; -- 2.43.0 From 5ef95807c4c7c5e0dcd9c11bac748f95772ec73c Mon Sep 17 00:00:00 2001 From: Gwenn Le Bihan Date: Wed, 21 May 2025 21:40:38 +0200 Subject: [PATCH] (wip)appview: add copy button right now, only the textnode inside the button gets added, without the