Skip to content

Commit 54398f8

Browse files
jmooringbep
authored andcommitted
tpl/tplimpl: Escape Markdown attributes in render hooks and shortcodes
1 parent b8c15f2 commit 54398f8

File tree

7 files changed

+74
-68
lines changed

7 files changed

+74
-68
lines changed

hugolib/content_render_hooks_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ baseURL="https://example.org"
9090
[markup.goldmark]
9191
[markup.goldmark.renderer]
9292
unsafe = true
93-
93+
9494
`)
9595

9696
b.WithTemplates("index.html", `
@@ -223,16 +223,16 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
223223
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
224224
-- layouts/_default/single.html --
225225
{{ .Title }}|{{ .Content }}|$
226-
226+
227227
`
228228

229229
t.Run("Default multilingual", func(t *testing.T) {
230230
b := Test(t, files)
231231

232232
b.AssertFileContent("public/nn/p1/index.html",
233-
"p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
233+
"p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img src=\"/nn/p1/pixel.nn.png\" alt=\"Pixel\">")
234234
b.AssertFileContent("public/en/p1/index.html",
235-
"p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
235+
"p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img src=\"/nn/p1/pixel.nn.png\" alt=\"Pixel\">")
236236
})
237237

238238
t.Run("Disabled", func(t *testing.T) {
@@ -279,7 +279,7 @@ Image: ![alt-"<>&](/destination-"<> 'title-"<>&')
279279
if enabled {
280280
b.AssertFileContent("public/index.html",
281281
"Link: <a href=\"/destination-%22%3C%3E\" title=\"title-&#34;&lt;&gt;&amp;\">text-&quot;&lt;&gt;&amp;</a>",
282-
"img alt=\"alt-&quot;&lt;&gt;&amp;\" src=\"/destination-%22%3C%3E\" title=\"title-&#34;&lt;&gt;&amp;\">",
282+
"img src=\"/destination-%22%3C%3E\" alt=\"alt-&quot;&lt;&gt;&amp;\" title=\"title-&#34;&lt;&gt;&amp;\">",
283283
"&gt;&lt;script&gt;",
284284
)
285285
} else {

markup/goldmark/tables/tables_integration_test.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ title = true
8989
| Codecademy Hoodie | False | 42.99 |
9090
{.foo}
9191
92+
## Table 2
93+
94+
a|b
95+
---|---
96+
1|2
97+
{id="\"><script>alert()</script>"}
9298
9399
-- layouts/_default/single.html --
94100
Summary: {{ .Summary }}
@@ -97,7 +103,8 @@ Content: {{ .Content }}
97103
`
98104
b := hugolib.Test(t, files)
99105

100-
b.AssertFileContent("public/p1/index.html", "<table class=\"foo\">")
106+
b.AssertFileContent("public/p1/index.html", `<table class="foo">`)
107+
b.AssertFileContent("public/p1/index.html", `<table id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`)
101108
}
102109

