Don't panic on invalid security whitelist regexp - 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 7f698c89346acb5e5116736d25325a046652ba81
 (DIR) parent fa0e16f4c79a703d122f1e3a3a99f4b779aea9b2
 (HTM) Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Wed, 28 Jun 2023 08:56:35 +0200
       
       Don't panic on invalid security whitelist regexp
       
       Fixes #11176
       
       Diffstat:
         M config/security/securityConfig.go   |      12 ++++++------
         M config/security/whitelist.go        |      23 ++++++++++++++++++-----
         M config/security/whitelist_test.go   |      12 ++++++------
         M hugolib/integrationtest_builder.go  |       3 ++-
         M hugolib/testhelpers_test.go         |       4 +++-
         M markup/pandoc/convert_test.go       |       4 +++-
         M markup/rst/convert_test.go          |       2 +-
       
       7 files changed, 39 insertions(+), 21 deletions(-)
       ---
 (DIR) diff --git a/config/security/securityConfig.go b/config/security/securityConfig.go
       @@ -34,7 +34,7 @@ const securityConfigKey = "security"
        // DefaultConfig holds the default security policy.
        var DefaultConfig = Config{
                Exec: Exec{
       -                Allow: NewWhitelist(
       +                Allow: MustNewWhitelist(
                                "^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
                                "^go$",                       // for Go Modules
                                "^npx$",                      // used by all Node tools (Babel, PostCSS).
       @@ -42,14 +42,14 @@ var DefaultConfig = Config{
                        ),
                        // These have been tested to work with Hugo's external programs
                        // on Windows, Linux and MacOS.
       -                OsEnv: NewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$`),
       +                OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$`),
                },
                Funcs: Funcs{
       -                Getenv: NewWhitelist("^HUGO_", "^CI$"),
       +                Getenv: MustNewWhitelist("^HUGO_", "^CI$"),
                },
                HTTP: HTTP{
       -                URLs:    NewWhitelist(".*"),
       -                Methods: NewWhitelist("(?i)GET|POST"),
       +                URLs:    MustNewWhitelist(".*"),
       +                Methods: MustNewWhitelist("(?i)GET|POST"),
                },
        }
        
       @@ -221,7 +221,7 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType {
        
                        wl := types.ToStringSlicePreserveString(data)
        
       -                return NewWhitelist(wl...), nil
       +                return NewWhitelist(wl...)
        
                }
        }
 (DIR) diff --git a/config/security/whitelist.go b/config/security/whitelist.go
       @@ -45,9 +45,9 @@ func (w Whitelist) MarshalJSON() ([]byte, error) {
        // NewWhitelist creates a new Whitelist from zero or more patterns.
        // An empty patterns list or a pattern with the value 'none' will create
        // a whitelist that will Accept none.
       -func NewWhitelist(patterns ...string) Whitelist {
       +func NewWhitelist(patterns ...string) (Whitelist, error) {
                if len(patterns) == 0 {
       -                return Whitelist{acceptNone: true}
       +                return Whitelist{acceptNone: true}, nil
                }
        
                var acceptSome bool
       @@ -68,7 +68,7 @@ func NewWhitelist(patterns ...string) Whitelist {
                if !acceptSome {
                        return Whitelist{
                                acceptNone: true,
       -                }
       +                }, nil
                }
        
                var patternsr []*regexp.Regexp
       @@ -78,10 +78,23 @@ func NewWhitelist(patterns ...string) Whitelist {
                        if p == "" {
                                continue
                        }
       -                patternsr = append(patternsr, regexp.MustCompile(p))
       +                re, err := regexp.Compile(p)
       +                if err != nil {
       +                        return Whitelist{}, fmt.Errorf("failed to compile whitelist pattern %q: %w", p, err)
       +                }
       +                patternsr = append(patternsr, re)
                }
        
       -        return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}
       +        return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}, nil
       +}
       +
       +// MustNewWhitelist creates a new Whitelist from zero or more patterns and panics on error.
       +func MustNewWhitelist(patterns ...string) Whitelist {
       +        w, err := NewWhitelist(patterns...)
       +        if err != nil {
       +                panic(err)
       +        }
       +        return w
        }
        
        // Accept reports whether name is whitelisted.
 (DIR) diff --git a/config/security/whitelist_test.go b/config/security/whitelist_test.go
       @@ -24,21 +24,21 @@ func TestWhitelist(t *testing.T) {
                c := qt.New(t)
        
                c.Run("none", func(c *qt.C) {
       -                c.Assert(NewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
       -                c.Assert(NewWhitelist().Accept("foo"), qt.IsFalse)
       -                c.Assert(NewWhitelist("").Accept("foo"), qt.IsFalse)
       -                c.Assert(NewWhitelist("  ", " ").Accept("foo"), qt.IsFalse)
       +                c.Assert(MustNewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
       +                c.Assert(MustNewWhitelist().Accept("foo"), qt.IsFalse)
       +                c.Assert(MustNewWhitelist("").Accept("foo"), qt.IsFalse)
       +                c.Assert(MustNewWhitelist("  ", " ").Accept("foo"), qt.IsFalse)
                        c.Assert(Whitelist{}.Accept("foo"), qt.IsFalse)
                })
        
                c.Run("One", func(c *qt.C) {
       -                w := NewWhitelist("^foo.*")
       +                w := MustNewWhitelist("^foo.*")
                        c.Assert(w.Accept("foo"), qt.IsTrue)
                        c.Assert(w.Accept("mfoo"), qt.IsFalse)
                })
        
                c.Run("Multiple", func(c *qt.C) {
       -                w := NewWhitelist("^foo.*", "^bar.*")
       +                w := MustNewWhitelist("^foo.*", "^bar.*")
                        c.Assert(w.Accept("foo"), qt.IsTrue)
                        c.Assert(w.Accept("bar"), qt.IsTrue)
                        c.Assert(w.Accept("mbar"), qt.IsFalse)
 (DIR) diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
       @@ -381,7 +381,8 @@ func (s *IntegrationTestBuilder) initBuilder() error {
                                s.Assert(os.Chdir(s.Cfg.WorkingDir), qt.IsNil)
                                s.C.Cleanup(func() { os.Chdir(wd) })
                                sc := security.DefaultConfig
       -                        sc.Exec.Allow = security.NewWhitelist("npm")
       +                        sc.Exec.Allow, err = security.NewWhitelist("npm")
       +                        s.Assert(err, qt.IsNil)
                                ex := hexec.New(sc)
                                command, err := ex.New("npm", "install")
                                s.Assert(err, qt.IsNil)
 (DIR) diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
       @@ -834,7 +834,9 @@ func (s *sitesBuilder) GetPageRel(p page.Page, ref string) page.Page {
        
        func (s *sitesBuilder) NpmInstall() hexec.Runner {
                sc := security.DefaultConfig
       -        sc.Exec.Allow = security.NewWhitelist("npm")
       +        var err error
       +        sc.Exec.Allow, err = security.NewWhitelist("npm")
       +        s.Assert(err, qt.IsNil)
                ex := hexec.New(sc)
                command, err := ex.New("npm", "install")
                s.Assert(err, qt.IsNil)
 (DIR) diff --git a/markup/pandoc/convert_test.go b/markup/pandoc/convert_test.go
       @@ -31,7 +31,9 @@ func TestConvert(t *testing.T) {
                }
                c := qt.New(t)
                sc := security.DefaultConfig
       -        sc.Exec.Allow = security.NewWhitelist("pandoc")
       +        var err error
       +        sc.Exec.Allow, err = security.NewWhitelist("pandoc")
       +        c.Assert(err, qt.IsNil)
                p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewDefault()})
                c.Assert(err, qt.IsNil)
                conv, err := p.New(converter.DocumentContext{})
 (DIR) diff --git a/markup/rst/convert_test.go b/markup/rst/convert_test.go
       @@ -31,7 +31,7 @@ func TestConvert(t *testing.T) {
                }
                c := qt.New(t)
                sc := security.DefaultConfig
       -        sc.Exec.Allow = security.NewWhitelist("rst", "python")
       +        sc.Exec.Allow = security.MustNewWhitelist("rst", "python")
        
                p, err := Provider.New(
                        converter.ProviderConfig{