diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index 591226dc2fe..67348d894d8 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -851,3 +851,80 @@ title: "p1" b.AssertFileContent("public/p1/index.html", "! ") b.AssertLogContains("! WARN") } + +// See https://github.com/gohugoio/hugo/issues/13278#issuecomment-2603280548 +func TestGoldmarkRawHTMLCommentNoWarning(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +markup.goldmark.renderer.unsafe = false +-- content/p1.md -- +--- +title: "p1" +--- +# HTML comments + +## Simple + + + + + **Hello**_world_. +## With HTML + + + +## With HTML and JS + + + +## With Block + + + +## XSS + + + + +## More + +This is a word. + +This is a word. + +This is a word. + +This is a word. + +This is a word. + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/p1/index.html", + "! ", + "! ", + "! ", + "! script", + ) + b.AssertLogContains("! WARN") + + b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) + b.AssertFileContent("public/p1/index.html", + "! ", + "", + "", + ) + b.AssertLogContains("! WARN") +} diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go index 601014b3705..e68acb8c31d 100644 --- a/markup/goldmark/hugocontext/hugocontext.go +++ b/markup/goldmark/hugocontext/hugocontext.go @@ -169,11 +169,16 @@ func (r *hugoContextRenderer) getPage(w util.BufWriter) any { return p } +func (r *hugoContextRenderer) isHTMLComment(b []byte) bool { + return len(b) > 4 && b[0] == '<' && b[1] == '!' && b[2] == '-' && b[3] == '-' +} + // HTML rendering based on Goldmark implementation. func (r *hugoContextRenderer) renderHTMLBlock( w util.BufWriter, source []byte, node ast.Node, entering bool, ) (ast.WalkStatus, error) { n := node.(*ast.HTMLBlock) + if entering { if r.Unsafe { l := n.Lines().Len() @@ -188,8 +193,12 @@ func (r *hugoContextRenderer) renderHTMLBlock( r.Writer.SecureWrite(w, linev) } } else { - r.logRawHTMLEmittedWarn(w) - _, _ = w.WriteString("\n") + l := n.Lines().At(0) + v := l.Value(source) + if !r.isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("\n") + } } } else { if n.HasClosure() { @@ -197,7 +206,11 @@ func (r *hugoContextRenderer) renderHTMLBlock( closure := n.ClosureLine r.Writer.SecureWrite(w, closure.Value(source)) } else { - _, _ = w.WriteString("\n") + l := n.Lines().At(0) + v := l.Value(source) + if !r.isHTMLComment(v) { + _, _ = w.WriteString("\n") + } } } } @@ -210,17 +223,21 @@ func (r *hugoContextRenderer) renderRawHTML( if !entering { return ast.WalkSkipChildren, nil } + n := node.(*ast.RawHTML) + l := n.Segments.Len() if r.Unsafe { - n := node.(*ast.RawHTML) - l := n.Segments.Len() for i := 0; i < l; i++ { segment := n.Segments.At(i) _, _ = w.Write(segment.Value(source)) } return ast.WalkSkipChildren, nil } - r.logRawHTMLEmittedWarn(w) - _, _ = w.WriteString("") + segment := n.Segments.At(0) + v := segment.Value(source) + if !r.isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("") + } return ast.WalkSkipChildren, nil }