VYPR
Moderate severityNVD Advisory· Published Oct 13, 2022· Updated Apr 23, 2025

Grafana data source and plugin proxy endpoints leaking authentication tokens to some destination plugins

CVE-2022-31130

Description

Grafana is an open source observability and data visualization platform. Versions of Grafana for endpoints prior to 9.1.8 and 8.5.14 could leak authentication tokens to some destination plugins under some conditions. The vulnerability impacts data source and plugin proxy endpoints with authentication tokens. The destination plugin could receive a user's Grafana authentication token. Versions 9.1.8 and 8.5.14 contain a patch for this issue. As a workaround, do not use API keys, JWT authentication, or any HTTP Header based authentication.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/grafana/grafanaGo
>= 9.0.0, < 9.1.89.1.8
github.com/grafana/grafanaGo
>= 7.0.0, < 8.5.148.5.14

Affected products

1

Patches

2
4dd56e4dabce

Security: Make proxy endpoints not leak sensitive HTTP headers

https://github.com/grafana/grafanaMarcus EfraimssonSep 9, 2022via ghsa
9 files changed · +102 2
  • pkg/api/plugins.go+9 0 modified
    @@ -24,6 +24,7 @@ import (
     	"github.com/grafana/grafana/pkg/plugins"
     	"github.com/grafana/grafana/pkg/plugins/backendplugin"
     	"github.com/grafana/grafana/pkg/plugins/manager/installer"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/grafana/grafana/pkg/services/pluginsettings"
     	"github.com/grafana/grafana/pkg/setting"
     	"github.com/grafana/grafana/pkg/util/errutil"
    @@ -546,6 +547,14 @@ func (hs *HTTPServer) makePluginResourceRequest(w http.ResponseWriter, req *http
     			hs.log.Warn("failed to to unpack JSONData in datasource instance settings", "err", err)
     		}
     	}
    +
    +	list := contexthandler.AuthHTTPHeaderListFromContext(req.Context())
    +	if list != nil {
    +		for _, name := range list.Items {
    +			req.Header.Del(name)
    +		}
    +	}
    +
     	proxyutil.ClearCookieHeader(req, keepCookieModel.KeepCookies)
     	proxyutil.PrepareProxyRequest(req)
     
    
  • pkg/api/plugins_test.go+8 0 modified
    @@ -20,6 +20,7 @@ import (
     	"github.com/grafana/grafana/pkg/infra/log"
     	"github.com/grafana/grafana/pkg/models"
     	"github.com/grafana/grafana/pkg/plugins"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/grafana/grafana/pkg/setting"
     	"github.com/grafana/grafana/pkg/web/webtest"
     )
    @@ -275,6 +276,12 @@ func TestMakePluginResourceRequest(t *testing.T) {
     		pluginClient: pluginClient,
     	}
     	req := httptest.NewRequest(http.MethodGet, "/", nil)
    +
    +	const customHeader = "X-CUSTOM"
    +	req.Header.Set(customHeader, "val")
    +	ctx := contexthandler.WithAuthHTTPHeader(req.Context(), customHeader)
    +	req = req.WithContext(ctx)
    +
     	resp := httptest.NewRecorder()
     	pCtx := backend.PluginContext{}
     	err := hs.makePluginResourceRequest(resp, req, pCtx)
    @@ -287,6 +294,7 @@ func TestMakePluginResourceRequest(t *testing.T) {
     	}
     
     	require.Equal(t, "sandbox", resp.Header().Get("Content-Security-Policy"))
    +	require.Empty(t, req.Header.Get(customHeader))
     }
     
     func callGetPluginAsset(sc *scenarioContext) {
    
  • pkg/middleware/middleware_basic_auth_test.go+6 0 modified
    @@ -36,6 +36,9 @@ func TestMiddlewareBasicAuth(t *testing.T) {
     		assert.True(t, sc.context.IsSignedIn)
     		assert.Equal(t, orgID, sc.context.OrgId)
     		assert.Equal(t, models.ROLE_EDITOR, sc.context.OrgRole)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{"Authorization"}, list.Items)
     	}, configure)
     
     	middlewareScenario(t, "Handle auth", func(t *testing.T, sc *scenarioContext) {
    @@ -69,6 +72,9 @@ func TestMiddlewareBasicAuth(t *testing.T) {
     
     		assert.True(t, sc.context.IsSignedIn)
     		assert.Equal(t, id, sc.context.UserId)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{"Authorization"}, list.Items)
     	}, configure)
     
     	middlewareScenario(t, "Should return error if user is not found", func(t *testing.T, sc *scenarioContext) {
    
  • pkg/middleware/middleware_jwt_auth_test.go+4 0 modified
    @@ -6,6 +6,7 @@ import (
     	"testing"
     
     	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
     
     	"github.com/grafana/grafana/pkg/models"
     	"github.com/grafana/grafana/pkg/services/contexthandler"
    @@ -54,6 +55,9 @@ func TestMiddlewareJWTAuth(t *testing.T) {
     		assert.Equal(t, orgID, sc.context.OrgId)
     		assert.Equal(t, id, sc.context.UserId)
     		assert.Equal(t, myUsername, sc.context.Login)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{sc.cfg.JWTAuthHeaderName}, list.Items)
     	}, configure, configureUsernameClaim)
     
     	middlewareScenario(t, "Valid token with valid email claim", func(t *testing.T, sc *scenarioContext) {
    
  • pkg/middleware/middleware_test.go+5 0 modified
    @@ -395,6 +395,11 @@ func TestMiddlewareContext(t *testing.T) {
     			assert.True(t, sc.context.IsSignedIn)
     			assert.Equal(t, userID, sc.context.UserId)
     			assert.Equal(t, orgID, sc.context.OrgId)
    +			list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +			require.NotNil(t, list)
    +			require.Contains(t, list.Items, sc.cfg.AuthProxyHeaderName)
    +			require.Contains(t, list.Items, "X-WEBAUTH-GROUPS")
    +			require.Contains(t, list.Items, "X-WEBAUTH-ROLE")
     		}, func(cfg *setting.Cfg) {
     			configure(cfg)
     			cfg.LDAPEnabled = false
    
  • pkg/services/contexthandler/auth_jwt.go+3 0 modified
    @@ -93,6 +93,9 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
     		return true
     	}
     
    +	newCtx := WithAuthHTTPHeader(ctx.Req.Context(), h.Cfg.JWTAuthHeaderName)
    +	*ctx.Req = *ctx.Req.WithContext(newCtx)
    +
     	ctx.SignedInUser = query.Result
     	ctx.IsSignedIn = true
     
    
  • pkg/services/contexthandler/contexthandler.go+52 2 modified
    @@ -211,6 +211,9 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
     		return true
     	}
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	// fetch key
     	keyQuery := models.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
     	if err := h.SQLStore.GetApiKeyByName(reqContext.Req.Context(), &keyQuery); err != nil {
    @@ -287,7 +290,7 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
     		return false
     	}
     
    -	ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
    +	_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
     	defer span.End()
     
     	username, password, err := util.DecodeBasicAuthHeader(header)
    @@ -296,12 +299,15 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
     		return true
     	}
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	authQuery := models.LoginUserQuery{
     		Username: username,
     		Password: password,
     		Cfg:      h.Cfg,
     	}
    -	if err := h.authenticator.AuthenticateUser(reqContext.Req.Context(), &authQuery); err != nil {
    +	if err := h.authenticator.AuthenticateUser(ctx, &authQuery); err != nil {
     		reqContext.Logger.Debug(
     			"Failed to authorize the user",
     			"username", username,
    @@ -523,6 +529,15 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
     
     	logger.Debug("Successfully got user info", "userID", user.UserId, "username", user.Login)
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), h.Cfg.AuthProxyHeaderName)
    +	for _, header := range h.Cfg.AuthProxyHeaders {
    +		if header != "" {
    +			ctx = WithAuthHTTPHeader(ctx, header)
    +		}
    +	}
    +
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	// Add user info to context
     	reqContext.SignedInUser = user
     	reqContext.IsSignedIn = true
    @@ -542,3 +557,38 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
     
     	return true
     }
    +
    +type authHTTPHeaderListContextKey struct{}
    +
    +var authHTTPHeaderListKey = authHTTPHeaderListContextKey{}
    +
    +// AuthHTTPHeaderList used to record HTTP headers that being when verifying authentication
    +// of an incoming HTTP request.
    +type AuthHTTPHeaderList struct {
    +	Items []string
    +}
    +
    +// WithAuthHTTPHeader returns a copy of parent in which the named HTTP header will be included
    +// and later retrievable by AuthHTTPHeaderListFromContext.
    +func WithAuthHTTPHeader(parent context.Context, name string) context.Context {
    +	list := AuthHTTPHeaderListFromContext(parent)
    +
    +	if list == nil {
    +		list = &AuthHTTPHeaderList{
    +			Items: []string{},
    +		}
    +	}
    +
    +	list.Items = append(list.Items, name)
    +
    +	return context.WithValue(parent, authHTTPHeaderListKey, list)
    +}
    +
    +// AuthHTTPHeaderListFromContext returns the AuthHTTPHeaderList in a context.Context, if any,
    +// and will include any HTTP headers used when verifying authentication of an incoming HTTP request.
    +func AuthHTTPHeaderListFromContext(c context.Context) *AuthHTTPHeaderList {
    +	if list, ok := c.Value(authHTTPHeaderListKey).(*AuthHTTPHeaderList); ok {
    +		return list
    +	}
    +	return nil
    +}
    
  • pkg/util/proxyutil/reverse_proxy.go+8 0 modified
    @@ -10,6 +10,7 @@ import (
     	"time"
     
     	glog "github.com/grafana/grafana/pkg/infra/log"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     )
     
     // StatusClientClosedRequest A non-standard status code introduced by nginx
    @@ -66,6 +67,13 @@ func NewReverseProxy(logger glog.Logger, director func(*http.Request), opts ...R
     // wrapDirector wraps a director and adds additional functionality.
     func wrapDirector(d func(*http.Request)) func(req *http.Request) {
     	return func(req *http.Request) {
    +		list := contexthandler.AuthHTTPHeaderListFromContext(req.Context())
    +		if list != nil {
    +			for _, name := range list.Items {
    +				req.Header.Del(name)
    +			}
    +		}
    +
     		d(req)
     		PrepareProxyRequest(req)
     
    
  • pkg/util/proxyutil/reverse_proxy_test.go+7 0 modified
    @@ -9,6 +9,7 @@ import (
     	"time"
     
     	"github.com/grafana/grafana/pkg/infra/log"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/stretchr/testify/require"
     )
     
    @@ -30,6 +31,11 @@ func TestReverseProxy(t *testing.T) {
     		req.Header.Set("Referer", "https://test.com/api")
     		req.RemoteAddr = "10.0.0.1"
     
    +		const customHeader = "X-CUSTOM"
    +		req.Header.Set(customHeader, "val")
    +		ctx := contexthandler.WithAuthHTTPHeader(req.Context(), customHeader)
    +		req = req.WithContext(ctx)
    +
     		rp := NewReverseProxy(log.New("test"), func(req *http.Request) {
     			req.Header.Set("X-KEY", "value")
     		})
    @@ -49,6 +55,7 @@ func TestReverseProxy(t *testing.T) {
     		require.Empty(t, resp.Cookies())
     		require.Equal(t, "sandbox", resp.Header.Get("Content-Security-Policy"))
     		require.NoError(t, resp.Body.Close())
    +		require.Empty(t, actualReq.Header.Get(customHeader))
     	})
     
     	t.Run("When proxying a request using WithModifyResponse should call it before default ModifyResponse func", func(t *testing.T) {
    
9da278c044ba

Plugins: Make proxy endpoints not leak sensitive HTTP headers

https://github.com/grafana/grafanaMarcus EfraimssonSep 9, 2022via ghsa
9 files changed · +102 2
  • pkg/api/plugin_resource.go+9 0 modified
    @@ -15,6 +15,7 @@ import (
     
     	"github.com/grafana/grafana/pkg/models"
     	"github.com/grafana/grafana/pkg/plugins/backendplugin"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/grafana/grafana/pkg/services/datasources"
     	"github.com/grafana/grafana/pkg/util/proxyutil"
     	"github.com/grafana/grafana/pkg/web"
    @@ -118,6 +119,14 @@ func (hs *HTTPServer) makePluginResourceRequest(w http.ResponseWriter, req *http
     			hs.log.Warn("failed to unpack JSONData in datasource instance settings", "err", err)
     		}
     	}
    +
    +	list := contexthandler.AuthHTTPHeaderListFromContext(req.Context())
    +	if list != nil {
    +		for _, name := range list.Items {
    +			req.Header.Del(name)
    +		}
    +	}
    +
     	proxyutil.ClearCookieHeader(req, keepCookieModel.KeepCookies)
     	proxyutil.PrepareProxyRequest(req)
     
    
  • pkg/api/plugins_test.go+8 0 modified
    @@ -21,6 +21,7 @@ import (
     	"github.com/grafana/grafana/pkg/infra/log/logtest"
     	"github.com/grafana/grafana/pkg/models"
     	"github.com/grafana/grafana/pkg/plugins"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/grafana/grafana/pkg/services/quota/quotatest"
     	"github.com/grafana/grafana/pkg/setting"
     	"github.com/grafana/grafana/pkg/web/webtest"
    @@ -271,6 +272,12 @@ func TestMakePluginResourceRequest(t *testing.T) {
     		pluginClient: pluginClient,
     	}
     	req := httptest.NewRequest(http.MethodGet, "/", nil)
    +
    +	const customHeader = "X-CUSTOM"
    +	req.Header.Set(customHeader, "val")
    +	ctx := contexthandler.WithAuthHTTPHeader(req.Context(), customHeader)
    +	req = req.WithContext(ctx)
    +
     	resp := httptest.NewRecorder()
     	pCtx := backend.PluginContext{}
     	err := hs.makePluginResourceRequest(resp, req, pCtx)
    @@ -283,6 +290,7 @@ func TestMakePluginResourceRequest(t *testing.T) {
     	}
     
     	require.Equal(t, "sandbox", resp.Header().Get("Content-Security-Policy"))
    +	require.Empty(t, req.Header.Get(customHeader))
     }
     
     func callGetPluginAsset(sc *scenarioContext) {
    
  • pkg/middleware/middleware_basic_auth_test.go+6 0 modified
    @@ -37,6 +37,9 @@ func TestMiddlewareBasicAuth(t *testing.T) {
     		assert.True(t, sc.context.IsSignedIn)
     		assert.Equal(t, orgID, sc.context.OrgId)
     		assert.Equal(t, models.ROLE_EDITOR, sc.context.OrgRole)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{"Authorization"}, list.Items)
     	}, configure)
     
     	middlewareScenario(t, "Handle auth", func(t *testing.T, sc *scenarioContext) {
    @@ -70,6 +73,9 @@ func TestMiddlewareBasicAuth(t *testing.T) {
     
     		assert.True(t, sc.context.IsSignedIn)
     		assert.Equal(t, id, sc.context.UserId)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{"Authorization"}, list.Items)
     	}, configure)
     
     	middlewareScenario(t, "Should return error if user is not found", func(t *testing.T, sc *scenarioContext) {
    
  • pkg/middleware/middleware_jwt_auth_test.go+4 0 modified
    @@ -6,6 +6,7 @@ import (
     	"testing"
     
     	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
     
     	"github.com/grafana/grafana/pkg/models"
     	"github.com/grafana/grafana/pkg/services/contexthandler"
    @@ -55,6 +56,9 @@ func TestMiddlewareJWTAuth(t *testing.T) {
     		assert.Equal(t, orgID, sc.context.OrgId)
     		assert.Equal(t, id, sc.context.UserId)
     		assert.Equal(t, myUsername, sc.context.Login)
    +		list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +		require.NotNil(t, list)
    +		require.EqualValues(t, []string{sc.cfg.JWTAuthHeaderName}, list.Items)
     	}, configure, configureUsernameClaim)
     
     	middlewareScenario(t, "Valid token with valid email claim", func(t *testing.T, sc *scenarioContext) {
    
  • pkg/middleware/middleware_test.go+5 0 modified
    @@ -396,6 +396,11 @@ func TestMiddlewareContext(t *testing.T) {
     			assert.True(t, sc.context.IsSignedIn)
     			assert.Equal(t, userID, sc.context.UserId)
     			assert.Equal(t, orgID, sc.context.OrgId)
    +			list := contexthandler.AuthHTTPHeaderListFromContext(sc.context.Req.Context())
    +			require.NotNil(t, list)
    +			require.Contains(t, list.Items, sc.cfg.AuthProxyHeaderName)
    +			require.Contains(t, list.Items, "X-WEBAUTH-GROUPS")
    +			require.Contains(t, list.Items, "X-WEBAUTH-ROLE")
     		}, func(cfg *setting.Cfg) {
     			configure(cfg)
     			cfg.LDAPEnabled = false
    
  • pkg/services/contexthandler/auth_jwt.go+3 0 modified
    @@ -99,6 +99,9 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
     		return true
     	}
     
    +	newCtx := WithAuthHTTPHeader(ctx.Req.Context(), h.Cfg.JWTAuthHeaderName)
    +	*ctx.Req = *ctx.Req.WithContext(newCtx)
    +
     	ctx.SignedInUser = query.Result
     	ctx.IsSignedIn = true
     
    
  • pkg/services/contexthandler/contexthandler.go+52 2 modified
    @@ -244,6 +244,9 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
     	_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAPIKey")
     	defer span.End()
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	var (
     		apikey *models.ApiKey
     		errKey error
    @@ -326,7 +329,7 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
     		return false
     	}
     
    -	ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
    +	_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
     	defer span.End()
     
     	username, password, err := util.DecodeBasicAuthHeader(header)
    @@ -335,12 +338,15 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
     		return true
     	}
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	authQuery := models.LoginUserQuery{
     		Username: username,
     		Password: password,
     		Cfg:      h.Cfg,
     	}
    -	if err := h.authenticator.AuthenticateUser(reqContext.Req.Context(), &authQuery); err != nil {
    +	if err := h.authenticator.AuthenticateUser(ctx, &authQuery); err != nil {
     		reqContext.Logger.Debug(
     			"Failed to authorize the user",
     			"username", username,
    @@ -571,6 +577,15 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
     
     	logger.Debug("Successfully got user info", "userID", user.UserId, "username", user.Login)
     
    +	ctx := WithAuthHTTPHeader(reqContext.Req.Context(), h.Cfg.AuthProxyHeaderName)
    +	for _, header := range h.Cfg.AuthProxyHeaders {
    +		if header != "" {
    +			ctx = WithAuthHTTPHeader(ctx, header)
    +		}
    +	}
    +
    +	*reqContext.Req = *reqContext.Req.WithContext(ctx)
    +
     	// Add user info to context
     	reqContext.SignedInUser = user
     	reqContext.IsSignedIn = true
    @@ -590,3 +605,38 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
     
     	return true
     }
    +
    +type authHTTPHeaderListContextKey struct{}
    +
    +var authHTTPHeaderListKey = authHTTPHeaderListContextKey{}
    +
    +// AuthHTTPHeaderList used to record HTTP headers that being when verifying authentication
    +// of an incoming HTTP request.
    +type AuthHTTPHeaderList struct {
    +	Items []string
    +}
    +
    +// WithAuthHTTPHeader returns a copy of parent in which the named HTTP header will be included
    +// and later retrievable by AuthHTTPHeaderListFromContext.
    +func WithAuthHTTPHeader(parent context.Context, name string) context.Context {
    +	list := AuthHTTPHeaderListFromContext(parent)
    +
    +	if list == nil {
    +		list = &AuthHTTPHeaderList{
    +			Items: []string{},
    +		}
    +	}
    +
    +	list.Items = append(list.Items, name)
    +
    +	return context.WithValue(parent, authHTTPHeaderListKey, list)
    +}
    +
    +// AuthHTTPHeaderListFromContext returns the AuthHTTPHeaderList in a context.Context, if any,
    +// and will include any HTTP headers used when verifying authentication of an incoming HTTP request.
    +func AuthHTTPHeaderListFromContext(c context.Context) *AuthHTTPHeaderList {
    +	if list, ok := c.Value(authHTTPHeaderListKey).(*AuthHTTPHeaderList); ok {
    +		return list
    +	}
    +	return nil
    +}
    
  • pkg/util/proxyutil/reverse_proxy.go+8 0 modified
    @@ -10,6 +10,7 @@ import (
     	"time"
     
     	glog "github.com/grafana/grafana/pkg/infra/log"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     )
     
     // StatusClientClosedRequest A non-standard status code introduced by nginx
    @@ -66,6 +67,13 @@ func NewReverseProxy(logger glog.Logger, director func(*http.Request), opts ...R
     // wrapDirector wraps a director and adds additional functionality.
     func wrapDirector(d func(*http.Request)) func(req *http.Request) {
     	return func(req *http.Request) {
    +		list := contexthandler.AuthHTTPHeaderListFromContext(req.Context())
    +		if list != nil {
    +			for _, name := range list.Items {
    +				req.Header.Del(name)
    +			}
    +		}
    +
     		d(req)
     		PrepareProxyRequest(req)
     
    
  • pkg/util/proxyutil/reverse_proxy_test.go+7 0 modified
    @@ -9,6 +9,7 @@ import (
     	"time"
     
     	"github.com/grafana/grafana/pkg/infra/log"
    +	"github.com/grafana/grafana/pkg/services/contexthandler"
     	"github.com/stretchr/testify/require"
     )
     
    @@ -30,6 +31,11 @@ func TestReverseProxy(t *testing.T) {
     		req.Header.Set("Referer", "https://test.com/api")
     		req.RemoteAddr = "10.0.0.1"
     
    +		const customHeader = "X-CUSTOM"
    +		req.Header.Set(customHeader, "val")
    +		ctx := contexthandler.WithAuthHTTPHeader(req.Context(), customHeader)
    +		req = req.WithContext(ctx)
    +
     		rp := NewReverseProxy(log.New("test"), func(req *http.Request) {
     			req.Header.Set("X-KEY", "value")
     		})
    @@ -49,6 +55,7 @@ func TestReverseProxy(t *testing.T) {
     		require.Empty(t, resp.Cookies())
     		require.Equal(t, "sandbox", resp.Header.Get("Content-Security-Policy"))
     		require.NoError(t, resp.Body.Close())
    +		require.Empty(t, actualReq.Header.Get(customHeader))
     	})
     
     	t.Run("When proxying a request using WithModifyResponse should call it before default ModifyResponse func", func(t *testing.T) {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.