VYPR
Medium severity4.3GHSA Advisory· Published Jul 17, 2025· Updated Apr 15, 2026

CVE-2025-3415

CVE-2025-3415

Description

Grafana is an open-source platform for monitoring and observability. The Grafana Alerting DingDing integration was not properly protected and could be exposed to users with Viewer permission. Fixed in versions 10.4.19+security-01, 11.2.10+security-01, 11.3.7+security-01, 11.4.5+security-01, 11.5.5+security-01, 11.6.2+security-01 and 12.0.1+security-01

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Grafana Alerting DingDing integration exposes webhook URL to users with Viewer permission due to missing secret field handling.

Vulnerability

The Grafana Alerting DingDing integration did not properly protect the webhook URL configured for that channel. The vulnerability stems from the fact that the integration’s URL field was not declared as a secure setting. As a result, a Grafana user with only Viewer permissions — who would normally have read-only access to alerting configurations — could retrieve the DingDing webhook URL through the Alerting API or UI. The affected code path lacked the necessary patchNewSecureFields logic that decrypts and migrates the URL from SecureSettings into Settings only when needed, a fix introduced in the referenced commits [2][3][4].

Exploitation

To exploit this issue, an attacker must be authenticated to a Grafana instance and have the Viewer role (the lowest built-in non-anonymous role). No additional privileges are required beyond those available to a Viewer. The attacker can then navigate to the Alerting configuration for a DingDing contact point or query the relevant API endpoint to obtain the plaintext URL. The attack is low complexity and requires no special network position beyond normal Grafana access. The vulnerability exists across multiple Grafana versions prior to the fixed releases listed in the advisory [1].

Impact

An attacker who obtains the DingDing webhook URL can send arbitrary messages to that DingTalk chat group, impersonating the Grafana alert notification. This could lead to social engineering, distribution of malicious links, or other abuse within the communication platform. The integrity and reliability of automated alert notifications are compromised. The confidentiality of the webhook URL is breached, which is treated as a secret token.

Mitigation

Grafana has released patches for all supported versions: 10.4.19+security-01, 11.2.10+security-01, 11.3.7+security-01, 11.4.5+security-01, 11.5.5+security-01, 11.6.2+security-01, and 12.0.1+security-01 [1]. Organizations running affected versions should upgrade immediately. There is no known workaround; upgrading is the only remediating action. The fix ensures the DingDing URL is handled as a secure field and not exposed to low-privilege users.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/grafana/grafanaGo
< 1.9.2-0.20250514160932-04111e9f2afd1.9.2-0.20250514160932-04111e9f2afd

Affected products

2
  • Grafana/GrafanaGHSA2 versions
    < 1.9.2-0.20250514160932-04111e9f2afd+ 1 more
    • (no CPE)range: < 1.9.2-0.20250514160932-04111e9f2afd
    • (no CPE)range: >=10.4.19+security-01, <11.2.10+security-01, >=11.2.10+security-01, <11.3.7+security-01, >=11.3.7+security-01, <11.4.5+security-01, >=11.4.5+security-01, <11.5.5+security-01, >=11.5.5+security-01, <11.6.2+security-01, >=11.6.2+security-01, <12.0.1+security-01

Patches

8
910eb1dd9e61

