]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
tpl/tplimpl: Escape Markdown attributes in render hooks and shortcodes
authorJoe Mooring <joe.mooring@veriphor.com>
Wed, 4 Dec 2024 22:01:31 +0000 (14:01 -0800)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 9 Dec 2024 16:58:57 +0000 (17:58 +0100)
hugolib/content_render_hooks_test.go
markup/goldmark/tables/tables_integration_test.go
tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
tpl/tplimpl/embedded/templates/_default/_markup/render-link.html
tpl/tplimpl/embedded/templates/_default/_markup/render-table.html
tpl/tplimpl/embedded/templates/shortcodes/youtube.html
tpl/tplimpl/render_hook_integration_test.go

index 593d01da891b339064d2816a7993979465a5c6f4..7f2c3503216b49d04b13eb9b091172a47c03c3d4 100644 (file)
@@ -90,7 +90,7 @@ baseURL="https://example.org"
   [markup.goldmark]
     [markup.goldmark.renderer]
       unsafe = true
-    
+
 `)
 
        b.WithTemplates("index.html", `
@@ -223,16 +223,16 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
 iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
 -- layouts/_default/single.html --
 {{ .Title }}|{{ .Content }}|$
-       
+
 `
 
        t.Run("Default multilingual", func(t *testing.T) {
                b := Test(t, files)
 
                b.AssertFileContent("public/nn/p1/index.html",
-                       "p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
+                       "p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img src=\"/nn/p1/pixel.nn.png\" alt=\"Pixel\">")
                b.AssertFileContent("public/en/p1/index.html",
-                       "p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
+                       "p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img src=\"/nn/p1/pixel.nn.png\" alt=\"Pixel\">")
        })
 
        t.Run("Disabled", func(t *testing.T) {
@@ -279,7 +279,7 @@ Image: ![alt-"<>&](/destination-"<> 'title-"<>&')
                        if enabled {
                                b.AssertFileContent("public/index.html",
                                        "Link: <a href=\"/destination-%22%3C%3E\" title=\"title-&#34;&lt;&gt;&amp;\">text-&quot;&lt;&gt;&amp;</a>",
-                                       "img alt=\"alt-&quot;&lt;&gt;&amp;\" src=\"/destination-%22%3C%3E\" title=\"title-&#34;&lt;&gt;&amp;\">",
+                                       "img src=\"/destination-%22%3C%3E\" alt=\"alt-&quot;&lt;&gt;&amp;\" title=\"title-&#34;&lt;&gt;&amp;\">",
                                        "&gt;&lt;script&gt;",
                                )
                        } else {
index 85cf81c9d0449cab85de8961fb546a783d89ebde..36cf953ae187e2a19d8770e8adcf007090b3b161 100644 (file)
@@ -89,6 +89,12 @@ title = true
 | Codecademy Hoodie |  False   | 42.99 |
 {.foo}
 
+## Table 2
+
+a|b
+---|---
+1|2
+{id="\"><script>alert()</script>"}
 
 -- layouts/_default/single.html --
 Summary: {{ .Summary }}
@@ -97,7 +103,8 @@ Content: {{ .Content }}
 `
        b := hugolib.Test(t, files)
 
-       b.AssertFileContent("public/p1/index.html", "<table class=\"foo\">")
+       b.AssertFileContent("public/p1/index.html", `<table class="foo">`)
+       b.AssertFileContent("public/p1/index.html", `<table id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`)
 }
 
 // Issue 12811.
@@ -166,14 +173,8 @@ title: "Home"
 | Codecademy Tee    |  False   | 19.99 |
 | Codecademy Hoodie |  False   | 42.99 |
 
-
-
-
-
 -- layouts/index.xml --
 Content: {{ .Content }}
