Fail with error when double-rendering text in markdownify/RenderString - hugo - [fork] hugo port for 9front
 (HTM) git clone git@git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 4e14cf7607ad3afdbf65272cd5bb61dba4b415da
 (DIR) parent 5697348e1732a5f64ee7467283eb0335f2ec36e8
 (HTM) Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Thu, 10 Mar 2022 08:19:03 +0100
       
       Fail with error when double-rendering text in markdownify/RenderString
       
       This commit prevents the most commons case of infinite recursion in link render hooks when the `linkify` option is enabled (see below). This is always a user error, but getting a `stack overflow` (the current stack limit in Go is 1 GB on 64-bit, 250 MB on 32-bit) error isn't very helpful. This fix will not prevent all such errors, though, but we may do better once #9570 is in place.
       
       So, these will fail:
       
       ```
       <a href="{{ .Destination | safeURL }}" >{{ .Text | markdownify }}</a>
       <a href="{{ .Destination | safeURL }}" >{{ .Text | .Page.RenderString }}</a>
       ```
       
       `.Text` is already rendered to `HTML`. The above needs to be rewritten to:
       
       ```
       <a href="{{ .Destination | safeURL }}" >{{ .Text | safeHTML }}</a>
       <a href="{{ .Destination | safeURL }}" >{{ .Text | safeHTML }}</a>
       ```
       
       Fixes #8959
       Diffstat:
         A common/types/hstring/stringtypes.go |      20 ++++++++++++++++++++
         A common/types/hstring/stringtypes_t… |      30 ++++++++++++++++++++++++++++++
         M hugolib/page__per_output.go         |      12 ++++++++++--
         M markup/converter/hooks/hooks.go     |       5 +++--
         M markup/goldmark/integration_test.go |      45 +++++++++++++++++++++++++++++++
         M markup/goldmark/render_hooks.go     |      17 +++++++++--------
         M tpl/transform/transform.go          |      13 +++++--------
       
       7 files changed, 122 insertions(+), 20 deletions(-)
       ---
 (DIR) diff --git a/common/types/hstring/stringtypes.go b/common/types/hstring/stringtypes.go
       @@ -0,0 +1,20 @@
       +// Copyright 2022 The Hugo Authors. All rights reserved.
       +//
       +// Licensed under the Apache License, Version 2.0 (the "License");
       +// you may not use this file except in compliance with the License.
       +// You may obtain a copy of the License at
       +// http://www.apache.org/licenses/LICENSE-2.0
       +//
       +// Unless required by applicable law or agreed to in writing, software
       +// distributed under the License is distributed on an "AS IS" BASIS,
       +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       +// See the License for the specific language governing permissions and
       +// limitations under the License.
       +
       +package hstring
       +
       +type RenderedString string
       +
       +func (s RenderedString) String() string {
       +        return string(s)
       +}
 (DIR) diff --git a/common/types/hstring/stringtypes_test.go b/common/types/hstring/stringtypes_test.go
       @@ -0,0 +1,30 @@
       +// Copyright 2022 The Hugo Authors. All rights reserved.
       +//
       +// Licensed under the Apache License, Version 2.0 (the "License");
       +// you may not use this file except in compliance with the License.
       +// You may obtain a copy of the License at
       +// http://www.apache.org/licenses/LICENSE-2.0
       +//
       +// Unless required by applicable law or agreed to in writing, software
       +// distributed under the License is distributed on an "AS IS" BASIS,
       +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       +// See the License for the specific language governing permissions and
       +// limitations under the License.
       +
       +package hstring
       +
       +import (
       +        "html/template"
       +        "testing"
       +
       +        qt "github.com/frankban/quicktest"
       +        "github.com/spf13/cast"
       +)
       +
       +func TestStringTypes(t *testing.T) {
       +        c := qt.New(t)
       +
       +        // Validate that it will behave like a string in Hugo settings.
       +        c.Assert(cast.ToString(RenderedString("Hugo")), qt.Equals, "Hugo")
       +        c.Assert(template.HTML(RenderedString("Hugo")), qt.Equals, template.HTML("Hugo"))
       +}
 (DIR) diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
       @@ -24,6 +24,7 @@ import (
                "unicode/utf8"
        
                "github.com/gohugoio/hugo/common/text"
       +        "github.com/gohugoio/hugo/common/types/hstring"
                "github.com/gohugoio/hugo/identity"
                "github.com/mitchellh/mapstructure"
                "github.com/pkg/errors"
       @@ -351,8 +352,16 @@ func (p *pageContentOutput) RenderString(args ...interface{}) (template.HTML, er
                        }
                }
        
       +        contentToRender := args[sidx]
       +
       +        if _, ok := contentToRender.(hstring.RenderedString); ok {
       +                // This content is already rendered, this is potentially
       +                // a infinite recursion.
       +                return "", errors.New("text is already rendered, repeating it may cause infinite recursion")
       +        }
       +
                var err error
       -        s, err = cast.ToStringE(args[sidx])
       +        s, err = cast.ToStringE(contentToRender)
                if err != nil {
                        return "", err
                }
       @@ -515,7 +524,6 @@ func (p *pageContentOutput) initRenderHooks() error {
                                                }
                                        }
                                }
       -
                                if !found1 {
                                        if tp == hooks.CodeBlockRendererType {
                                                // No user provided tempplate for code blocks, so we use the native Go code version -- which is also faster.
 (DIR) diff --git a/markup/converter/hooks/hooks.go b/markup/converter/hooks/hooks.go
       @@ -18,6 +18,7 @@ import (
        
                "github.com/gohugoio/hugo/common/hugio"
                "github.com/gohugoio/hugo/common/text"
       +        "github.com/gohugoio/hugo/common/types/hstring"
                "github.com/gohugoio/hugo/identity"
                "github.com/gohugoio/hugo/markup/internal/attributes"
        )
       @@ -32,7 +33,7 @@ type LinkContext interface {
                Page() interface{}
                Destination() string
                Title() string
       -        Text() string
       +        Text() hstring.RenderedString
                PlainText() string
        }
        
       @@ -75,7 +76,7 @@ type HeadingContext interface {
                // Anchor is the HTML id assigned to the heading.
                Anchor() string
                // Text is the rendered (HTML) heading text, excluding the heading marker.
       -        Text() string
       +        Text() hstring.RenderedString
                // PlainText is the unrendered version of Text.
                PlainText() string
        
 (DIR) diff --git a/markup/goldmark/integration_test.go b/markup/goldmark/integration_test.go
       @@ -18,6 +18,8 @@ import (
                "strings"
                "testing"
        
       +        qt "github.com/frankban/quicktest"
       +
                "github.com/gohugoio/hugo/hugolib"
        )
        
       @@ -395,6 +397,49 @@ FENCE
                }
        }
        
       +// Iisse #8959
       +func TestHookInfiniteRecursion(t *testing.T) {
       +        t.Parallel()
       +
       +        for _, renderFunc := range []string{"markdownify", ".Page.RenderString"} {
       +                t.Run(renderFunc, func(t *testing.T) {
       +
       +                        files := `
       +-- config.toml --
       +-- layouts/_default/_markup/render-link.html --
       +<a href="{{ .Destination | safeURL }}">{{ .Text | RENDERFUNC }}</a>        
       +-- layouts/_default/single.html --
       +{{ .Content }}
       +-- content/p1.md --
       +---
       +title: "p1"
       +---
       +
       +https://example.org
       +
       +a@b.com
       +                
       +                        
       +                        `
       +
       +                        files = strings.ReplaceAll(files, "RENDERFUNC", renderFunc)
       +
       +                        b, err := hugolib.NewIntegrationTestBuilder(
       +                                hugolib.IntegrationTestConfig{
       +                                        T:           t,
       +                                        TxtarString: files,
       +                                },
       +                        ).BuildE()
       +
       +                        b.Assert(err, qt.IsNotNil)
       +                        b.Assert(err.Error(), qt.Contains, "text is already rendered, repeating it may cause infinite recursion")
       +
       +                })
       +
       +        }
       +
       +}
       +
        // Issue 9594
        func TestQuotesInImgAltAttr(t *testing.T) {
                t.Parallel()
 (DIR) diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go
       @@ -17,6 +17,7 @@ import (
                "bytes"
                "strings"
        
       +        "github.com/gohugoio/hugo/common/types/hstring"
                "github.com/gohugoio/hugo/markup/converter/hooks"
                "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
                "github.com/gohugoio/hugo/markup/goldmark/internal/render"
       @@ -49,7 +50,7 @@ type linkContext struct {
                page        interface{}
                destination string
                title       string
       -        text        string
       +        text        hstring.RenderedString
                plainText   string
        }
        
       @@ -65,7 +66,7 @@ func (ctx linkContext) Page() interface{} {
                return ctx.page
        }
        
       -func (ctx linkContext) Text() string {
       +func (ctx linkContext) Text() hstring.RenderedString {
                return ctx.text
        }
        
       @@ -81,7 +82,7 @@ type headingContext struct {
                page      interface{}
                level     int
                anchor    string
       -        text      string
       +        text      hstring.RenderedString
                plainText string
                *attributes.AttributesHolder
        }
       @@ -98,7 +99,7 @@ func (ctx headingContext) Anchor() string {
                return ctx.anchor
        }
        
       -func (ctx headingContext) Text() string {
       +func (ctx headingContext) Text() hstring.RenderedString {
                return ctx.text
        }
        
       @@ -156,7 +157,7 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
                                page:        ctx.DocumentContext().Document,
                                destination: string(n.Destination),
                                title:       string(n.Title),
       -                        text:        string(text),
       +                        text:        hstring.RenderedString(text),
                                plainText:   string(n.Text(source)),
                        },
                )
       @@ -226,7 +227,7 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
                                page:        ctx.DocumentContext().Document,
                                destination: string(n.Destination),
                                title:       string(n.Title),
       -                        text:        string(text),
       +                        text:        hstring.RenderedString(text),
                                plainText:   string(n.Text(source)),
                        },
                )
       @@ -293,7 +294,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
                        linkContext{
                                page:        ctx.DocumentContext().Document,
                                destination: url,
       -                        text:        label,
       +                        text:        hstring.RenderedString(label),
                                plainText:   label,
                        },
                )
       @@ -381,7 +382,7 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
                                page:             ctx.DocumentContext().Document,
                                level:            n.Level,
                                anchor:           string(anchor),
       -                        text:             string(text),
       +                        text:             hstring.RenderedString(text),
                                plainText:        string(n.Text(source)),
                                AttributesHolder: attributes.New(n.Attributes(), attributes.AttributesOwnerGeneral),
                        },
 (DIR) diff --git a/tpl/transform/transform.go b/tpl/transform/transform.go
       @@ -20,7 +20,6 @@ import (
        
                "github.com/alecthomas/chroma/lexers"
                "github.com/gohugoio/hugo/cache/namedmemcache"
       -        "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/markup/converter/hooks"
                "github.com/gohugoio/hugo/markup/highlight"
        
       @@ -119,20 +118,18 @@ func (ns *Namespace) HTMLUnescape(s interface{}) (string, error) {
        
        // Markdownify renders a given input from Markdown to HTML.
        func (ns *Namespace) Markdownify(s interface{}) (template.HTML, error) {
       -        defer herrors.Recover()
       -        ss, err := cast.ToStringE(s)
       -        if err != nil {
       -                return "", err
       -        }
        
                home := ns.deps.Site.Home()
                if home == nil {
                        panic("home must not be nil")
                }
       -        sss, err := home.RenderString(ss)
       +        ss, err := home.RenderString(s)
       +        if err != nil {
       +                return "", err
       +        }
        
                // Strip if this is a short inline type of text.
       -        bb := ns.deps.ContentSpec.TrimShortHTML([]byte(sss))
       +        bb := ns.deps.ContentSpec.TrimShortHTML([]byte(ss))
        
                return helpers.BytesToHTML(bb), nil
        }