Security: apply patch 428 (#106710)

https://github.com/grafana/grafanaKevin MinehartJun 13, 2025via ghsa
5 files changed · +66 8
  • pkg/services/ngalert/notifier/alertmanager.go+55 1 modified
    @@ -53,6 +53,7 @@ type alertmanager struct {
     	Store                AlertingStore
     	stateStore           stateStore
     	DefaultConfiguration string
    +	decryptFn            alertingNotify.GetDecryptedValueFn
     }
     
     // maintenanceOptions represent the options for components that need maintenance on a frequency within the Alertmanager.
    @@ -149,6 +150,7 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
     		Store:                store,
     		stateStore:           stateStore,
     		logger:               l.New("component", "alertmanager", opts.TenantKey, opts.TenantID), // similar to what the base does
    +		decryptFn:            decryptFn,
     	}
     
     	return am, nil
    @@ -335,14 +337,22 @@ func (am *alertmanager) applyConfig(ctx context.Context, cfg *apimodels.Postable
     		return false, nil
     	}
     
    +	receivers := PostableApiAlertingConfigToApiReceivers(cfg.AlertmanagerConfig)
    +	for _, recv := range receivers {
    +		err = patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +		if err != nil {
    +			return false, err
    +		}
    +	}
    +
     	am.logger.Info("Applying new configuration to Alertmanager", "configHash", fmt.Sprintf("%x", configHash))
     	err = am.Base.ApplyConfig(alertingNotify.NotificationsConfiguration{
     		RoutingTree:       cfg.AlertmanagerConfig.Route.AsAMRoute(),
     		InhibitRules:      cfg.AlertmanagerConfig.InhibitRules,
     		MuteTimeIntervals: cfg.AlertmanagerConfig.MuteTimeIntervals,
     		TimeIntervals:     cfg.AlertmanagerConfig.TimeIntervals,
     		Templates:         ToTemplateDefinitions(cfg),
    -		Receivers:         PostableApiAlertingConfigToApiReceivers(cfg.AlertmanagerConfig),
    +		Receivers:         receivers,
     		DispatcherLimits:  &nilLimits{},
     		Raw:               rawConfig,
     		Hash:              configHash,
    @@ -355,6 +365,50 @@ func (am *alertmanager) applyConfig(ctx context.Context, cfg *apimodels.Postable
     	return true, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decode, decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	if _, ok := integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(integration.SecureSettings)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, decoded, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -284,6 +284,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -24,12 +24,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	a := &alertingNotify.PostableAlert{}
     	if c.Alert != nil {
    
  • pkg/tests/api/alerting/api_notification_channel_test.go+2 4 modified
    @@ -2088,10 +2088,8 @@ var expAlertmanagerConfigFromAPI = `
                 "name": "dingding_test",
                 "type": "dingding",
                 "disableResolveMessage": false,
    -            "settings": {
    -              "url": "http://CHANNEL_ADDR/dingding_recv/dingding_test"
    -            },
    -            "secureFields": {}
    +            "settings": {},
    +            "secureFields": {"url": true}
               }
             ]
           },
    
19c912476d4f

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
4 files changed · +59 3
  • pkg/services/ngalert/notifier/alertmanager.go+50 0 modified
    @@ -385,6 +385,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -411,6 +415,52 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	var encrypted string
    +	var ok bool
    +	if encrypted, ok = integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(encrypted)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, map[string][]byte{key: decoded}, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -126,6 +126,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -23,12 +23,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    
0adb869188fa

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
4 files changed · +57 3
  • pkg/services/ngalert/notifier/alertmanager.go+48 0 modified
    @@ -387,6 +387,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -414,6 +418,50 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decode, decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	if _, ok := integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(integration.SecureSettings)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, decoded, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -127,6 +127,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -24,12 +24,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	a := &alertingNotify.PostableAlert{}
     	if c.Alert != nil {
    
4fc33647a829

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
4 files changed · +59 3
  • pkg/services/ngalert/notifier/alertmanager.go+50 0 modified
    @@ -385,6 +385,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -411,6 +415,52 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	var encrypted string
    +	var ok bool
    +	if encrypted, ok = integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(encrypted)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, map[string][]byte{key: decoded}, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -126,6 +126,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -23,12 +23,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    
a78de30720b4

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
3 files changed · +58 2
  • pkg/services/ngalert/notifier/alertmanager.go+50 0 modified
    @@ -380,6 +380,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -406,6 +410,52 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	var encrypted string
    +	var ok bool
    +	if encrypted, ok = integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(encrypted)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, map[string][]byte{key: decoded}, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -125,6 +125,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -44,12 +44,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, am.decryptFn)
    +		if err != nil {
    +			return nil, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    
04111e9f2afd

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
3 files changed · +58 2
  • pkg/services/ngalert/notifier/alertmanager.go+50 0 modified
    @@ -388,6 +388,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -414,6 +418,52 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	var encrypted string
    +	var ok bool
    +	if encrypted, ok = integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(encrypted)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, map[string][]byte{key: decoded}, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -124,6 +124,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -46,12 +46,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, am.decryptFn)
    +		if err != nil {
    +			return nil, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    
4144c636d1a6

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
4 files changed · +57 3
  • pkg/services/ngalert/notifier/alertmanager.go+48 0 modified
    @@ -398,6 +398,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -422,6 +426,50 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decode, decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	if _, ok := integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(integration.SecureSettings)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, decoded, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -127,6 +127,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -23,12 +23,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    
91327938626c

declare dingding url as secret

https://github.com/grafana/grafanaYuri TseretyanMay 14, 2025via ghsa
4 files changed · +59 3
  • pkg/services/ngalert/notifier/alertmanager.go+50 0 modified
    @@ -385,6 +385,10 @@ func (am *alertmanager) AppURL() string {
     
     // buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
     func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
    +	err := patchNewSecureFields(context.Background(), receiver, am.decryptFn)
    +	if err != nil {
    +		return nil, err
    +	}
     	receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
     	if err != nil {
     		return nil, err
    @@ -411,6 +415,52 @@ func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
     	return integrations, nil
     }
     
    +func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	for _, integration := range api.Integrations {
    +		switch integration.Type {
    +		case "dingding":
    +			err := patchSettingsFromSecureSettings(ctx, integration, "url", decrypt)
    +			if err != nil {
    +				return err
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
    +func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decrypt alertingNotify.GetDecryptedValueFn) error {
    +	var encrypted string
    +	var ok bool
    +	if encrypted, ok = integration.SecureSettings[key]; !ok {
    +		return nil
    +	}
    +	decoded, err := decode(encrypted)
    +	if err != nil {
    +		return err
    +	}
    +	settings := map[string]any{}
    +	err = json.Unmarshal(integration.Settings, &settings)
    +	if err != nil {
    +		return err
    +	}
    +	currentValue, ok := settings[key]
    +	currentString := ""
    +	if ok {
    +		currentString, _ = currentValue.(string)
    +	}
    +	secretValue := decrypt(ctx, map[string][]byte{key: decoded}, key, currentString)
    +	if secretValue == currentString {
    +		return nil
    +	}
    +	settings[key] = secretValue
    +	data, err := json.Marshal(settings)
    +	if err != nil {
    +		return err
    +	}
    +	integration.Settings = data
    +	return nil
    +}
    +
     // PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
     func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
     	alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
    
  • pkg/services/ngalert/notifier/channels_config/available_channels.go+1 0 modified
    @@ -126,6 +126,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
     					Placeholder:  "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
     					PropertyName: "url",
     					Required:     true,
    +					Secure:       true,
     				},
     				{
     					Label:        "Message Type",
    
  • pkg/services/ngalert/notifier/channels_config/available_channels_test.go+1 1 modified
    @@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
     		receiverType         string
     		expectedSecretFields []string
     	}{
    -		{receiverType: "dingding", expectedSecretFields: []string{}},
    +		{receiverType: "dingding", expectedSecretFields: []string{"url"}},
     		{receiverType: "kafka", expectedSecretFields: []string{"password"}},
     		{receiverType: "email", expectedSecretFields: []string{}},
     		{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},
    
  • pkg/services/ngalert/notifier/testreceivers.go+7 2 modified
    @@ -23,12 +23,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
     				SecureSettings:        gr.SecureSettings,
     			})
     		}
    -		receivers = append(receivers, &alertingNotify.APIReceiver{
    +		recv := &alertingNotify.APIReceiver{
     			ConfigReceiver: r.Receiver,
     			GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
     				Integrations: integrations,
     			},
    -		})
    +		}
    +		err := patchNewSecureFields(ctx, recv, am.decryptFn)
    +		if err != nil {
    +			return nil, 0, err
    +		}
    +		receivers = append(receivers, recv)
     	}
     	var alert *alertingNotify.TestReceiversConfigAlertParams
     	if c.Alert != nil {
    

Vulnerability mechanics

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

References

11

News mentions

0

No linked articles in our index yet.