-
-
 `
        b := hugolib.Test(t, files)
 
index 89514cb83a0c41b15eab38601f31e8e75b050768..5c0f4383597d10fb567c9e0d1ba01b72c29ea6b2 100644 (file)
@@ -1,7 +1,7 @@
 {{- $u := urls.Parse .Destination -}}
 {{- $src := $u.String -}}
 {{- if not $u.IsAbs -}}
-  {{- $path := strings.TrimPrefix "./" $u.Path }}
+  {{- $path := strings.TrimPrefix "./" $u.Path -}}
   {{- with or (.PageInner.Resources.Get $path) (resources.Get $path) -}}
     {{- $src = .RelPermalink -}}
     {{- with $u.RawQuery -}}
     {{- end -}}
   {{- end -}}
 {{- end -}}
-{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" (.Title | transform.HTMLEscape)) -}}
-<img
-  {{- range $k, $v := $attributes -}}
+<img src="{{ $src }}" alt="{{ .Text }}"
+  {{- with .Title }} title="{{ . }}" {{- end -}}
+  {{- range $k, $v := .Attributes -}}
     {{- if $v -}}
-      {{- printf " %s=%q" $k $v | safeHTMLAttr -}}
+      {{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr -}}
     {{- end -}}
-  {{- end -}}>
+  {{- end -}}
+>
 {{- /**/ -}}
index f82158a2d5ccfe9242c87262a81813543154d5bd..95e15aba426f19294fbb8d2a35281d845f24793f 100644 (file)
@@ -1,9 +1,9 @@
 {{- $u := urls.Parse .Destination -}}
 {{- $href := $u.String -}}
-{{- if strings.HasPrefix $u.String "#" }}
-  {{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment }}
-{{- else if not $u.IsAbs -}}
-  {{- $path := strings.TrimPrefix "./" $u.Path }}
+{{- if strings.HasPrefix $u.String "#" -}}
+  {{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}}
+{{- else if and $href (not $u.IsAbs) -}}
+  {{- $path := strings.TrimPrefix "./" $u.Path -}}
   {{- with or
     ($.PageInner.GetPage $path)
     ($.PageInner.Resources.Get $path)
     {{- end -}}
   {{- end -}}
 {{- end -}}
-{{- $attributes := dict "href" $href "title" (.Title | transform.HTMLEscape) -}}
-<a
-  {{- range $k, $v := $attributes -}}
-    {{- if $v -}}
-      {{- printf " %s=%q" $k $v | safeHTMLAttr -}}
-    {{- end -}}
-  {{- end -}}
-  >{{ .Text }}</a>
+<a href="{{ $href }}" {{- with .Title }} title="{{ . }}" {{- end }}>{{ .Text }}</a>
 {{- /**/ -}}
index 5fdd79ddb9ab309b012574fe606f249f2da0e9c2..c43a72832877953f32081e53a1bb7fced1351e0c 100644 (file)
@@ -1,7 +1,7 @@
 <table
   {{- range $k, $v := .Attributes }}
     {{- if $v }}
-      {{- printf " %s=%q" $k $v | safeHTMLAttr }}
+      {{- printf " %s=%q" $k ($v | transform.HTMLEscape) | safeHTMLAttr }}
     {{- end }}
   {{- end }}>
   <thead>
index 93fa18197070f3a9c35f6408a94bd47bf750dba7..441469d0ad09a3dedf605eff1a231c948cc07afb 100644 (file)
@@ -26,7 +26,7 @@ Renders an embedded YouTube video.
 {{- if not $pc.Disable }}
   {{- with $id := or (.Get "id") (.Get 0) }}
 
-    {{/* Set defaults. */}}
+    {{/* Set defaults. */}}
     {{- $allowFullScreen := "allowfullscreen" }}
     {{- $autoplay := 0 }}
     {{- $class := "" }}
@@ -70,23 +70,8 @@ Renders an embedded YouTube video.
     {{- $start := or ($.Get "start") $start }}
     {{- $title := or ($.Get "title") $title }}
 
-    {{- /* Determine host. */}}
-    {{- $host := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" }}
-
-    {{- /* Set styles. */}}
-    {{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }}
-    {{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }}
-    {{- if $class }}
-      {{- $iframeStyle = "" }}
-    {{- end }}
-
-    {{- /* Set class or style of wrapping div element. */}}
-    {{- $divClassOrStyle := printf "style=%q" $divStyle }}
-    {{- with $class }}
-      {{- $divClassOrStyle = printf "class=%q" $class }}
-    {{- end }}
-
     {{- /* Define src attribute. */}}
+    {{- $host := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" }}
     {{- $src := printf "https://%s/embed/%s" $host $id }}
     {{- $params := dict
       "autoplay" $autoplay
@@ -108,25 +93,33 @@ Renders an embedded YouTube video.
       {{- $src = printf "%s?%s" $src . }}
     {{- end }}
 
+    {{- /* Set div attributes. */}}
+    {{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }}
+    {{- if $class }}
+      {{- $divStyle = "" }}
+    {{- end }}
+
     {{- /* Set iframe attributes. */}}
-    {{- $iframeAttributes := dict
-      "allow" "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
-      "allowfullscreen" $allowFullScreen
-      "loading" $loading
-      "referrerpolicy" "strict-origin-when-cross-origin"
-      "src" $src
-      "style" $iframeStyle
-      "title" $title
-    }}
+    {{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }}
+    {{- if $class }}
+      {{- $iframeStyle = "" }}
+    {{- end }}
+    {{- $allow := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }}
+    {{- $referrerpolicy := "strict-origin-when-cross-origin" }}
 
     {{- /* Render. */}}
-    <div {{ $divClassOrStyle | safeHTMLAttr }}>
+    <div
+      {{- with $class }} class="{{ . }}" {{- end }}
+      {{- with $divStyle }} style="{{ . | safeCSS }}" {{- end -}}
+    >
       <iframe
-        {{- range $k, $v := $iframeAttributes }}
-          {{- if $v }}
-            {{- printf " %s=%q" $k $v | safeHTMLAttr }}
-          {{- end }}
-        {{- end }}
+        {{- with $allow }} allow="{{ . }}" {{- end }}
+        {{- with $allowFullScreen }} allowfullscreen="{{ . }}" {{- end }}
+        {{- with $loading }} loading="{{ . }}" {{- end }}
+        {{- with $referrerpolicy }} referrerpolicy="{{ . }}" {{- end }}
+        {{- with $src }} src="{{ . }}" {{- end }}
+        {{- with $iframeStyle}} style="{{ . | safeCSS }}" {{- end }}
+        {{- with $title }} title="{{ . }}" {{- end -}}
       ></iframe>
     </div>
   {{- else }}
index b91358227daf838a450a73c8c728bab0303c8ca3..9f5f670a80fd0ff997984a74a8de33024b9a23ca 100644 (file)
@@ -91,6 +91,9 @@ title: s1/p3
 [430](p2/)
 [440](/s1/p2/)
 [450](../s1/p2/)
+
+// empty
+[]()
 `
 
        b := hugolib.Test(t, files)
