]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
markup/goldmark: Fail on invalid Markdown attributes
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 14 Mar 2023 11:45:09 +0000 (12:45 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 15 Mar 2023 07:54:34 +0000 (08:54 +0100)
common/herrors/error_locator.go
common/herrors/file_error.go
hugolib/page.go
markup/goldmark/codeblocks/integration_test.go
markup/goldmark/codeblocks/render.go

index c7e2d2f06f5cc82c23f87820279c47d40db9ad6f..7624bab9876b5ab8e415de05535c7589c8b7fe43 100644 (file)
@@ -61,6 +61,16 @@ var OffsetMatcher = func(m LineMatcher) int {
        return -1
 }
 
+// ContainsMatcher is a line matcher that matches by line content.
+func ContainsMatcher(text string) func(m LineMatcher) int {
+       return func(m LineMatcher) int {
+               if idx := strings.Index(m.Line, text); idx != -1 {
+                       return idx + 1
+               }
+               return -1
+       }
+}
+
 // ErrorContext contains contextual information about an error. This will
 // typically be the lines surrounding some problem in a file.
 type ErrorContext struct {
index e6baaf6e37eb1964ff2998867f72613c6cba2e49..9273b2a8041774d1e24e93a8af4ed5b7dbed19c9 100644 (file)
@@ -392,3 +392,17 @@ func extractPosition(e error) (pos text.Position) {
        }
        return
 }
+
+// TextSegmentError is an error with a text segment attached.
+type TextSegmentError struct {
+       Segment string
+       Err     error
+}
+
+func (e TextSegmentError) Unwrap() error {
+       return e.Err
+}
+
+func (e TextSegmentError) Error() string {
+       return e.Err.Error()
+}
index a80b28a3e255ce7e0ff08bbeeaa0c9d8f334e1c1..ebc29df4765c779913b74c2a73e4504d8bb7b514 100644 (file)
@@ -617,7 +617,13 @@ func (p *pageState) wrapError(err error) error {
                }
        }
 
-       return herrors.NewFileErrorFromFile(err, filename, p.s.SourceSpec.Fs.Source, herrors.NopLineMatcher)
+       lineMatcher := herrors.NopLineMatcher
+
+       if textSegmentErr, ok := err.(*herrors.TextSegmentError); ok {
+               lineMatcher = herrors.ContainsMatcher(textSegmentErr.Segment)
+       }
+
+       return herrors.NewFileErrorFromFile(err, filename, p.s.SourceSpec.Fs.Source, lineMatcher)
 
 }
 
index 29ff0dc7d8c24e63b88e8d9068ac2b93e63bce39..7f02018784b3716e32f23e7e8ceda2605f27be49 100644 (file)
@@ -18,6 +18,8 @@ import (
        "strings"
        "testing"
 
+       qt "github.com/frankban/quicktest"
+
        "github.com/gohugoio/hugo/hugolib"
 )
 
@@ -384,3 +386,40 @@ Common
        }
 
 }
+
+// Issue 10835
+func TestAttributesValidation(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+disableKinds = ["taxonomy", "term"]
+-- content/p1.md --
+---
+title: "p1"
+---
+
+## Issue 10835
+
+§§§bash { color=red dimensions=300x200 }
+Hello, World!
+§§§
+
+-- layouts/index.html --
+-- layouts/_default/single.html --
+{{ .Content }}
+-- layouts/_default/_markup/render-codeblock.html --
+Attributes: {{ .Attributes }}|Type: {{ .Type }}|
+`
+
+       b, err := hugolib.NewIntegrationTestBuilder(
+               hugolib.IntegrationTestConfig{
+                       T:           t,
+                       TxtarString: files,
+               },
+       ).BuildE()
+
+       b.Assert(err, qt.Not(qt.IsNil))
+       b.Assert(err.Error(), qt.Contains, "p1.md:7:9\": failed to parse Markdown attributes; you may need to quote the values")
+
+}
index cf5a0f2966c09c4f88532978ef55d17761839c85..5f053d2787beca5269717d043a48c1e03c2c93f1 100644 (file)
@@ -15,6 +15,7 @@ package codeblocks
 
 import (
        "bytes"
+       "errors"
        "fmt"
        "strings"
        "sync"
@@ -101,7 +102,10 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
        }
 
        // IsDefaultCodeBlockRendererProvider
-       attrs := getAttributes(n.b, info)
+       attrs, attrStr, err := getAttributes(n.b, info)
+       if err != nil {
+               return ast.WalkStop, &herrors.TextSegmentError{Err: err, Segment: attrStr}
+       }
        cbctx := &codeBlockContext{
                page:             ctx.DocumentContext().Document,
                lang:             lang,
@@ -123,7 +127,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
 
        cr := renderer.(hooks.CodeBlockRenderer)
 
-       err := cr.RenderCodeblock(
+       err = cr.RenderCodeblock(
                ctx.RenderContext().Ctx,
                w,
                cbctx,
@@ -182,30 +186,36 @@ func getLang(node *ast.FencedCodeBlock, src []byte) string {
        return lang
 }
 
-func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
+func getAttributes(node *ast.FencedCodeBlock, infostr []byte) ([]ast.Attribute, string, error) {
        if node.Attributes() != nil {
-               return node.Attributes()
+               return node.Attributes(), "", nil
        }
        if infostr != nil {
                attrStartIdx := -1
+               attrEndIdx := -1
 
                for idx, char := range infostr {
-                       if char == '{' {
+                       if attrEndIdx == -1 && char == '{' {
                                attrStartIdx = idx
+                       }
+                       if attrStartIdx != -1 && char == '}' {
+                               attrEndIdx = idx
                                break
                        }
                }
 
-               if attrStartIdx != -1 {
+               if attrStartIdx != -1 && attrEndIdx != -1 {
                        n := ast.NewTextBlock() // dummy node for storing attributes
-                       attrStr := infostr[attrStartIdx:]
+                       attrStr := infostr[attrStartIdx : attrEndIdx+1]
                        if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
                                for _, attr := range attrs {
                                        n.SetAttribute(attr.Name, attr.Value)
                                }
-                               return n.Attributes()
+                               return n.Attributes(), "", nil
+                       } else {
+                               return nil, string(attrStr), errors.New("failed to parse Markdown attributes; you may need to quote the values")
                        }
                }
        }
-       return nil
+       return nil, "", nil
 }