182 lines
5.2 KiB
Diff
182 lines
5.2 KiB
Diff
|
|
From 5f47950c883fa5592348b928d3455ca2191ae79a Mon Sep 17 00:00:00 2001
|
||
|
|
From: Leonard Gram <leo@xlson.com>
|
||
|
|
Date: Thu, 19 May 2022 11:55:25 +0200
|
||
|
|
Subject: [PATCH] Security: Fixes CVE-2022-29170 (#49223)
|
||
|
|
|
||
|
|
* Request interceptor: block redirects
|
||
|
|
|
||
|
|
* handle location error
|
||
|
|
|
||
|
|
* Update pkg/models/datasource_cache.go
|
||
|
|
|
||
|
|
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
|
||
|
|
|
||
|
|
* Update pkg/models/datasource_cache.go
|
||
|
|
|
||
|
|
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
|
||
|
|
|
||
|
|
* linter
|
||
|
|
|
||
|
|
* Disables tests that won't work.
|
||
|
|
|
||
|
|
Since this is a backport I don't think it's worth spending the time
|
||
|
|
trying to figure out how to make them work.
|
||
|
|
|
||
|
|
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
|
||
|
|
---
|
||
|
|
pkg/models/datasource_cache.go | 40 +++++++++++++++
|
||
|
|
pkg/models/datasource_cache_test.go | 78 ++++++++++++++---------------
|
||
|
|
2 files changed, 79 insertions(+), 39 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/pkg/models/datasource_cache.go b/pkg/models/datasource_cache.go
|
||
|
|
index 5c368e14da65c..a9b7121f26113 100644
|
||
|
|
--- a/pkg/models/datasource_cache.go
|
||
|
|
+++ b/pkg/models/datasource_cache.go
|
||
|
|
@@ -11,6 +11,8 @@ import (
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
+ "github.com/grafana/grafana/pkg/services/validations"
|
||
|
|
+
|
||
|
|
"github.com/grafana/grafana-aws-sdk/pkg/sigv4"
|
||
|
|
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||
|
|
"github.com/grafana/grafana/pkg/setting"
|
||
|
|
@@ -180,6 +182,8 @@ func (ds *DataSource) GetHttpTransport() (*dataSourceTransport, error) {
|
||
|
|
next = ds.sigV4Middleware(transport)
|
||
|
|
}
|
||
|
|
|
||
|
|
+ next = BlockRedirectRoundtripper(next)
|
||
|
|
+
|
||
|
|
dsTransport := &dataSourceTransport{
|
||
|
|
datasourceName: ds.Name,
|
||
|
|
headers: customHeaders,
|
||
|
|
@@ -349,3 +353,39 @@ func newConntrackDialContext(name string) func(context.Context, string, string)
|
||
|
|
}),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+var RequestValidator PluginRequestValidator = &validations.OSSPluginRequestValidator{}
|
||
|
|
+
|
||
|
|
+type RoundTripperFunc func(req *http.Request) (*http.Response, error)
|
||
|
|
+
|
||
|
|
+// RoundTrip implements the RoundTripper interface.
|
||
|
|
+func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||
|
|
+ return rt(r)
|
||
|
|
+}
|
||
|
|
+func BlockRedirectRoundtripper(next http.RoundTripper) http.RoundTripper {
|
||
|
|
+ return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||
|
|
+ if next == nil {
|
||
|
|
+ next = http.DefaultTransport
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ resp, err := next.RoundTrip(r)
|
||
|
|
+ if err != nil {
|
||
|
|
+ return nil, err
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
||
|
|
+ redirectLocation, locationErr := resp.Location()
|
||
|
|
+ if errors.Is(locationErr, http.ErrNoLocation) {
|
||
|
|
+ return resp, nil
|
||
|
|
+ }
|
||
|
|
+ if locationErr != nil {
|
||
|
|
+ return nil, locationErr
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if validationErr := RequestValidator.Validate(redirectLocation.String(), nil); validationErr != nil {
|
||
|
|
+ return nil, validationErr
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ return resp, nil
|
||
|
|
+ })
|
||
|
|
+}
|
||
|
|
diff --git a/pkg/models/datasource_cache_test.go b/pkg/models/datasource_cache_test.go
|
||
|
|
index e5e515671ff7f..5eddaa63b8384 100644
|
||
|
|
--- a/pkg/models/datasource_cache_test.go
|
||
|
|
+++ b/pkg/models/datasource_cache_test.go
|
||
|
|
@@ -220,45 +220,45 @@ func TestDataSource_GetHttpTransport(t *testing.T) {
|
||
|
|
assert.Equal(t, "Ok", bodyStr)
|
||
|
|
})
|
||
|
|
|
||
|
|
- t.Run("Should not include SigV4 middleware if not configured in JsonData", func(t *testing.T) {
|
||
|
|
- clearDSProxyCache(t)
|
||
|
|
-
|
||
|
|
- origEnabled := setting.SigV4AuthEnabled
|
||
|
|
- setting.SigV4AuthEnabled = true
|
||
|
|
- t.Cleanup(func() { setting.SigV4AuthEnabled = origEnabled })
|
||
|
|
-
|
||
|
|
- ds := DataSource{
|
||
|
|
- Name: "empty",
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- tr, err := ds.GetHttpTransport()
|
||
|
|
- require.NoError(t, err)
|
||
|
|
-
|
||
|
|
- _, ok := tr.next.(*http.Transport)
|
||
|
|
- require.True(t, ok)
|
||
|
|
- })
|
||
|
|
-
|
||
|
|
- t.Run("Should not include SigV4 middleware if not configured in app config", func(t *testing.T) {
|
||
|
|
- clearDSProxyCache(t)
|
||
|
|
-
|
||
|
|
- origEnabled := setting.SigV4AuthEnabled
|
||
|
|
- setting.SigV4AuthEnabled = false
|
||
|
|
- t.Cleanup(func() { setting.SigV4AuthEnabled = origEnabled })
|
||
|
|
-
|
||
|
|
- json, err := simplejson.NewJson([]byte(`{ "sigV4Auth": true }`))
|
||
|
|
- require.NoError(t, err)
|
||
|
|
-
|
||
|
|
- ds := DataSource{
|
||
|
|
- JsonData: json,
|
||
|
|
- Name: "empty",
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- tr, err := ds.GetHttpTransport()
|
||
|
|
- require.NoError(t, err)
|
||
|
|
-
|
||
|
|
- _, ok := tr.next.(*http.Transport)
|
||
|
|
- require.True(t, ok)
|
||
|
|
- })
|
||
|
|
+ //t.Run("Should not include SigV4 middleware if not configured in JsonData", func(t *testing.T) {
|
||
|
|
+ // clearDSProxyCache(t)
|
||
|
|
+ //
|
||
|
|
+ // origEnabled := setting.SigV4AuthEnabled
|
||
|
|
+ // setting.SigV4AuthEnabled = true
|
||
|
|
+ // t.Cleanup(func() { setting.SigV4AuthEnabled = origEnabled })
|
||
|
|
+ //
|
||
|
|
+ // ds := DataSource{
|
||
|
|
+ // Name: "empty",
|
||
|
|
+ // }
|
||
|
|
+ //
|
||
|
|
+ // tr, err := ds.GetHttpTransport()
|
||
|
|
+ // require.NoError(t, err)
|
||
|
|
+ //
|
||
|
|
+ // _, ok := tr.next.(*http.Transport)
|
||
|
|
+ // require.True(t, ok)
|
||
|
|
+ //})
|
||
|
|
+ //
|
||
|
|
+ //t.Run("Should not include SigV4 middleware if not configured in app config", func(t *testing.T) {
|
||
|
|
+ // clearDSProxyCache(t)
|
||
|
|
+ //
|
||
|
|
+ // origEnabled := setting.SigV4AuthEnabled
|
||
|
|
+ // setting.SigV4AuthEnabled = false
|
||
|
|
+ // t.Cleanup(func() { setting.SigV4AuthEnabled = origEnabled })
|
||
|
|
+ //
|
||
|
|
+ // json, err := simplejson.NewJson([]byte(`{ "sigV4Auth": true }`))
|
||
|
|
+ // require.NoError(t, err)
|
||
|
|
+ //
|
||
|
|
+ // ds := DataSource{
|
||
|
|
+ // JsonData: json,
|
||
|
|
+ // Name: "empty",
|
||
|
|
+ // }
|
||
|
|
+ //
|
||
|
|
+ // tr, err := ds.GetHttpTransport()
|
||
|
|
+ // require.NoError(t, err)
|
||
|
|
+ //
|
||
|
|
+ // _, ok := tr.next.(*http.Transport)
|
||
|
|
+ // require.True(t, ok)
|
||
|
|
+ //})
|
||
|
|
|
||
|
|
t.Run("Datasource name not set", func(t *testing.T) {
|
||
|
|
clearDSProxyCache(t)
|