Don't panic on invalid security whitelist regexp

Fixes #11176
This commit is contained in:
Bjørn Erik Pedersen 2023-06-28 08:56:35 +02:00
parent fa0e16f4c7
commit 7f698c8934
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
7 changed files with 39 additions and 21 deletions

View file

@ -34,7 +34,7 @@ const securityConfigKey = "security"
// DefaultConfig holds the default security policy. // DefaultConfig holds the default security policy.
var DefaultConfig = Config{ var DefaultConfig = Config{
Exec: Exec{ Exec: Exec{
Allow: NewWhitelist( Allow: MustNewWhitelist(
"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded. "^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
"^go$", // for Go Modules "^go$", // for Go Modules
"^npx$", // used by all Node tools (Babel, PostCSS). "^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 // These have been tested to work with Hugo's external programs
// on Windows, Linux and MacOS. // 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{ Funcs: Funcs{
Getenv: NewWhitelist("^HUGO_", "^CI$"), Getenv: MustNewWhitelist("^HUGO_", "^CI$"),
}, },
HTTP: HTTP{ HTTP: HTTP{
URLs: NewWhitelist(".*"), URLs: MustNewWhitelist(".*"),
Methods: NewWhitelist("(?i)GET|POST"), Methods: MustNewWhitelist("(?i)GET|POST"),
}, },
} }
@ -221,7 +221,7 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType {
wl := types.ToStringSlicePreserveString(data) wl := types.ToStringSlicePreserveString(data)
return NewWhitelist(wl...), nil return NewWhitelist(wl...)
} }
} }

View file

@ -45,9 +45,9 @@ func (w Whitelist) MarshalJSON() ([]byte, error) {
// NewWhitelist creates a new Whitelist from zero or more patterns. // NewWhitelist creates a new Whitelist from zero or more patterns.
// An empty patterns list or a pattern with the value 'none' will create // An empty patterns list or a pattern with the value 'none' will create
// a whitelist that will Accept none. // a whitelist that will Accept none.
func NewWhitelist(patterns ...string) Whitelist { func NewWhitelist(patterns ...string) (Whitelist, error) {
if len(patterns) == 0 { if len(patterns) == 0 {
return Whitelist{acceptNone: true} return Whitelist{acceptNone: true}, nil
} }
var acceptSome bool var acceptSome bool
@ -68,7 +68,7 @@ func NewWhitelist(patterns ...string) Whitelist {
if !acceptSome { if !acceptSome {
return Whitelist{ return Whitelist{
acceptNone: true, acceptNone: true,
} }, nil
} }
var patternsr []*regexp.Regexp var patternsr []*regexp.Regexp
@ -78,10 +78,23 @@ func NewWhitelist(patterns ...string) Whitelist {
if p == "" { if p == "" {
continue 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. // Accept reports whether name is whitelisted.

View file

@ -24,21 +24,21 @@ func TestWhitelist(t *testing.T) {
c := qt.New(t) c := qt.New(t)
c.Run("none", func(c *qt.C) { c.Run("none", func(c *qt.C) {
c.Assert(NewWhitelist("none", "foo").Accept("foo"), qt.IsFalse) c.Assert(MustNewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
c.Assert(NewWhitelist().Accept("foo"), qt.IsFalse) c.Assert(MustNewWhitelist().Accept("foo"), qt.IsFalse)
c.Assert(NewWhitelist("").Accept("foo"), qt.IsFalse) c.Assert(MustNewWhitelist("").Accept("foo"), qt.IsFalse)
c.Assert(NewWhitelist(" ", " ").Accept("foo"), qt.IsFalse) c.Assert(MustNewWhitelist(" ", " ").Accept("foo"), qt.IsFalse)
c.Assert(Whitelist{}.Accept("foo"), qt.IsFalse) c.Assert(Whitelist{}.Accept("foo"), qt.IsFalse)
}) })
c.Run("One", func(c *qt.C) { c.Run("One", func(c *qt.C) {
w := NewWhitelist("^foo.*") w := MustNewWhitelist("^foo.*")
c.Assert(w.Accept("foo"), qt.IsTrue) c.Assert(w.Accept("foo"), qt.IsTrue)
c.Assert(w.Accept("mfoo"), qt.IsFalse) c.Assert(w.Accept("mfoo"), qt.IsFalse)
}) })
c.Run("Multiple", func(c *qt.C) { 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("foo"), qt.IsTrue)
c.Assert(w.Accept("bar"), qt.IsTrue) c.Assert(w.Accept("bar"), qt.IsTrue)
c.Assert(w.Accept("mbar"), qt.IsFalse) c.Assert(w.Accept("mbar"), qt.IsFalse)

View file

@ -381,7 +381,8 @@ func (s *IntegrationTestBuilder) initBuilder() error {
s.Assert(os.Chdir(s.Cfg.WorkingDir), qt.IsNil) s.Assert(os.Chdir(s.Cfg.WorkingDir), qt.IsNil)
s.C.Cleanup(func() { os.Chdir(wd) }) s.C.Cleanup(func() { os.Chdir(wd) })
sc := security.DefaultConfig 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) ex := hexec.New(sc)
command, err := ex.New("npm", "install") command, err := ex.New("npm", "install")
s.Assert(err, qt.IsNil) s.Assert(err, qt.IsNil)

View file

@ -834,7 +834,9 @@ func (s *sitesBuilder) GetPageRel(p page.Page, ref string) page.Page {
func (s *sitesBuilder) NpmInstall() hexec.Runner { func (s *sitesBuilder) NpmInstall() hexec.Runner {
sc := security.DefaultConfig 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) ex := hexec.New(sc)
command, err := ex.New("npm", "install") command, err := ex.New("npm", "install")
s.Assert(err, qt.IsNil) s.Assert(err, qt.IsNil)

View file

@ -31,7 +31,9 @@ func TestConvert(t *testing.T) {
} }
c := qt.New(t) c := qt.New(t)
sc := security.DefaultConfig 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()}) p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewDefault()})
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{}) conv, err := p.New(converter.DocumentContext{})

View file

@ -31,7 +31,7 @@ func TestConvert(t *testing.T) {
} }
c := qt.New(t) c := qt.New(t)
sc := security.DefaultConfig sc := security.DefaultConfig
sc.Exec.Allow = security.NewWhitelist("rst", "python") sc.Exec.Allow = security.MustNewWhitelist("rst", "python")
p, err := Provider.New( p, err := Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{