@@ -122,6 +125,8 @@ title: s1/p3
                `<a href="/s1/p2/">430</a>`,
                `<a href="/s1/p2/">440</a>`,
                `<a href="/s1/p2/">450</a>`,
+
+               `<a href=""></a>`,
        )
 
        b.AssertFileContent("public/s1/p2/index.html",
@@ -148,10 +153,17 @@ block = false
 [markup.goldmark.renderHooks.image]
 enableDefault = true
 -- content/p1/index.md --
+![]()
+
 ![alt1](./pixel.png)
 
-![alt2](pixel.png?a=b&c=d#fragment)
+![alt2-&<>'](pixel.png "&<>'")
+
+![alt3](pixel.png?a=b&c=d#fragment)
 {.foo #bar}
+
+![alt4](pixel.png)
+{id="\"><script>alert()</script>"}
 -- content/p1/pixel.png --
 iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
 -- layouts/_default/single.html --
@@ -160,15 +172,21 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
 
        b := hugolib.Test(t, files)
        b.AssertFileContent("public/p1/index.html",
-               `<img alt="alt1" src="/dir/p1/pixel.png">`,
-               `<img alt="alt2" src="/dir/p1/pixel.png?a=b&c=d#fragment">`,
+               `<img src="" alt="">`,
+               `<img src="/dir/p1/pixel.png" alt="alt1">`,
+               `<img src="/dir/p1/pixel.png" alt="alt2-&amp;&lt;&gt;&rsquo;" title="&amp;&lt;&gt;&#39;">`,
+               `<img src="/dir/p1/pixel.png?a=b&amp;c=d#fragment" alt="alt3">`,
+               `<img src="/dir/p1/pixel.png" alt="alt4">`,
        )
 
        files = strings.Replace(files, "block = false", "block = true", -1)
 
        b = hugolib.Test(t, files)
        b.AssertFileContent("public/p1/index.html",
-               `<img alt="alt1" src="/dir/p1/pixel.png">`,
-               `<img alt="alt2" class="foo" id="bar" src="/dir/p1/pixel.png?a=b&c=d#fragment">`,
+               `<img src="" alt="">`,
+               `<img src="/dir/p1/pixel.png" alt="alt1">`,
+               `<img src="/dir/p1/pixel.png" alt="alt2-&amp;&lt;&gt;&rsquo;" title="&amp;&lt;&gt;&#39;">`,
+               `<img src="/dir/p1/pixel.png?a=b&amp;c=d#fragment" alt="alt3" class="foo" id="bar">`,
+               `<img src="/dir/p1/pixel.png" alt="alt4" id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`,
        )
 }