diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index d51039eba..4a4d14da6 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -16,6 +16,7 @@ package create import ( + "errors" "net/http" "os" "path" @@ -97,6 +98,15 @@ func New(rs *resources.Spec) *Client { remoteResourceLogger: rs.Logger.InfoCommand("remote"), httpClient: &http.Client{ Timeout: httpTimeout, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if err := rs.ExecHelper.Sec().CheckAllowedHTTPURL(req.URL.String()); err != nil { + return err + } + if len(via) >= 10 { + return errors.New("stopped after 10 redirects") + } + return nil + }, Transport: &httpcache.Transport{ Cache: fileCache.AsHTTPCache(), CacheKey: func(req *http.Request) string { diff --git a/resources/resource_factories/create/create_integration_test.go b/resources/resource_factories/create/create_integration_test.go index c2c234bcc..783161b31 100644 --- a/resources/resource_factories/create/create_integration_test.go +++ b/resources/resource_factories/create/create_integration_test.go @@ -276,6 +276,47 @@ urls = ['.*'] }) } +// Issue 14871. +func TestGetRemoteRedirectSecurityCheckIssue14871(t *testing.T) { + t.Parallel() + + // Final server: the redirect target. Its host must be denied by security.http.urls. + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("should not reach here")) + })) + t.Cleanup(func() { target.Close() }) + + // Redirector: the allowed host. Redirects to the denied target. + redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, target.URL+"/", http.StatusFound) + })) + t.Cleanup(func() { redirector.Close() }) + + files := ` +-- hugo.toml -- +[security] +[security.http] +urls = ['REDIRECTOR'] +mediaTypes = ['text/plain'] +-- layouts/home.html -- +{{ $url := "REDIRECTOR/" }} +{{ with try (resources.GetRemote $url) }} + {{ with .Err }} + Err: {{ . }} + {{ else with .Value }} + Content: {{ .Content }} + {{ end }} +{{ end }} +` + files = strings.ReplaceAll(files, "REDIRECTOR", redirector.URL) + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "Err:", "security.http.urls") + b.AssertFileContent("public/index.html", "! should not reach here") +} + // Issue 14611. func TestGetRemotePerRequestTimeoutBodyRead(t *testing.T) { t.Parallel()