103110
// Issue 12811.
@@ -166,14 +173,8 @@ title: "Home"
166173
| Codecademy Tee | False | 19.99 |
167174
| Codecademy Hoodie | False | 42.99 |
168175
169-
170-
171-
172-
173176
-- layouts/index.xml --
174177
Content: {{ .Content }}
175-
176-
177178
`
178179
b := hugolib.Test(t, files)
179180

Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{{- $u := urls.Parse .Destination -}}
22
{{- $src := $u.String -}}
33
{{- if not $u.IsAbs -}}
4-
{{- $path := strings.TrimPrefix "./" $u.Path }}
4+
{{- $path := strings.TrimPrefix "./" $u.Path -}}
55
{{- with or (.PageInner.Resources.Get $path) (resources.Get $path) -}}
66
{{- $src = .RelPermalink -}}
77
{{- with $u.RawQuery -}}
@@ -12,11 +12,12 @@
1212
{{- end -}}
1313
{{- end -}}
1414
{{- end -}}
15-
{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" (.Title | transform.HTMLEscape)) -}}
16-
<img
17-
{{- range $k, $v := $attributes -}}
15+
<img src="{{ $src }}" alt="{{ .Text }}"
16+
{{- with .Title }} title="{{ . }}" {{- end -}}
17+
{{- range $k, $v := .Attributes -}}
1818
{{- if $v -}}
19-
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
19+
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
2020
{{- end -}}
21-
{{- end -}}>
21+
{{- end -}}
22+
>
2223
{{- /**/ -}}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{{- $u := urls.Parse .Destination -}}
22
{{- $href := $u.String -}}
3-
{{- if strings.HasPrefix $u.String "#" }}
4-
{{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment }}
5-
{{- else if not $u.IsAbs -}}
6-
{{- $path := strings.TrimPrefix "./" $u.Path }}
3+
{{- if strings.HasPrefix $u.String "#" -}}
4+
{{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}}
5+
{{- else if and $href (not $u.IsAbs) -}}
6+
{{- $path := strings.TrimPrefix "./" $u.Path -}}
77
{{- with or
88
($.PageInner.GetPage $path)
99
($.PageInner.Resources.Get $path)
@@ -18,12 +18,5 @@
1818
{{- end -}}
1919
{{- end -}}
2020
{{- end -}}
21-
{{- $attributes := dict "href" $href "title" (.Title | transform.HTMLEscape) -}}
22-
<a
23-
{{- range $k, $v := $attributes -}}
24-
{{- if $v -}}
25-
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
26-
{{- end -}}
27-
{{- end -}}
28-
>{{ .Text }}</a>
21+
<a href="{{ $href }}" {{- with .Title }} title="{{ . }}" {{- end }}>{{ .Text }}</a>
2922
{{- /**/ -}}

tpl/tplimpl/embedded/templates/_default/_markup/render-table.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<table
22
{{- range $k, $v := .Attributes }}
33
{{- if $v }}
4-
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
4+
{{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr }}
55
{{- end }}
66
{{- end }}>
77
<thead>

tpl/tplimpl/embedded/templates/shortcodes/youtube.html

+25-32
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
{{- if not $pc.Disable }}
2727
{{- with $id := or (.Get "id") (.Get 0) }}
2828

29-
{{/* Set defaults. */}}
29+
{{- /* Set defaults. */}}
3030
{{- $allowFullScreen := "allowfullscreen" }}
3131
{{- $autoplay := 0 }}
3232
{{- $class := "" }}
@@ -70,23 +70,8 @@
7070
{{- $start := or ($.Get "start") $start }}
7171
{{- $title := or ($.Get "title") $title }}
7272

73-
{{- /* Determine host. */}}
74-
{{- $host := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" }}
75-
76-
{{- /* Set styles. */}}
77-
{{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }}
78-
{{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }}
79-
{{- if $class }}
80-
{{- $iframeStyle = "" }}
81-
{{- end }}
82-
83-
{{- /* Set class or style of wrapping div element. */}}
84-
{{- $divClassOrStyle := printf "style=%q" $divStyle }}
85-
{{- with $class }}
86-
{{- $divClassOrStyle = printf "class=%q" $class }}
87-
{{- end }}
88-
8973
{{- /* Define src attribute. */}}
74+
{{- $host := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" }}
9075
{{- $src := printf "https://%s/embed/%s" $host $id }}
9176
{{- $params := dict
9277
"autoplay" $autoplay
@@ -108,25 +93,33 @@
10893
{{- $src = printf "%s?%s" $src . }}
10994
{{- end }}
11095

96+
{{- /* Set div attributes. */}}
97+
{{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }}
98+
{{- if $class }}
99+
{{- $divStyle = "" }}
100+
{{- end }}
101+
111102
{{- /* Set iframe attributes. */}}
112-
{{- $iframeAttributes := dict
113-
"allow" "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
114-
"allowfullscreen" $allowFullScreen
115-
"loading" $loading
116-
"referrerpolicy" "strict-origin-when-cross-origin"
117-
"src" $src
118-
"style" $iframeStyle
119-
"title" $title
120-
}}
103+
{{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }}
104+
{{- if $class }}
105+
{{- $iframeStyle = "" }}
106+
{{- end }}
107+
{{- $allow := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }}
108+
{{- $referrerpolicy := "strict-origin-when-cross-origin" }}
121109

122110
{{- /* Render. */}}
123-
<div {{ $divClassOrStyle | safeHTMLAttr }}>
111+
<div
112+
{{- with $class }} class="{{ . }}" {{- end }}
113+
{{- with $divStyle }} style="{{ . | safeCSS }}" {{- end -}}
114+
>
124115
<iframe
125-
{{- range $k, $v := $iframeAttributes }}
126-
{{- if $v }}
127-
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
128-
{{- end }}
129-
{{- end }}
116+
{{- with $allow }} allow="{{ . }}" {{- end }}
117+
{{- with $allowFullScreen }} allowfullscreen="{{ . }}" {{- end }}
118+
{{- with $loading }} loading="{{ . }}" {{- end }}
119+
{{- with $referrerpolicy }} referrerpolicy="{{ . }}" {{- end }}
120+
{{- with $src }} src="{{ . }}" {{- end }}
121+
{{- with $iframeStyle}} style="{{ . | safeCSS }}" {{- end }}
122+
{{- with $title }} title="{{ . }}" {{- end -}}
130123
></iframe>
131124
</div>
132125
{{- else }}

tpl/tplimpl/render_hook_integration_test.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ title: s1/p3
9191
[430](p2/)
9292
[440](/s1/p2/)
9393
[450](../s1/p2/)
94+
95+
// empty
96+
[]()
9497
`
9598

