| 301 | } |
| 302 | |
| 303 | func (a *API) isValidExternalHost(w http.ResponseWriter, req *http.Request) (context.Context, error) { |
| 304 | ctx := req.Context() |
| 305 | config := a.config |
| 306 | |
| 307 | xForwardedHost := req.Header.Get("X-Forwarded-Host") |
| 308 | xForwardedProto := req.Header.Get("X-Forwarded-Proto") |
| 309 | reqHost := req.URL.Hostname() |
| 310 | |
| 311 | if len(config.Mailer.ExternalHosts) > 0 { |
| 312 | // this server is configured to accept multiple external hosts, validate the host from the X-Forwarded-Host or Host headers |
| 313 | |
| 314 | hostname := "" |
| 315 | protocol := "https" |
| 316 | |
| 317 | if xForwardedHost != "" { |
| 318 | if slices.Contains(config.Mailer.ExternalHosts, xForwardedHost) { |
| 319 | hostname = xForwardedHost |
| 320 | } |
| 321 | } else if reqHost != "" { |
| 322 | if slices.Contains(config.Mailer.ExternalHosts, reqHost) { |
| 323 | hostname = reqHost |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | if hostname != "" { |
| 328 | if hostname == "localhost" { |
| 329 | // allow the use of HTTP only if the accepted hostname was localhost |
| 330 | if xForwardedProto == "http" || req.URL.Scheme == "http" { |
| 331 | protocol = "http" |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | externalHostURL, err := url.ParseRequestURI(fmt.Sprintf("%s://%s", protocol, hostname)) |
| 336 | if err != nil { |
| 337 | return ctx, err |
| 338 | } |
| 339 | |
| 340 | return withExternalHost(ctx, externalHostURL), nil |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | if xForwardedHost != "" || reqHost != "" { |
| 345 | // host has been provided to the request, but it hasn't been |
| 346 | // added to the allow list, raise a log message |
| 347 | // in Supabase platform the X-Forwarded-Host and full request |
| 348 | // URL are likely sanitzied before they reach the server |
| 349 | |
| 350 | fields := make(logrus.Fields) |
| 351 | |
| 352 | if xForwardedHost != "" { |
| 353 | fields["x_forwarded_host"] = xForwardedHost |
| 354 | } |
| 355 | |
| 356 | if xForwardedProto != "" { |
| 357 | fields["x_forwarded_proto"] = xForwardedProto |
| 358 | } |
| 359 | |
| 360 | if reqHost != "" { |