]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix potential content XSS by escaping dangerous URLs in links and images
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 1 Apr 2026 11:54:10 +0000 (13:54 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 1 Apr 2026 12:23:24 +0000 (14:23 +0200)
* This issue in question was fixed upstream in https://github.com/yuin/goldmark/releases/tag/v1.8.2
* We do, however, have a copy of the affected functions, used as fallbacks when no render hook are defined for e.g. links and images, so we need to port these fixes to our copy of the affected functions.

markup/goldmark/goldmark_integration_test.go
markup/goldmark/render_hooks.go

index b1c18203483c42ba50585c60b8544a5a4d4a1a0f..c088201b39cb52d46e6228852a9f9fb52d089a50 100644 (file)
@@ -1035,3 +1035,33 @@ foo[^1] and bar[^2]
                "<p>foo<sup id=\"hb5cdcabc9e678612fnref:1\"><a href=\"#hb5cdcabc9e678612fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup> and bar<sup id=\"hb5cdcabc9e678612fnref:2\"><a href=\"#hb5cdcabc9e678612fn:2\" class=\"footnote-ref\" role=\"doc-noteref\">2</a></sup></p>\n<div class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"hb5cdcabc9e678612fn:1\">\n<p>footnote one&#160;<a href=\"#hb5cdcabc9e678612fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">back</a></p>\n</li>\n<li id=\"hb5cdcabc9e678612fn:2\">\n<p>footnote two&#160;<a href=\"#hb5cdcabc9e678612fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\">back</a></p>\n</li>\n</ol>\n</div>",
        )
 }
+
+func TestRenderLinkDefaultDangerous(t *testing.T) {
+       t.Parallel()
+
+       /*
+               Content: <p>Link: <a href="javascript:alert(1)">Click me</a>
+               AutoLink: <a href="">javascript:alert(1)</a>
+               Image: <img src="javascript:alert(1)" alt="alt"></p>
+       */
+
+       files := `
+-- content/p1.md --
+---
+title: "p1"
+---
+Link: [Click me](&#106;avascript:alert(1))
+AutoLink: <javascript:alert(2)>
+Image: ![alt](&#106;avascript:alert(3))
+-- layouts/all.html --
+Content: {{ .Content }}
+`
+
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContent("public/p1/index.html",
+               `! alert(1)"`,
+               `! href="javascript:alert(2)"`,
+               `! alert(3)"`,
+       )
+}
index 1e91f7ab131b5b72156a6504fdc881b0ae196282..4183580544d92c630c592387eb38f079eff2b6e8 100644 (file)
@@ -230,8 +230,9 @@ func (r *hookedRenderer) renderImageDefault(w util.BufWriter, source []byte, nod
        }
        n := node.(*ast.Image)
        _, _ = w.WriteString("<img src=\"")
-       if r.Unsafe || !html.IsDangerousURL(n.Destination) {
-               _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
+       dest := util.URLEscape(n.Destination, true)
+       if r.Unsafe || !html.IsDangerousURL(dest) {
+               _, _ = w.Write(util.EscapeHTML(dest))
        }
        _, _ = w.WriteString(`" alt="`)
        r.renderTexts(w, source, n)
@@ -375,8 +376,9 @@ func (r *hookedRenderer) renderLinkDefault(w util.BufWriter, source []byte, node
        n := node.(*ast.Link)
        if entering {
                _, _ = w.WriteString("<a href=\"")
-               if r.Unsafe || !html.IsDangerousURL(n.Destination) {
-                       _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
+               dest := util.URLEscape(n.Destination, true)
+               if r.Unsafe || !html.IsDangerousURL(dest) {
+                       _, _ = w.Write(util.EscapeHTML(util.EscapeHTML(dest)))
                }
                _ = w.WriteByte('"')
                if n.Title != nil {
@@ -445,12 +447,15 @@ func (r *hookedRenderer) renderAutoLinkDefault(w util.BufWriter, source []byte,
        }
 
        _, _ = w.WriteString(`<a href="`)
-       url := r.autoLinkURL(n, source)
+       url := util.URLEscape(r.autoLinkURL(n, source), false)
+
        label := n.Label(source)
        if n.AutoLinkType == ast.AutoLinkEmail && !bytes.HasPrefix(bytes.ToLower(url), []byte("mailto:")) {
                _, _ = w.WriteString("mailto:")
        }
-       _, _ = w.Write(util.EscapeHTML(util.URLEscape(url, false)))
+       if r.Unsafe || !html.IsDangerousURL(url) {
+               _, _ = w.Write(util.EscapeHTML(url))
+       }
        if n.Attributes() != nil {
                _ = w.WriteByte('"')
                html.RenderAttributes(w, n, html.LinkAttributeFilter)