diff options
author | Joe Mooring <joe.mooring@veriphor.com> | 2023-11-24 15:51:27 -0800 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-11-27 10:52:33 +0100 |
commit | b4c5df42ff7d2542a661cf06b7a7acb03282bc5a (patch) | |
tree | b35e5e2a32ff657ea56f4544da6789d6161e4e3e /tpl | |
parent | ef12d169c81b0b82f6c2cd1a1ba680f04669c360 (diff) |
tpl/transform: Add transform.XMLEscape template function
Fixes #3268
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/tplimpl/embedded/templates/_default/rss.xml | 2 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs_test.go | 6 | ||||
-rw-r--r-- | tpl/transform/init.go | 10 | ||||
-rw-r--r-- | tpl/transform/integration_test.go | 20 | ||||
-rw-r--r-- | tpl/transform/transform.go | 31 |
5 files changed, 67 insertions, 2 deletions
diff --git a/tpl/tplimpl/embedded/templates/_default/rss.xml b/tpl/tplimpl/embedded/templates/_default/rss.xml index a4cdd2383..6818d31ec 100644 --- a/tpl/tplimpl/embedded/templates/_default/rss.xml +++ b/tpl/tplimpl/embedded/templates/_default/rss.xml @@ -64,7 +64,7 @@ <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate> {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }} <guid>{{ .Permalink }}</guid> - <description>{{ .Summary | html }}</description> + <description>{{ .Summary | transform.XMLEscape | safeHTML }}</description> </item> {{- end }} </channel> diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go index cb1aa6feb..9cc84934b 100644 --- a/tpl/tplimpl/template_funcs_test.go +++ b/tpl/tplimpl/template_funcs_test.go @@ -37,7 +37,7 @@ home=["HTML"] -- files/README.txt -- Hugo Rocks! -- content/blog/hugo-rocks.md -- ---- +--- title: "**BatMan**" --- ` @@ -65,6 +65,10 @@ title: "**BatMan**" // This will fail the build, so skip for now. continue } + if strings.Contains(example[0], "transform.XMLEscape") { + // This will fail the build, so skip for now. + continue + } templates = append(templates, example[0]) expected = append(expected, example[1]) } diff --git a/tpl/transform/init.go b/tpl/transform/init.go index 00ae8f89d..e43960427 100644 --- a/tpl/transform/init.go +++ b/tpl/transform/init.go @@ -112,6 +112,16 @@ func init() { }, ) + ns.AddMethodMapping(ctx.XMLEscape, + nil, + [][2]string{ + { + `{{ transform.XMLEscape "<p>abc</p>" }}`, + `<p>abc</p>`, + }, + }, + ) + return ns } diff --git a/tpl/transform/integration_test.go b/tpl/transform/integration_test.go index 17348928d..9a68b7ff2 100644 --- a/tpl/transform/integration_test.go +++ b/tpl/transform/integration_test.go @@ -65,3 +65,23 @@ foo b.AssertFileContent("public/p3/index.html", "_<h2 id=\"foo\">foo</h2>\n<p>bar</p>\n_") b.AssertFileContent("public/p4/index.html", "_<p id=\"bar\">foo</p>\n_") } + +func TestXMLEscape(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +disableKinds = ['section','sitemap','taxonomy','term'] +-- content/p1.md -- +--- +title: p1 +--- +a **b** c +<!--more--> + ` + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.xml", ` + <description><p>a <strong>b</strong> c</p></description> + `) +} diff --git a/tpl/transform/transform.go b/tpl/transform/transform.go index d943b0b57..178db84e4 100644 --- a/tpl/transform/transform.go +++ b/tpl/transform/transform.go @@ -15,9 +15,12 @@ package transform import ( + "bytes" "context" + "encoding/xml" "html" "html/template" + "strings" "github.com/gohugoio/hugo/cache/namedmemcache" "github.com/gohugoio/hugo/markup/converter/hooks" @@ -118,6 +121,34 @@ func (ns *Namespace) HTMLUnescape(s any) (string, error) { return html.UnescapeString(ss), nil } +// XMLEscape returns the given string, removing disallowed characters then +// escaping the result to its XML equivalent. +func (ns *Namespace) XMLEscape(s any) (string, error) { + ss, err := cast.ToStringE(s) + if err != nil { + return "", err + } + + // https://www.w3.org/TR/xml/#NT-Char + cleaned := strings.Map(func(r rune) rune { + if r == 0x9 || r == 0xA || r == 0xD || + (r >= 0x20 && r <= 0xD7FF) || + (r >= 0xE000 && r <= 0xFFFD) || + (r >= 0x10000 && r <= 0x10FFFF) { + return r + } + return -1 + }, ss) + + var buf bytes.Buffer + err = xml.EscapeText(&buf, []byte(cleaned)) + if err != nil { + return "", err + } + + return buf.String(), nil +} + // Markdownify renders s from Markdown to HTML. func (ns *Namespace) Markdownify(ctx context.Context, s any) (template.HTML, error) { |