9699
b := hugolib.Test(t, files)
@@ -122,6 +125,8 @@ title: s1/p3
122125
`<a href="/s1/p2/">430</a>`,
123126
`<a href="/s1/p2/">440</a>`,
124127
`<a href="/s1/p2/">450</a>`,
128+
129+
`<a href=""></a>`,
125130
)
126131

127132
b.AssertFileContent("public/s1/p2/index.html",
@@ -148,10 +153,17 @@ block = false
148153
[markup.goldmark.renderHooks.image]
149154
enableDefault = true
150155
-- content/p1/index.md --
156+
![]()
157+
151158
![alt1](./pixel.png)
152159
153-
![alt2](pixel.png?a=b&c=d#fragment)
160+
![alt2-&<>'](pixel.png "&<>'")
161+
162+
![alt3](pixel.png?a=b&c=d#fragment)
154163
{.foo #bar}
164+
165+
![alt4](pixel.png)
166+
{id="\"><script>alert()</script>"}
155167
-- content/p1/pixel.png --
156168
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
157169
-- layouts/_default/single.html --
@@ -160,15 +172,21 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
160172

161173
b := hugolib.Test(t, files)
162174
b.AssertFileContent("public/p1/index.html",
163-
`<img alt="alt1" src="/dir/p1/pixel.png">`,
164-
`<img alt="alt2" src="/dir/p1/pixel.png?a=b&c=d#fragment">`,
175+
`<img src="" alt="">`,
176+
`<img src="/dir/p1/pixel.png" alt="alt1">`,
177+
`<img src="/dir/p1/pixel.png" alt="alt2-&amp;&lt;&gt;&rsquo;" title="&amp;&lt;&gt;&#39;">`,
178+
`<img src="/dir/p1/pixel.png?a=b&amp;c=d#fragment" alt="alt3">`,
179+
`<img src="/dir/p1/pixel.png" alt="alt4">`,
165180
)
166181

167182
files = strings.Replace(files, "block = false", "block = true", -1)
168183

169184
b = hugolib.Test(t, files)
170185
b.AssertFileContent("public/p1/index.html",
171-
`<img alt="alt1" src="/dir/p1/pixel.png">`,
172-
`<img alt="alt2" class="foo" id="bar" src="/dir/p1/pixel.png?a=b&c=d#fragment">`,
186+
`<img src="" alt="">`,
187+
`<img src="/dir/p1/pixel.png" alt="alt1">`,
188+
`<img src="/dir/p1/pixel.png" alt="alt2-&amp;&lt;&gt;&rsquo;" title="&amp;&lt;&gt;&#39;">`,
189+
`<img src="/dir/p1/pixel.png?a=b&amp;c=d#fragment" alt="alt3" class="foo" id="bar">`,
190+
`<img src="/dir/p1/pixel.png" alt="alt4" id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`,
173191
)
174192
}

0 commit comments

Comments
 (0)