Medium severity5.0OSV Advisory· Published Aug 4, 2025· Updated Apr 15, 2026
CVE-2025-8341
CVE-2025-8341
Description
Grafana is an open-source platform for monitoring and observability. The Infinity datasource plugin, maintained by Grafana Labs, allows visualizing data from JSON, CSV, XML, GraphQL, and HTML endpoints.
If the plugin was configured to allow only certain URLs, an attacker could bypass this restriction using a specially crafted URL. This vulnerability is fixed in version 3.4.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/grafana/grafana-infinity-datasourceGo | < 1.4.2-0.20250731100004-9c736aa21b3a | 1.4.2-0.20250731100004-9c736aa21b3a |
Affected products
1- Range: 0.0.0-x, v0.6.0, v0.6.0-alpha1, …
Patches
19c736aa21b3aChore: prefix https to the non-localhost URLs (#1293)
9 files changed · +533 −51
CHANGELOG.md+6 −0 modified@@ -1,5 +1,11 @@ # Change Log +## 3.4.1 + +⚙️ **Chore**: If the URL is specified without `http://` / `https://` scheme, infinity will now default to `https://` for non localhost URLs. + +⚙️ **Chore**: Added more tests + ## 3.4.0 🚀 **OAuth2 token customization**: Added support for OAuth2 token customization. To learn more, refer the [documentation](https://grafana.com/docs/plugins/yesoreyeram-infinity-datasource/latest/setup/oauth2-token-customization/)
cspell.config.json+1 −0 modified@@ -170,6 +170,7 @@ "unixtime", "unstyled", "uplot", + "urllib", "urlquery", "Userinfo", "vercel",
package.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "grafana-infinity-datasource", - "version": "3.4.0", + "version": "3.4.1", "description": "JSON, CSV, XML, GraphQL, HTML and REST API datasource for Grafana. Do infinite things with Grafana. Transform data with UQL/GROQ. Visualize data from many apis, RSS/ATOM feeds directly", "keywords": [ "grafana",
pkg/infinity/client.go+34 −4 modified@@ -234,19 +234,49 @@ func CanParseAsJSON(queryType models.QueryType, responseHeaders http.Header) boo return false } -func CanAllowURL(url string, allowedHosts []string) bool { +func CanAllowURL(urlString string, allowedUrls []string) bool { allow := false - if len(allowedHosts) == 0 { + if len(allowedUrls) == 0 { return true } - for _, host := range allowedHosts { - if strings.HasPrefix(url, host) { + allowedHosts, err := GetAllowedHosts(allowedUrls) + if err != nil { + backend.Logger.Debug("error parsing allowed list URLs", "err", err.Error()) + return false + } + for _, allowedUrl := range allowedUrls { + // Legacy Check : Make sure the URL matches the pattern / allowed list prefix + // This is to ensure only certain paths are allowed. Example: Should allow foo.com/a and block foo.com/b + matchesURL := strings.HasPrefix(urlString, allowedUrl) + // Updated Check : Make sure the host name matches + fullUrlString := models.FixMissingURLSchema(urlString) + parsedURL, err := url.Parse(fullUrlString) + if err != nil { + backend.Logger.Debug("error parsing the URL", "err", err.Error()) + return false + } + parsedURLHostName := parsedURL.Hostname() + matchesHostName := allowedHosts[parsedURLHostName] + if matchesURL && matchesHostName { return true } } return allow } +func GetAllowedHosts(allowedUrls []string) (map[string]bool, error) { + allowedHosts := map[string]bool{} + for _, allowedUrl := range allowedUrls { + fullAllowedUrl := models.FixMissingURLSchema(allowedUrl) + parsedURL, err := url.Parse(fullAllowedUrl) + if err != nil { + return allowedHosts, err + } + allowedHosts[parsedURL.Hostname()] = true + } + return allowedHosts, nil +} + func GetQueryBody(ctx context.Context, query models.Query) io.Reader { logger := backend.Logger.FromContext(ctx) var body io.Reader
pkg/infinity/client_test.go+254 −34 modified@@ -118,42 +118,262 @@ func TestGetQueryBody(t *testing.T) { } func TestCanAllowURL(t *testing.T) { - tests := []struct { - name string - url string - allowedHosts []string - want bool - }{ - { - url: "https://foo.com", - want: true, - }, - { - url: "https://foo.com", - allowedHosts: []string{"https://foo.com"}, - want: true, - }, - { - url: "https://bar.com", - allowedHosts: []string{"https://foo.com"}, - want: false, - }, - { - name: "should match only case sensitive URL", - url: "https://FOO.com", - allowedHosts: []string{"https://foo.com"}, - want: false, - }, - { - url: "https://bar.com/", - allowedHosts: []string{"https://foo.com/", "https://bar.com/", "https://baz.com/"}, - want: true, - }, + type testItem = struct { + name string + url string + allowedUrls []string + allow bool + } + tests := []testItem{ + {name: "should allow if no allowed urls found by default", url: "https://foo.com", allow: true}, + {name: "should allow if url matches url in allowed list", url: "https://foo.com", allowedUrls: []string{"https://foo.com"}, allow: true}, + {name: "should allow if url matches one of the url in the allowed list", url: "https://foo.com/", allowedUrls: []string{"https://bar.com/", "https://foo.com/", "https://baz.com/"}, allow: true}, + {name: "should allow localhost", url: "localhost:3000", allowedUrls: []string{"localhost"}, allow: true}, + {name: "should not allow if the hostname doesn't match", url: "https://bar.com", allowedUrls: []string{"https://foo.com"}}, + {name: "should not allow if the case of the URL doesn't match", url: "https://FOO.com", allowedUrls: []string{"https://foo.com"}}, + {name: "should now allow if the path is not matching in the allowed list", url: "https://foo.com/b", allowedUrls: []string{"https://foo.com/a"}}, + {name: "should now allow if localhost port doesn't match", url: "localhost:3000", allowedUrls: []string{"localhost:8080"}}, + } + // patterns found from https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet + malciousUrls := []string{ + "https://\\bar.com/", + "https://foo.com &%40bar.com# %40bar.com/", + "https://foo.com;.bar.com/", + "https://foo.com:%40bar.com/", + "https://foo.com:443:\\%40%40bar.com/", + "https://foo.com:443\\%40bar.com/", + "https://foo.com:anything%40bar.com/", + "https://foo.com.%5F.bar.com/", + "https://foo.com.-.bar.com/", + "https://foo.com.%2C.bar.com/", + "https://foo.com.;.bar.com/", + "https://foo.com.%21.bar.com/", + "https://foo.com.%27.bar.com/", + `https://foo.com.".bar.com/`, + "https://foo.com.%28.bar.com/", + "https://foo.com.%29.bar.com/", + "https://foo.com.{.bar.com/", + "https://foo.com.}.bar.com/", + "https://foo.com.*.bar.com/", + "https://foo.com.&.bar.com/", + "https://foo.com.`.bar.com/", + "https://foo.com.+.bar.com/", + "https://foo.com.bar.com/", + "https://foo.com.=.bar.com/", + "https://foo.com.%7E.bar.com/", + "https://foo.com.%24.bar.com/", + "https://foo.com%5B%40bar.com/", + "https://foo.com%40bar.com/", + "https://foo.com\\;%40bar.com/", + "https://foo.com&anything%40bar.com/", + "https://foo.com%2523bar.com/", + "https://foo.combar.com/", + "https://bar.com%09foo.com/", + "https://bar.com%0Afoo.com/", + "https://bar.com%0D%0Afoo.com/", + "https://bar.com%0Dfoo.com/", + "https://bar.com%E2%80%A8foo.com/", + "https://bar.com%E2%80%A9foo.com/", + "https://bar.com %40foo.com/", + "https://bar.com &%40foo.com/", + "https://bar.com foo.com/", + "https://bar.com;https://foo.com/", + "https://bar.com:\\%40%40foo.com/", + "0://bar.com:80;http://foo.com:80/", + "https://bar.com?foo.com/", + "https://bar.com?%00foo.com/", + "https://bar.com?=.foo.com/", + "https://bar.com?=foo.com/", + "https://bar.com?http://foo.com/", + "https://bar.com?https://foo.com/", + "https://bar.com../", + "http://bar.com.foo.com/", + "https://bar.com.foo.com/", + "https://bar.com%EF%BC%8Efoo.com/", + "https://bar.com%40%40foo.com/", + "https://bar.com%40foo.com/", + "https://bar.com/?d=foo.com/", + "https://bar.com/.foo.com/", + "https://bar.com///foo.com/", + "https://bar.com/foo.com/", + "https://bar.com\\.foo.com/", + "https://bar.com\\%40%40foo.com/", + "https://bar.com\foo.com/", + "https://bar.com\anything%40foo.com/", + "https://bar.com%EF%BC%86foo.com/", + "https://bar.com%EF%B9%A0foo.com/", + "https://bar.com#%40foo.com/", + `https://bar.com#\%40foo.com/`, + "https://bar.com#foo.com/", + "https://bar.com#%00foo.com/", + "https://bar.com%250d%250a%40foo.com/", + "https://bar.com%2523%40foo.com/", + "https://bar.com%252e%40foo.com/", + "https://bar.com%252f%40foo.com/", + "https://bar.com%253a443.foo.com/", + "https://bar.com%25fffoo.com/", + "https://bar.com+%40foo.com/", + "https://bar.com+&%40foo.com/", + "https://bar.com%00foo.com/", + "http://anythingfoo.com/", + "https://anythingfoo.com/", + "https://foo%40bar.com %40foo.com/", + "https://foo%40bar.com:443%40foo.com/", + "http://localhost.bar.com/", + "https://localhost.bar.com/", + "http://sfoo.com/", + "https://%09bar.com/", + "https://%0Abar.com/", + "%0D%0A//bar.com", + "%0D%0A\\bar.com", + "%40bar.com", + "http:%40bar.com", + "https:%40bar.com", + "///bar.com", + "//bar.com", + "/\bar.com", + "/\/bar.com", + "/
/bar.com", + "///bar.com", + "/	/bar.com", + "\\%09\bar.com", + "\\%0A\bar.com", + "\\/\\/bar.com", + "\\/bar.com", + "http:\\bar.com\\", + "#bar.com", + "http:bar.com", + "https:bar.com", + "%00http://bar.com", + "%01http://bar.com", + "%02http://bar.com", + "%03http://bar.com", + "%04http://bar.com", + "%05http://bar.com", + "%06http://bar.com", + "%07http://bar.com", + "%08http://bar.com", + "%09http://bar.com", + "%0Ahttp://bar.com", + "%0Bhttp://bar.com", + "%0Chttp://bar.com", + "%0Dhttp://bar.com", + "%0Ehttp://bar.com", + "%0Fhttp://bar.com", + "%10http://bar.com", + "%11http://bar.com", + "%12http://bar.com", + "%13http://bar.com", + "%14http://bar.com", + "%15http://bar.com", + "%16http://bar.com", + "%17http://bar.com", + "%18http://bar.com", + "%19http://bar.com", + "%1Ahttp://bar.com", + "%1Bhttp://bar.com", + "%1Chttp://bar.com", + "%1Dhttp://bar.com", + "%1Ehttp://bar.com", + "%1Fhttp://bar.com", + " http://bar.com", + "h%09ttp://bar.com", + "h%0Attp://bar.com", + "h%0Dttp://bar.com", + "http%09://bar.com", + "http%0A://bar.com", + "http%0D://bar.com", + "%09http%09://bar.com", + "%0Ahttp%0A://bar.com", + "%0Dhttp%0D://bar.com", + "http:/\bar.com", + "http:/\\bar.com", + "http:\\bar.com", + "http:\bar.com", + "http:/bar.com", + "http:/0/bar.com", + "https://%E2%80%8Bbar.com/", + "https://%E2%81%A0bar.com/", + "https://%C2%ADbar.com/", + "https://%5B::%5D/", + "https://%5B::1%5D/", + "https://%5B::ffff:0.0.0.0%5D/", + "https://%5B::ffff:0000:0000%5D/", + "https://%5B::ffff:7f00:1%5D/", + "https://%5B::%EF%AC%80%EF%AC%80:7f00:1%5D/", + "https://%5B0:0:0:0:0:ffff:127.0.0.1%5D/", + "https://%5B0:0:0:0:0:ffff:1%E3%89%97.0.0.1%5D/", + "https://%5B0:0:0:0:0:ffff:%E2%91%AB7.0.0.1%5D/", + "https://%5B0:0:0:0:0:%EF%AC%80%EF%AC%80:127.0.0.1%5D/", + "https://%5B0000::1%5D/", + "https://%5B0000:0000:0000:0000:0000:0000:0000:0000%5D/", + "https://%5B0000:0000:0000:0000:0000:0000:0000:0001%5D/", + "https://%400/", + "https://\\l\\o\\c\\a\\l\\h\\o\\s\\t/", + "https://foo.com.local/", + "https://foo.com.localhost/", + "https://0/", + "0:80", + "https://0.0.0.0/", + "https://0000.0000.0000.0000/", + "https://00000177.00000000.00000000.00000001/", + "https://0177.0000.0000.0001/", + "https://017700000001/", + "https://0%E2%91%B0700000001/", + "https://0x00000000/", + "https://0x100000000/", + "https://0x17f000001/", + "https://0x17f000002/", + "https://0x7F.0.0000.00000001/", + "https://0x7F.0.0000.0001/", + "https://0x7f.0x00.0x00.0x01/", + "https://0x7f.0x00.0x00.0x02/", + "https://0x7F.1/", + "https://0x7f000001/", + "https://0x7f000002/", + "https://127.0.0.1/", + "https://1%E3%89%97.0.0.1/", + "https://%E2%91%AB7.0.0.1/", + "https://127.0.0.2/", + "https://1%E3%89%97.0.0.2/", + "https://%E2%91%AB7.0.0.2/", + "https://127.000000000000000.1/", + "https://127.1/", + "https://2130706433/", + "https://21307064%E3%89%9D/", + "https://2130706%E3%8A%B83/", + "https://21%E3%89%9A706433/", + "https://2%E2%91%AC0706433/", + "https://%E3%89%9130706433/", + "https://%E3%89%91%E3%89%9A%E2%91%A6%E2%93%AA%E2%91%A5%E2%91%A3%E3%89%9D/", + "https://45080379393/", + "https://localhost/", + "https://%C2%ADlocalhost/", + "https://%CD%8Flocalhost/", + "https://%E1%A0%8Blocalhost/", + "https://%E1%A0%8Clocalhost/", + "https://%E1%A0%8Dlocalhost/", + "https://%E1%A0%8Elocalhost/", + "https://%E1%A0%8Flocalhost/", + "https://%E2%80%8Blocalhost/", + "https://%E2%81%A0localhost/", + "https://%E2%81%A4localhost/", + "https://localho%EF%AC%86/", + "https://lo%E3%8E%88host/", + "https://localho%EF%AC%85/", + "https://fooxcom/", + } + for _, v := range malciousUrls { + tests = append(tests, testItem{url: v, allowedUrls: []string{"https://foo.com"}}) } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := infinity.CanAllowURL(tt.url, tt.allowedHosts) - assert.Equal(t, tt.want, got) + testName := tt.name + if testName == "" { + testName = tt.url + } + t.Run(testName, func(t *testing.T) { + got := infinity.CanAllowURL(tt.url, tt.allowedUrls) + assert.Equal(t, tt.allow, got) }) } }
pkg/infinity/request.go+1 −0 modified@@ -92,6 +92,7 @@ func GetQueryURL(ctx context.Context, pCtx *backend.PluginContext, settings mode } func NormalizeURL(u string) string { + u = models.FixMissingURLSchema(u) urlArray := strings.Split(u, "/") if strings.HasPrefix(u, "https://github.com") && len(urlArray) > 5 && urlArray[5] == "blob" && urlArray[4] != "blob" && urlArray[3] != "blob" { u = strings.Replace(u, "https://github.com", "https://raw.githubusercontent.com", 1)
pkg/infinity/request_test.go+1 −1 modified@@ -144,7 +144,7 @@ func TestGetQueryURL(t *testing.T) { query: models.Query{ URL: "0.0.0.0", }, - want: "0.0.0.0", + want: "https://0.0.0.0", }, { settings: models.InfinitySettings{},
pkg/models/settings.go+59 −10 modified@@ -3,8 +3,10 @@ package models import ( "context" "encoding/json" + "errors" "fmt" "net/textproto" + urllib "net/url" "strings" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -156,19 +158,26 @@ func (s *InfinitySettings) Validate() error { } return nil } - if s.AuthenticationMethod != AuthenticationMethodAzureBlob && s.AuthenticationMethod != AuthenticationMethodNone && len(s.AllowedHosts) < 1 { + if s.DoesAllowedHostsRequired() && len(s.AllowedHosts) < 1 { return ErrInvalidConfigHostNotAllowed } - if s.HaveSecureHeaders() && len(s.AllowedHosts) < 1 { - return ErrInvalidConfigHostNotAllowed - } - if len(s.KeepCookies) > 0 && len(s.AllowedHosts) < 1 { - return ErrInvalidConfigHostNotAllowed - } - return nil + return ValidateAllowedHosts(s.AllowedHosts) } -func (s *InfinitySettings) HaveSecureHeaders() bool { +func (s *InfinitySettings) DoesAllowedHostsRequired() bool { + // If base url is configured, there is no need for allowed hosts + if strings.TrimSpace(s.URL) != "" { + return false + } + // If there is specific authentication mechanism (except none and azure blob), then allowed hosts required + if s.AuthenticationMethod != "" && s.AuthenticationMethod != AuthenticationMethodNone && s.AuthenticationMethod != AuthenticationMethodAzureBlob { + return true + } + // If there are any TLS specific settings enabled, then allowed hosts required + if s.TLSAuthWithCACert || s.TLSClientAuth { + return true + } + // If there are custom headers (not generic headers such as Accept, Content Type etc), then allowed hosts required if len(s.CustomHeaders) > 0 { for k := range s.CustomHeaders { if textproto.CanonicalMIMEHeaderKey(k) == "Accept" { @@ -179,11 +188,51 @@ func (s *InfinitySettings) HaveSecureHeaders() bool { } return true } - return false + } + // If there are custom query parameters, then allowed hosts required + if len(s.SecureQueryFields) > 0 { + return true + } + // If there are cookie forwarding, then allowed hosts required + if len(s.KeepCookies) > 0 { + return true } return false } +func ValidateAllowedHosts(allowedUrls []string) error { + for _, allowedUrl := range allowedUrls { + fullAllowedUrl := FixMissingURLSchema(allowedUrl) + parsedURL, err := urllib.Parse(fullAllowedUrl) + if err != nil { + return fmt.Errorf("error parsing allowed list url %s", allowedUrl) + } + allowedHostName := parsedURL.Hostname() + if strings.TrimSpace(allowedHostName) == "" { + return errors.New("invalid url found in allowed hosts settings") + } + protocol := strings.ToLower(parsedURL.Scheme) + if !(protocol == "http" || protocol == "https") { + return fmt.Errorf("invalid url in allowed list %s", allowedUrl) + } + } + return nil +} + +func FixMissingURLSchema(urlString string) string { + if strings.TrimSpace(urlString) == "" { + return "" + } + lowerUrlString := strings.ToLower(urlString) + if !(strings.HasPrefix(lowerUrlString, "http://") || strings.HasPrefix(lowerUrlString, "https://")) { + if lowerUrlString == "localhost" || strings.HasPrefix(lowerUrlString, "localhost:") { + return "http://" + urlString + } + return "https://" + urlString + } + return urlString +} + type RefData struct { Name string `json:"name,omitempty"` Data string `json:"data,omitempty"`
pkg/models/settings_test.go+176 −1 modified@@ -3,6 +3,7 @@ package models_test import ( "context" "errors" + "strings" "testing" "github.com/grafana/grafana-infinity-datasource/pkg/models" @@ -253,7 +254,7 @@ func TestAllSettingsAgainstFrontEnd(t *testing.T) { SecureQueryFields: map[string]string{ "foo": "bar", }, - KeepCookies: []string{"cookie1", "cookie2"}, + KeepCookies: []string{"cookie1", "cookie2"}, }, gotSettings) } @@ -406,3 +407,177 @@ func TestInfinitySettings_Validate(t *testing.T) { }) } } + +func TestValidateAllowedHosts(t *testing.T) { + tests := []struct { + name string + allowedUrls []string + wantErr bool + expectedErr string + }{ + { + name: "empty list should not error", + allowedUrls: []string{}, + }, + { + name: "nil list should not error", + allowedUrls: nil, + }, + { + name: "valid https URLs", + allowedUrls: []string{"https://api.example.com", "https://api2.example.com"}, + }, + { + name: "valid http URLs", + allowedUrls: []string{"http://api.example.com", "http://localhost:8080"}, + }, + { + name: "valid URLs with paths", + allowedUrls: []string{"https://api.example.com/v1", "https://api.example.com/v2/users"}, + }, + { + name: "valid URLs with ports", + allowedUrls: []string{"https://api.example.com:8443", "http://localhost:3000"}, + }, + { + name: "valid URLs with query parameters", + allowedUrls: []string{"https://api.example.com?version=v1", "https://api.example.com/search?q=test"}, + }, + { + name: "valid URLs with fragments", + allowedUrls: []string{"https://api.example.com#section", "https://docs.example.com/api#overview"}, + }, + { + name: "valid IP addresses", + allowedUrls: []string{"http://192.168.1.1", "https://10.0.0.1:8080"}, + }, + { + name: "valid IPv6 addresses", + allowedUrls: []string{"http://[::1]", "https://[2001:db8::1]:8080"}, + }, + { + name: "valid subdomain URLs", + allowedUrls: []string{"https://api.sub.example.com", "https://v2.api.example.com"}, + }, + { + name: "valid URLs with authentication info", + allowedUrls: []string{"https://user:pass@api.example.com", "http://admin@localhost:8080"}, + }, + { + name: "mixed valid URLs", + allowedUrls: []string{ + "https://api.example.com", + "http://localhost:8080", + "https://192.168.1.100:9000/api/v1", + "http://[::1]:3000", + }, + }, + { + name: "invalid URL - malformed", + allowedUrls: []string{"://invalid-url"}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "valid URL - missing protocol - should default to https", + allowedUrls: []string{"api.example.com"}, + wantErr: false, + }, + { + name: "invalid URL - empty string", + allowedUrls: []string{""}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "invalid URL - only protocol", + allowedUrls: []string{"https://"}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "invalid URL - whitespace only", + allowedUrls: []string{" "}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "invalid URL - protocol only with path", + allowedUrls: []string{"https:///path"}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "invalid URL - space in URL", + allowedUrls: []string{"https://api example.com"}, + wantErr: true, + expectedErr: "error parsing allowed list url", + }, + { + name: "invalid URL - invalid characters", + allowedUrls: []string{"https://api.exam ple.com"}, + wantErr: true, + expectedErr: "error parsing allowed list url", + }, + { + name: "mixed valid and invalid URLs - should fail on first invalid", + allowedUrls: []string{"https://api.example.com", "https://", "https://api2.example.com"}, + wantErr: true, + expectedErr: "invalid url found in allowed hosts settings", + }, + { + name: "invalid URL - control characters", + allowedUrls: []string{"https://api.example.com\x00"}, + wantErr: true, + expectedErr: "error parsing allowed list url", + }, + { + name: "edge case - localhost variants", + allowedUrls: []string{"http://localhost", "http://127.0.0.1", "http://0.0.0.0"}, + wantErr: false, + }, + { + name: "edge case - unusual but valid ports", + allowedUrls: []string{"https://api.example.com:65535", "http://localhost:1"}, + }, + { + name: "edge case - very long hostname", + allowedUrls: []string{"https://" + strings.Repeat("a", 253) + ".com"}, + }, + { + name: "invalid URL - percent encoding issues", + allowedUrls: []string{"https://api.example.com/%"}, + wantErr: true, + expectedErr: "error parsing allowed list url", + }, + { + name: "valid host name", + allowedUrls: []string{"localhost"}, + }, + { + name: "valid host name with port", + allowedUrls: []string{"localhost:8080"}, + }, + { + name: "hostnames such as h should be considered as hostname instead of url scheme", + allowedUrls: []string{"h"}, + }, + { + name: "hostnames such as abc should be considered as hostname instead of url scheme", + allowedUrls: []string{"abc"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := models.ValidateAllowedHosts(tt.allowedUrls) + if tt.wantErr { + require.Error(t, err, "Expected an error but got none") + if tt.expectedErr != "" { + assert.Contains(t, err.Error(), tt.expectedErr, "Error message should contain expected text") + } + } else { + require.NoError(t, err, "Expected no error but got: %v", err) + } + }) + } +}
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- github.com/advisories/GHSA-3c93-92r7-j934ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-8341ghsaADVISORY
- github.com/grafana/grafana-infinity-datasource/commit/9c736aa21b3a669d3070d3f5f80d949326fafa77ghsaWEB
- github.com/grafana/grafana-infinity-datasource/releases/tag/v3.4.1nvdWEB
- grafana.com/security/security-advisories/cve-2025-8341ghsaWEB
- grafana.com/security/security-advisories/cve-2025-8341/nvd
News mentions
0No linked articles in our index yet.