summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
authorJoe Mooring <joe.mooring@veriphor.com>2023-11-24 15:51:27 -0800
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-11-27 10:52:33 +0100
commitb4c5df42ff7d2542a661cf06b7a7acb03282bc5a (patch)
treeb35e5e2a32ff657ea56f4544da6789d6161e4e3e /tpl
parentef12d169c81b0b82f6c2cd1a1ba680f04669c360 (diff)
tpl/transform: Add transform.XMLEscape template function
Fixes #3268
Diffstat (limited to 'tpl')
-rw-r--r--tpl/tplimpl/embedded/templates/_default/rss.xml2
-rw-r--r--tpl/tplimpl/template_funcs_test.go6
-rw-r--r--tpl/transform/init.go10
-rw-r--r--tpl/transform/integration_test.go20
-rw-r--r--tpl/transform/transform.go31
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>" }}`,
+ `&lt;p&gt;abc&lt;/p&gt;`,
+ },
+ },
+ )
+
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>&lt;p&gt;a &lt;strong&gt;b&lt;/strong&gt; c&lt;/p&gt;</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) {