From 553720f21209b8388d56ba7f9767d503df7ca5d7 Mon Sep 17 00:00:00 2001 From: Dimitris Sotirakis Date: Wed, 4 May 2022 13:47:36 +0100 Subject: [PATCH] Request interceptor: block redirects backport to v8.5.x #334 (#335) * Request interceptor: block redirects (cherry picked from commit 77865143a33328adfc89c874633510a34b0f1006) (cherry picked from commit 8e431c40a99669e1c9f52758d1c2e17ed0a51950) * handle location missing (cherry picked from commit fa93edf698cf96178881cc5f685e3747d9fb2b3e) (cherry picked from commit 4dea307274b415d9ae00ae0c908c7ca5b30be7f1) * Update pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go Co-authored-by: Marcus Efraimsson (cherry picked from commit 801b0b77110d1f366e2f692eb2c77537f8834879) * Update pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go Co-authored-by: Marcus Efraimsson (cherry picked from commit 8e1545f104f9f934ebce32ce6ed00f22adb25f38) * linter (cherry picked from commit 0e065f0f78e30901ac01b0cf1a9b31944ca4b12c) * fixes tests (cherry picked from commit a91a849df1e012097c971818eb8e7a9285620677) (cherry picked from commit aea9009f8a7e155a17e9e89d090451f191cf1b30) Co-authored-by: Leonard Gram --- .../host_redirect_validation_middleware.go | 37 +++++++++++++++++++ .../http_client_provider.go | 5 ++- .../http_client_provider_test.go | 12 +++--- 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go diff --git a/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go b/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go new file mode 100644 index 00000000000..7bffa1126c6 --- /dev/null +++ b/pkg/infra/httpclient/httpclientprovider/host_redirect_validation_middleware.go @@ -0,0 +1,37 @@ +package httpclientprovider + +import ( + "errors" + "net/http" + + "github.com/grafana/grafana/pkg/models" + + sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" +) + +const HostRedirectValidationMiddlewareName = "host-redirect-validation" + +func RedirectLimitMiddleware(reqValidator models.PluginRequestValidator) sdkhttpclient.Middleware { + return sdkhttpclient.NamedMiddlewareFunc(HostRedirectValidationMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper { + return sdkhttpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { + res, err := next.RoundTrip(req) + if err != nil { + return nil, err + } + if res.StatusCode >= 300 && res.StatusCode < 400 { + location, locationErr := res.Location() + if errors.Is(locationErr, http.ErrNoLocation) { + return res, nil + } + if locationErr != nil { + return nil, locationErr + } + + if validationErr := reqValidator.Validate(location.String(), nil); validationErr != nil { + return nil, validationErr + } + } + return res, nil + }) + }) +} diff --git a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go index 26b9b79c4b4..a011258afc2 100644 --- a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go +++ b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go @@ -5,6 +5,8 @@ import ( "net/http" "time" + "github.com/grafana/grafana/pkg/models" + sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics/metricutil" @@ -16,7 +18,7 @@ import ( var newProviderFunc = sdkhttpclient.NewProvider // New creates a new HTTP client provider with pre-configured middlewares. -func New(cfg *setting.Cfg, tracer tracing.Tracer) *sdkhttpclient.Provider { +func New(cfg *setting.Cfg, validator models.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider { logger := log.New("httpclient") userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion) @@ -27,6 +29,7 @@ func New(cfg *setting.Cfg, tracer tracing.Tracer) *sdkhttpclient.Provider { sdkhttpclient.BasicAuthenticationMiddleware(), sdkhttpclient.CustomHeadersMiddleware(), ResponseLimitMiddleware(cfg.ResponseLimit), + RedirectLimitMiddleware(validator), } if cfg.SigV4AuthEnabled { diff --git a/pkg/infra/httpclient/httpclientprovider/http_client_provider_test.go b/pkg/infra/httpclient/httpclientprovider/http_client_provider_test.go index d00deedf3bf..a0b58db5530 100644 --- a/pkg/infra/httpclient/httpclientprovider/http_client_provider_test.go +++ b/pkg/infra/httpclient/httpclientprovider/http_client_provider_test.go @@ -3,6 +3,8 @@ package httpclientprovider import ( "testing" + "github.com/grafana/grafana/pkg/services/validations" + sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/setting" @@ -22,10 +24,10 @@ func TestHTTPClientProvider(t *testing.T) { }) tracer, err := tracing.InitializeTracerForTest() require.NoError(t, err) - _ = New(&setting.Cfg{SigV4AuthEnabled: false}, tracer) + _ = New(&setting.Cfg{SigV4AuthEnabled: false}, &validations.OSSPluginRequestValidator{}, tracer) require.Len(t, providerOpts, 1) o := providerOpts[0] - require.Len(t, o.Middlewares, 6) + require.Len(t, o.Middlewares, 7) require.Equal(t, TracingMiddlewareName, o.Middlewares[0].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, DataSourceMetricsMiddlewareName, o.Middlewares[1].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, SetUserAgentMiddlewareName, o.Middlewares[2].(sdkhttpclient.MiddlewareName).MiddlewareName()) @@ -46,16 +48,16 @@ func TestHTTPClientProvider(t *testing.T) { }) tracer, err := tracing.InitializeTracerForTest() require.NoError(t, err) - _ = New(&setting.Cfg{SigV4AuthEnabled: true}, tracer) + _ = New(&setting.Cfg{SigV4AuthEnabled: true}, &validations.OSSPluginRequestValidator{}, tracer) require.Len(t, providerOpts, 1) o := providerOpts[0] - require.Len(t, o.Middlewares, 7) + require.Len(t, o.Middlewares, 8) require.Equal(t, TracingMiddlewareName, o.Middlewares[0].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, DataSourceMetricsMiddlewareName, o.Middlewares[1].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, SetUserAgentMiddlewareName, o.Middlewares[2].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, sdkhttpclient.BasicAuthenticationMiddlewareName, o.Middlewares[3].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, sdkhttpclient.CustomHeadersMiddlewareName, o.Middlewares[4].(sdkhttpclient.MiddlewareName).MiddlewareName()) require.Equal(t, ResponseLimitMiddlewareName, o.Middlewares[5].(sdkhttpclient.MiddlewareName).MiddlewareName()) - require.Equal(t, SigV4MiddlewareName, o.Middlewares[6].(sdkhttpclient.MiddlewareName).MiddlewareName()) + require.Equal(t, SigV4MiddlewareName, o.Middlewares[7].(sdkhttpclient.MiddlewareName).MiddlewareName()) }) }