From 9f978d387f8b7cb6bc03fe6b4dd52bb16862a784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 4 Dec 2023 12:07:54 +0100 Subject: [PATCH] Pull in the latest code from Go's template packages (#11771) Fixes #10707 Fixes #11507 --- config/allconfig/load.go | 4 - config/security/securityConfig.go | 20 +-- config/security/securityConfig_test.go | 8 +- scripts/fork_go_templates/main.go | 2 +- .../go_templates/fmtsort/sort_test.go | 10 +- .../go_templates/htmltemplate/context.go | 21 ++- .../go_templates/htmltemplate/error.go | 4 + .../go_templates/htmltemplate/escape.go | 51 +++--- .../go_templates/htmltemplate/escape_test.go | 151 ++++++++++++++++-- .../go_templates/htmltemplate/exec_test.go | 50 +++--- .../htmltemplate/hugo_template.go | 6 - tpl/internal/go_templates/htmltemplate/js.go | 31 ++++ .../go_templates/htmltemplate/state_string.go | 6 +- .../go_templates/htmltemplate/transition.go | 62 ++++++- tpl/internal/go_templates/testenv/exec.go | 12 +- tpl/internal/go_templates/testenv/testenv.go | 18 +-- .../go_templates/testenv/testenv_notunix.go | 5 +- .../go_templates/testenv/testenv_test.go | 27 +++- .../go_templates/testenv/testenv_unix.go | 25 ++- tpl/internal/go_templates/texttemplate/doc.go | 8 +- .../go_templates/texttemplate/exec.go | 5 +- .../go_templates/texttemplate/exec_test.go | 50 +++--- .../go_templates/texttemplate/funcs.go | 2 +- tpl/template.go | 7 - tpl/tplimpl/integration_test.go | 22 +-- 25 files changed, 417 insertions(+), 190 deletions(-) diff --git a/config/allconfig/load.go b/config/allconfig/load.go index e7dae1806..7d706c7e3 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -34,7 +34,6 @@ import ( hglob "github.com/gohugoio/hugo/hugofs/glob" "github.com/gohugoio/hugo/modules" "github.com/gohugoio/hugo/parser/metadecoders" - "github.com/gohugoio/hugo/tpl" "github.com/spf13/afero" ) @@ -91,9 +90,6 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) { return nil, fmt.Errorf("failed to init config: %w", err) } - // This is unfortunate, but these are global settings. - tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl) - loggers.InitGlobalLogger(d.Logger.Level(), configs.Base.PanicOnWarning) return configs, nil diff --git a/config/security/securityConfig.go b/config/security/securityConfig.go index 16f8c23d8..e3bffedca 100644 --- a/config/security/securityConfig.go +++ b/config/security/securityConfig.go @@ -68,9 +68,6 @@ type Config struct { // Allow inline shortcodes EnableInlineShortcodes bool `json:"enableInlineShortcodes"` - - // Go templates related security config. - GoTemplates GoTemplates `json:"goTemplates"` } // Exec holds os/exec policies. @@ -96,15 +93,6 @@ type HTTP struct { MediaTypes Whitelist `json:"mediaTypes"` } -type GoTemplates struct { - - // Enable to allow template actions inside bakcticks in ES6 template literals. - // This was blocked in Hugo 0.114.0 for security reasons and you now get errors on the form - // "... appears in a JS template literal" if you have this in your templates. - // See https://github.com/golang/go/issues/59234 - AllowActionJSTmpl bool -} - // ToTOML converts c to TOML with [security] as the root. func (c Config) ToTOML() string { sec := c.ToSecurityMap() @@ -127,7 +115,6 @@ func (c Config) CheckAllowedExec(name string) error { } } return nil - } func (c Config) CheckAllowedGetEnv(name string) error { @@ -176,7 +163,6 @@ func (c Config) ToSecurityMap() map[string]any { "security": m, } return sec - } // DecodeConfig creates a privacy Config from a given Hugo configuration. @@ -206,15 +192,14 @@ func DecodeConfig(cfg config.Provider) (Config, error) { } return sc, nil - } func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType { return func( f reflect.Type, t reflect.Type, - data any) (any, error) { - + data any, + ) (any, error) { if t != reflect.TypeOf(Whitelist{}) { return data, nil } @@ -222,7 +207,6 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType { wl := types.ToStringSlicePreserveString(data) return NewWhitelist(wl...) - } } diff --git a/config/security/securityConfig_test.go b/config/security/securityConfig_test.go index cdfbe6341..3d58288c9 100644 --- a/config/security/securityConfig_test.go +++ b/config/security/securityConfig_test.go @@ -53,7 +53,6 @@ getEnv=["a", "b"] c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse) c.Assert(pc.Funcs.Getenv.Accept("a"), qt.IsTrue) c.Assert(pc.Funcs.Getenv.Accept("c"), qt.IsFalse) - }) c.Run("String whitelist", func(c *qt.C) { @@ -80,7 +79,6 @@ osEnv="b" c.Assert(pc.Exec.Allow.Accept("d"), qt.IsFalse) c.Assert(pc.Exec.OsEnv.Accept("b"), qt.IsTrue) c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse) - }) c.Run("Default exec.osEnv", func(c *qt.C) { @@ -105,7 +103,6 @@ allow="a" c.Assert(pc.Exec.Allow.Accept("a"), qt.IsTrue) c.Assert(pc.Exec.OsEnv.Accept("PATH"), qt.IsTrue) c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse) - }) c.Run("Enable inline shortcodes, legacy", func(c *qt.C) { @@ -129,9 +126,7 @@ osEnv="b" pc, err := DecodeConfig(cfg) c.Assert(err, qt.IsNil) c.Assert(pc.EnableInlineShortcodes, qt.IsTrue) - }) - } func TestToTOML(t *testing.T) { @@ -140,7 +135,7 @@ func TestToTOML(t *testing.T) { got := DefaultConfig.ToTOML() c.Assert(got, qt.Equals, - "[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.goTemplates]\n AllowActionJSTmpl = false\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']", + "[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']", ) } @@ -169,5 +164,4 @@ func TestDecodeConfigDefault(t *testing.T) { c.Assert(pc.Exec.OsEnv.Accept("a"), qt.IsFalse) c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse) c.Assert(pc.Exec.OsEnv.Accept("MYSECRET"), qt.IsFalse) - } diff --git a/scripts/fork_go_templates/main.go b/scripts/fork_go_templates/main.go index 4ab89a547..8e14813ec 100644 --- a/scripts/fork_go_templates/main.go +++ b/scripts/fork_go_templates/main.go @@ -16,7 +16,7 @@ import ( ) func main() { - // The current is built with 2c1e5b05fe39fc5e6c730dd60e82946b8e67c6ba, tag: go1.21.1. + // The current is built with 446a5dcf5a3230ce9832682d8f521071d8a34a2b (go 1.22 dev. Thu Oct 5 12:20:11 2023 -0700) fmt.Println("Forking ...") defer fmt.Println("Done ...") diff --git a/tpl/internal/go_templates/fmtsort/sort_test.go b/tpl/internal/go_templates/fmtsort/sort_test.go index 064b09107..fef952fa6 100644 --- a/tpl/internal/go_templates/fmtsort/sort_test.go +++ b/tpl/internal/go_templates/fmtsort/sort_test.go @@ -6,13 +6,14 @@ package fmtsort_test import ( "fmt" - "github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort" "math" "reflect" "sort" "strings" "testing" "unsafe" + + "github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort" ) var compareTests = [][]reflect.Value{ @@ -38,7 +39,7 @@ var compareTests = [][]reflect.Value{ ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), - ct(reflect.TypeOf(any(any(0))), iFace, 1, 2, 3), + ct(reflect.TypeOf(any(0)), iFace, 1, 2, 3), } var iFace any @@ -190,12 +191,15 @@ func sprintKey(key reflect.Value) string { var ( ints [3]int chans = makeChans() + // pin runtime.Pinner ) func makeChans() []chan int { cs := []chan int{make(chan int), make(chan int), make(chan int)} // Order channels by address. See issue #49431. - // TODO: pin these pointers once pinning is available (#46787). + for i := range cs { + reflect.ValueOf(cs[i]).UnsafePointer() + } sort.Slice(cs, func(i, j int) bool { return uintptr(reflect.ValueOf(cs[i]).UnsafePointer()) < uintptr(reflect.ValueOf(cs[j]).UnsafePointer()) }) diff --git a/tpl/internal/go_templates/htmltemplate/context.go b/tpl/internal/go_templates/htmltemplate/context.go index 061f17ddb..3acb9bdf0 100644 --- a/tpl/internal/go_templates/htmltemplate/context.go +++ b/tpl/internal/go_templates/htmltemplate/context.go @@ -22,10 +22,15 @@ type context struct { delim delim urlPart urlPart jsCtx jsCtx - attr attr - element element - n parse.Node // for range break/continue - err *Error + // jsBraceDepth contains the current depth, for each JS template literal + // string interpolation expression, of braces we've seen. This is used to + // determine if the next } will close a JS template literal string + // interpolation expression or not. + jsBraceDepth []int + attr attr + element element + n parse.Node // for range break/continue + err *Error } func (c context) String() string { @@ -121,8 +126,8 @@ const ( stateJSDqStr // stateJSSqStr occurs inside a JavaScript single quoted string. stateJSSqStr - // stateJSBqStr occurs inside a JavaScript back quoted string. - stateJSBqStr + // stateJSTmplLit occurs inside a JavaScript back quoted string. + stateJSTmplLit // stateJSRegexp occurs inside a JavaScript regexp literal. stateJSRegexp // stateJSBlockCmt occurs inside a JavaScript /* block comment */. @@ -176,14 +181,14 @@ func isInTag(s state) bool { } // isInScriptLiteral returns true if s is one of the literal states within a -//