github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/config/security/securityConfig.go (about) 1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package security 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "errors" 20 "fmt" 21 "reflect" 22 "strings" 23 24 "github.com/gohugoio/hugo/common/herrors" 25 "github.com/gohugoio/hugo/common/types" 26 "github.com/gohugoio/hugo/config" 27 "github.com/gohugoio/hugo/parser" 28 "github.com/gohugoio/hugo/parser/metadecoders" 29 "github.com/mitchellh/mapstructure" 30 ) 31 32 const securityConfigKey = "security" 33 34 // DefaultConfig holds the default security policy. 35 var DefaultConfig = Config{ 36 Exec: Exec{ 37 Allow: MustNewWhitelist( 38 "^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded. 39 "^go$", // for Go Modules 40 "^npx$", // used by all Node tools (Babel, PostCSS). 41 "^postcss$", 42 ), 43 // These have been tested to work with Hugo's external programs 44 // on Windows, Linux and MacOS. 45 OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$`), 46 }, 47 Funcs: Funcs{ 48 Getenv: MustNewWhitelist("^HUGO_", "^CI$"), 49 }, 50 HTTP: HTTP{ 51 URLs: MustNewWhitelist(".*"), 52 Methods: MustNewWhitelist("(?i)GET|POST"), 53 }, 54 } 55 56 // Config is the top level security config. 57 // <docsmeta>{"name": "security", "description": "This section holds the top level security config.", "newIn": "0.91.0" }</docsmeta> 58 type Config struct { 59 // Restricts access to os.Exec.... 60 // <docsmeta>{ "newIn": "0.91.0" }</docsmeta> 61 Exec Exec `json:"exec"` 62 63 // Restricts access to certain template funcs. 64 Funcs Funcs `json:"funcs"` 65 66 // Restricts access to resources.GetRemote, getJSON, getCSV. 67 HTTP HTTP `json:"http"` 68 69 // Allow inline shortcodes 70 EnableInlineShortcodes bool `json:"enableInlineShortcodes"` 71 72 // Go templates related security config. 73 GoTemplates GoTemplates `json:"goTemplates"` 74 } 75 76 // Exec holds os/exec policies. 77 type Exec struct { 78 Allow Whitelist `json:"allow"` 79 OsEnv Whitelist `json:"osEnv"` 80 } 81 82 // Funcs holds template funcs policies. 83 type Funcs struct { 84 // OS env keys allowed to query in os.Getenv. 85 Getenv Whitelist `json:"getenv"` 86 } 87 88 type HTTP struct { 89 // URLs to allow in remote HTTP (resources.Get, getJSON, getCSV). 90 URLs Whitelist `json:"urls"` 91 92 // HTTP methods to allow. 93 Methods Whitelist `json:"methods"` 94 95 // Media types where the Content-Type in the response is used instead of resolving from the file content. 96 MediaTypes Whitelist `json:"mediaTypes"` 97 } 98 99 type GoTemplates struct { 100 101 // Enable to allow template actions inside bakcticks in ES6 template literals. 102 // This was blocked in Hugo 0.114.0 for security reasons and you now get errors on the form 103 // "... appears in a JS template literal" if you have this in your templates. 104 // See https://github.com/golang/go/issues/59234 105 AllowActionJSTmpl bool 106 } 107 108 // ToTOML converts c to TOML with [security] as the root. 109 func (c Config) ToTOML() string { 110 sec := c.ToSecurityMap() 111 112 var b bytes.Buffer 113 114 if err := parser.InterfaceToConfig(sec, metadecoders.TOML, &b); err != nil { 115 panic(err) 116 } 117 118 return strings.TrimSpace(b.String()) 119 } 120 121 func (c Config) CheckAllowedExec(name string) error { 122 if !c.Exec.Allow.Accept(name) { 123 return &AccessDeniedError{ 124 name: name, 125 path: "security.exec.allow", 126 policies: c.ToTOML(), 127 } 128 } 129 return nil 130 131 } 132 133 func (c Config) CheckAllowedGetEnv(name string) error { 134 if !c.Funcs.Getenv.Accept(name) { 135 return &AccessDeniedError{ 136 name: name, 137 path: "security.funcs.getenv", 138 policies: c.ToTOML(), 139 } 140 } 141 return nil 142 } 143 144 func (c Config) CheckAllowedHTTPURL(url string) error { 145 if !c.HTTP.URLs.Accept(url) { 146 return &AccessDeniedError{ 147 name: url, 148 path: "security.http.urls", 149 policies: c.ToTOML(), 150 } 151 } 152 return nil 153 } 154 155 func (c Config) CheckAllowedHTTPMethod(method string) error { 156 if !c.HTTP.Methods.Accept(method) { 157 return &AccessDeniedError{ 158 name: method, 159 path: "security.http.method", 160 policies: c.ToTOML(), 161 } 162 } 163 return nil 164 } 165 166 // ToSecurityMap converts c to a map with 'security' as the root key. 167 func (c Config) ToSecurityMap() map[string]any { 168 // Take it to JSON and back to get proper casing etc. 169 asJson, err := json.Marshal(c) 170 herrors.Must(err) 171 m := make(map[string]any) 172 herrors.Must(json.Unmarshal(asJson, &m)) 173 174 // Add the root 175 sec := map[string]any{ 176 "security": m, 177 } 178 return sec 179 180 } 181 182 // DecodeConfig creates a privacy Config from a given Hugo configuration. 183 func DecodeConfig(cfg config.Provider) (Config, error) { 184 sc := DefaultConfig 185 if cfg.IsSet(securityConfigKey) { 186 m := cfg.GetStringMap(securityConfigKey) 187 dec, err := mapstructure.NewDecoder( 188 &mapstructure.DecoderConfig{ 189 WeaklyTypedInput: true, 190 Result: &sc, 191 DecodeHook: stringSliceToWhitelistHook(), 192 }, 193 ) 194 if err != nil { 195 return sc, err 196 } 197 198 if err = dec.Decode(m); err != nil { 199 return sc, err 200 } 201 } 202 203 if !sc.EnableInlineShortcodes { 204 // Legacy 205 sc.EnableInlineShortcodes = cfg.GetBool("enableInlineShortcodes") 206 } 207 208 return sc, nil 209 210 } 211 212 func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType { 213 return func( 214 f reflect.Type, 215 t reflect.Type, 216 data any) (any, error) { 217 218 if t != reflect.TypeOf(Whitelist{}) { 219 return data, nil 220 } 221 222 wl := types.ToStringSlicePreserveString(data) 223 224 return NewWhitelist(wl...) 225 226 } 227 } 228 229 // AccessDeniedError represents a security policy conflict. 230 type AccessDeniedError struct { 231 path string 232 name string 233 policies string 234 } 235 236 func (e *AccessDeniedError) Error() string { 237 return fmt.Sprintf("access denied: %q is not whitelisted in policy %q; the current security configuration is:\n\n%s\n\n", e.name, e.path, e.policies) 238 } 239 240 // IsAccessDenied reports whether err is an AccessDeniedError 241 func IsAccessDenied(err error) bool { 242 var notFoundErr *AccessDeniedError 243 return errors.As(err, ¬FoundErr) 244 }