diff --git a/SOURCES/0017-fix-CVE-2026-42127.patch b/SOURCES/0017-fix-CVE-2026-42127.patch index 838bc9c..67588a6 100644 --- a/SOURCES/0017-fix-CVE-2026-42127.patch +++ b/SOURCES/0017-fix-CVE-2026-42127.patch @@ -1,28 +1,29 @@ -From 82ef13993059351bf21de35b8488bbd9b42df4f4 Mon Sep 17 00:00:00 2001 -From: Mariell Hoversholm -Date: Tue, 16 Jun 2026 09:54:34 +0200 -Subject: [PATCH] fix(web/bind): cap the request body size and validate - public dashboard access +From db2af7de91c43c0b94b5a0a834e99c52a5da87b1 Mon Sep 17 00:00:00 2001 +From: RHEL Packaging Agent +Date: Thu, 30 Jul 2026 09:48:07 +0000 +Subject: [PATCH] fix(web/bind): cap the request body size and validate public + dashboard access Cap request body size in Bind() with MaxBytesReader (100 MiB limit). Add specific body size limits (10 KiB) for public dashboard create, update, and query endpoints. Validate access token before processing query request body. -Backported from upstream grafana/grafana commit -82ef13993059351bf21de35b8488bbd9b42df4f4 (PR #125789), taking the -VUL-2026-0074 / VUL-2026-0077 changes only and adapting them to the -10.2.6 tree, where the public dashboards API lives in -pkg/services/publicdashboards/api and its models are not yet in a -separate package. - GL-Vuln: VUL-2026-0074 GL-Vuln: VUL-2026-0077 --- + pkg/services/publicdashboards/api/api.go | 10 ++++ + pkg/services/publicdashboards/api/query.go | 7 +++ + .../publicdashboards/api/query_test.go | 2 + + pkg/web/binding.go | 6 +- + pkg/web/binding_test.go | 58 +++++++++++++++++++ + 5 files changed, 82 insertions(+), 1 deletion(-) + diff --git a/pkg/services/publicdashboards/api/api.go b/pkg/services/publicdashboards/api/api.go +index 3713e7d8c5..4464cc8ca0 100644 --- a/pkg/services/publicdashboards/api/api.go +++ b/pkg/services/publicdashboards/api/api.go -@@ -20,6 +20,12 @@ +@@ -20,6 +20,12 @@ import ( "github.com/grafana/grafana/pkg/web" ) @@ -35,7 +36,7 @@ diff --git a/pkg/services/publicdashboards/api/api.go b/pkg/services/publicdashb type Api struct { PublicDashboardService publicdashboards.Service RouteRegister routing.RouteRegister -@@ -176,6 +182,8 @@ +@@ -176,6 +182,8 @@ func (api *Api) CreatePublicDashboard(c *contextmodel.ReqContext) response.Respo return response.Err(ErrInvalidUid.Errorf("CreatePublicDashboard: invalid Uid %s", dashboardUid)) } @@ -44,7 +45,7 @@ diff --git a/pkg/services/publicdashboards/api/api.go b/pkg/services/publicdashb pdDTO := &PublicDashboardDTO{} if err := web.Bind(c.Req, pdDTO); err != nil { return response.Err(ErrBadRequest.Errorf("CreatePublicDashboard: bad request data %v", err)) -@@ -235,6 +243,8 @@ +@@ -235,6 +243,8 @@ func (api *Api) UpdatePublicDashboard(c *contextmodel.ReqContext) response.Respo return response.Err(ErrInvalidUid.Errorf("UpdatePublicDashboard: invalid Uid %s", uid)) } @@ -54,9 +55,10 @@ diff --git a/pkg/services/publicdashboards/api/api.go b/pkg/services/publicdashb if err := web.Bind(c.Req, pdDTO); err != nil { return response.Err(ErrBadRequest.Errorf("UpdatePublicDashboard: bad request data %v", err)) diff --git a/pkg/services/publicdashboards/api/query.go b/pkg/services/publicdashboards/api/query.go +index e6dabff13d..28161c087c 100644 --- a/pkg/services/publicdashboards/api/query.go +++ b/pkg/services/publicdashboards/api/query.go -@@ -57,6 +57,13 @@ +@@ -57,6 +57,13 @@ func (api *Api) QueryPublicDashboard(c *contextmodel.ReqContext) response.Respon return response.Err(ErrInvalidAccessToken.Errorf("QueryPublicDashboard: invalid access token")) } @@ -71,9 +73,10 @@ diff --git a/pkg/services/publicdashboards/api/query.go b/pkg/services/publicdas if err != nil { return response.Err(ErrInvalidPanelId.Errorf("QueryPublicDashboard: error parsing panelId %v", err)) diff --git a/pkg/services/publicdashboards/api/query_test.go b/pkg/services/publicdashboards/api/query_test.go +index 2dcaaa7561..d1d244b67a 100644 --- a/pkg/services/publicdashboards/api/query_test.go +++ b/pkg/services/publicdashboards/api/query_test.go -@@ -98,6 +98,7 @@ +@@ -98,6 +98,7 @@ func TestAPIViewPublicDashboard(t *testing.T) { for _, test := range testCases { t.Run(test.Name, func(t *testing.T) { service := publicdashboards.NewFakePublicDashboardService(t) @@ -81,7 +84,7 @@ diff --git a/pkg/services/publicdashboards/api/query_test.go b/pkg/services/publ service.On("GetPublicDashboardForView", mock.Anything, mock.AnythingOfType("string")). Return(test.DashboardResult, test.Err).Maybe() -@@ -202,6 +203,7 @@ +@@ -202,6 +203,7 @@ func TestAPIQueryPublicDashboard(t *testing.T) { setup := func(enabled bool) (*web.Mux, *publicdashboards.FakePublicDashboardService) { service := publicdashboards.NewFakePublicDashboardService(t) @@ -90,9 +93,10 @@ diff --git a/pkg/services/publicdashboards/api/query_test.go b/pkg/services/publ testServer := setupTestServer( diff --git a/pkg/web/binding.go b/pkg/web/binding.go +index ef5d580e25..6ce059b3a7 100644 --- a/pkg/web/binding.go +++ b/pkg/web/binding.go -@@ -10,6 +10,9 @@ +@@ -10,6 +10,9 @@ import ( "reflect" ) @@ -102,7 +106,7 @@ diff --git a/pkg/web/binding.go b/pkg/web/binding.go // Bind deserializes JSON payload from the request func Bind(req *http.Request, v any) error { if req.Body != nil { -@@ -21,7 +24,8 @@ +@@ -21,7 +24,8 @@ func Bind(req *http.Request, v any) error { return errors.New("bad content type") } defer func() { _ = req.Body.Close() }() @@ -113,9 +117,10 @@ diff --git a/pkg/web/binding.go b/pkg/web/binding.go return err } diff --git a/pkg/web/binding_test.go b/pkg/web/binding_test.go +index 4c5d9550f4..f269b93cbf 100644 --- a/pkg/web/binding_test.go +++ b/pkg/web/binding_test.go -@@ -2,6 +2,10 @@ +@@ -2,6 +2,10 @@ package web import ( "errors" @@ -126,7 +131,7 @@ diff --git a/pkg/web/binding_test.go b/pkg/web/binding_test.go "testing" ) -@@ -61,6 +65,60 @@ +@@ -61,6 +65,60 @@ func (sv *StructWithPointerValidation) Validate() error { return nil } diff --git a/SOURCES/0018-fix-CVE-2026-33376.patch b/SOURCES/0018-fix-CVE-2026-33376.patch new file mode 100644 index 0000000..e0a85f0 --- /dev/null +++ b/SOURCES/0018-fix-CVE-2026-33376.patch @@ -0,0 +1,45 @@ +From 1030d08fadb0cde0cf0a70429ab7f6aff5722021 Mon Sep 17 00:00:00 2001 +From: "github-actions[bot]" +Date: Wed, 11 Mar 2026 13:59:53 +0000 +Subject: [PATCH] patch(security): patch(security): fix auth proxy IPv6 bare + whitelist parsing (main) + +GL-Vuln: VUL-2026-0045 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/77 +GL-Partner-Rel: 2026-04-09 +GL-Public-After: 2026-05-12 +GL-Partner-Ack: 2026-03-30 +--- + pkg/services/authn/clients/proxy.go | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/pkg/services/authn/clients/proxy.go b/pkg/services/authn/clients/proxy.go +index 06abe1d..80a7909 100644 +--- a/pkg/services/authn/clients/proxy.go ++++ b/pkg/services/authn/clients/proxy.go +@@ -6,7 +6,6 @@ import ( + "fmt" + "hash/fnv" + "net" +- "path" + "strconv" + "strings" + "time" +@@ -189,7 +188,17 @@ func parseAcceptList(s string) ([]*net.IPNet, error) { + func coerceProxyAddress(proxyAddr string) (*net.IPNet, error) { + proxyAddr = strings.TrimSpace(proxyAddr) + if !strings.Contains(proxyAddr, "/") { +- proxyAddr = path.Join(proxyAddr, "32") ++ ip := net.ParseIP(proxyAddr) ++ if ip == nil { ++ return nil, fmt.Errorf("could not parse the network: invalid IP address") ++ } ++ ++ mask := 32 ++ if ip.To4() == nil { ++ mask = 128 ++ } ++ ++ proxyAddr = fmt.Sprintf("%s/%d", proxyAddr, mask) + } + + _, network, err := net.ParseCIDR(proxyAddr) diff --git a/SOURCES/0019-fix-CVE-2026-33377.patch b/SOURCES/0019-fix-CVE-2026-33377.patch new file mode 100644 index 0000000..e479d8c --- /dev/null +++ b/SOURCES/0019-fix-CVE-2026-33377.patch @@ -0,0 +1,30 @@ +From 2270fa620efc17221bde881c5b6987cb6460c42e Mon Sep 17 00:00:00 2001 +From: "github-actions[bot]" +Date: Thu, 19 Mar 2026 16:54:46 +0000 +Subject: [PATCH] patch(security): [11.6.14] Dashboards: Fix /import permission + setting + +GL-Vuln: VUL-2026-0058 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/90 +GL-Partner-Rel: 2026-04-09 +GL-Public-After: 2026-05-12 +GL-Partner-Ack: 2026-03-24 +--- + pkg/services/dashboards/service/dashboard_service.go | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go +index 49ee617..913c479 100644 +--- a/pkg/services/dashboards/service/dashboard_service.go ++++ b/pkg/services/dashboards/service/dashboard_service.go +@@ -482,7 +482,10 @@ func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *dashbo + return nil, err + } + +- dr.setDefaultPermissions(ctx, dto, dash, false) ++ // new dashboard created ++ if dto.Dashboard.ID == 0 { ++ dr.setDefaultPermissions(ctx, dto, dash, false) ++ } + + return dash, nil + } diff --git a/SOURCES/0020-fix-CVE-2026-8609.patch b/SOURCES/0020-fix-CVE-2026-8609.patch new file mode 100644 index 0000000..638d27f --- /dev/null +++ b/SOURCES/0020-fix-CVE-2026-8609.patch @@ -0,0 +1,25 @@ +From 5284c45953228aeb3c13efe13f6f72f70a3e9b96 Mon Sep 17 00:00:00 2001 +From: lmchilton +Date: Fri, 14 Aug 2026 13:15:10 -0400 +Subject: [PATCH] Fix CVE-2026-8609 + +--- + pkg/services/authn/authnimpl/service.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pkg/services/authn/authnimpl/service.go b/pkg/services/authn/authnimpl/service.go +index b72fc43600a..f242d7068d9 100644 +--- a/pkg/services/authn/authnimpl/service.go ++++ b/pkg/services/authn/authnimpl/service.go +@@ -279,7 +279,7 @@ func (s *Service) Login(ctx context.Context, client string, r *authn.Request) (i + + c, ok := s.clients[client] + if !ok { +- s.metrics.failedLogin.WithLabelValues(client).Inc() ++ s.metrics.failedLogin.WithLabelValues("unknown").Inc() + return nil, authn.ErrClientNotConfigured.Errorf("client not configured: %s", client) + } + +-- +2.54.0 + diff --git a/SOURCES/0021-fix-CVE-2026-33382.patch b/SOURCES/0021-fix-CVE-2026-33382.patch new file mode 100644 index 0000000..3841278 --- /dev/null +++ b/SOURCES/0021-fix-CVE-2026-33382.patch @@ -0,0 +1,96 @@ +From 537a7969ce1198b969e623ecc866739e5bcb3175 Mon Sep 17 00:00:00 2001 +From: lmchilton +Date: Tue, 18 Aug 2026 11:37:34 -0400 +Subject: [PATCH] fix CVE-33382 + +--- + pkg/api/login.go | 3 +++ + pkg/api/org_invite.go | 2 ++ + pkg/api/password.go | 4 ++++ + pkg/api/signup.go | 7 +++++++ + 4 files changed, 16 insertions(+) + +diff --git a/pkg/api/login.go b/pkg/api/login.go +index e9e88cf3841..80884c99410 100644 +--- a/pkg/api/login.go ++++ b/pkg/api/login.go +@@ -204,6 +204,9 @@ func (hs *HTTPServer) LoginAPIPing(c *contextmodel.ReqContext) response.Response + } + + func (hs *HTTPServer) LoginPost(c *contextmodel.ReqContext) response.Response { ++ // Cap the request body-front so any downstream consumer inherits the limit. ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + identity, err := hs.authnService.Login(c.Req.Context(), authn.ClientForm, &authn.Request{HTTPRequest: c.Req, Resp: c.Resp}) + if err != nil { + tokenErr := &auth.CreateTokenErr{} +diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go +index ab4f1b944b9..437f95594ba 100644 +--- a/pkg/api/org_invite.go ++++ b/pkg/api/org_invite.go +@@ -235,6 +235,8 @@ func (hs *HTTPServer) GetInviteInfoByCode(c *contextmodel.ReqContext) response.R + } + + func (hs *HTTPServer) CompleteInvite(c *contextmodel.ReqContext) response.Response { ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + completeInvite := dtos.CompleteInviteForm{} + var err error + if err = web.Bind(c.Req, &completeInvite); err != nil { +diff --git a/pkg/api/password.go b/pkg/api/password.go +index 5567c9af3f9..0ca9c281c78 100644 +--- a/pkg/api/password.go ++++ b/pkg/api/password.go +@@ -16,6 +16,8 @@ import ( + ) + + func (hs *HTTPServer) SendResetPasswordEmail(c *contextmodel.ReqContext) response.Response { ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + form := dtos.SendResetPasswordEmailForm{} + if err := web.Bind(c.Req, &form); err != nil { + return response.Error(http.StatusBadRequest, "bad request data", err) +@@ -54,6 +56,8 @@ func (hs *HTTPServer) SendResetPasswordEmail(c *contextmodel.ReqContext) respons + } + + func (hs *HTTPServer) ResetPassword(c *contextmodel.ReqContext) response.Response { ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + form := dtos.ResetUserPasswordForm{} + if err := web.Bind(c.Req, &form); err != nil { + return response.Error(http.StatusBadRequest, "bad request data", err) +diff --git a/pkg/api/signup.go b/pkg/api/signup.go +index 0f9310d35f6..b5d20c28651 100644 +--- a/pkg/api/signup.go ++++ b/pkg/api/signup.go +@@ -19,6 +19,9 @@ import ( + "github.com/grafana/grafana/pkg/web" + ) + ++// maxPreAuthFormBodySize caps pre-auth form-shaped request bodies (signup, password reset, invite completion, login). ++const maxPreAuthFormBodySize = 100 * 1024 // 100 KiB ++ + // GET /api/user/signup/options + func (hs *HTTPServer) GetSignUpOptions(c *contextmodel.ReqContext) response.Response { + return response.JSON(http.StatusOK, util.DynMap{ +@@ -29,6 +32,8 @@ func (hs *HTTPServer) GetSignUpOptions(c *contextmodel.ReqContext) response.Resp + + // POST /api/user/signup + func (hs *HTTPServer) SignUp(c *contextmodel.ReqContext) response.Response { ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + form := dtos.SignUpForm{} + var err error + if err = web.Bind(c.Req, &form); err != nil { +@@ -82,6 +87,8 @@ func (hs *HTTPServer) SignUp(c *contextmodel.ReqContext) response.Response { + } + + func (hs *HTTPServer) SignUpStep2(c *contextmodel.ReqContext) response.Response { ++ c.Req.Body = http.MaxBytesReader(c.Resp, c.Req.Body, maxPreAuthFormBodySize) ++ + form := dtos.SignUpStep2Form{} + if err := web.Bind(c.Req, &form); err != nil { + return response.Error(http.StatusBadRequest, "bad request data", err) +-- +2.54.0 + diff --git a/SPECS/grafana.spec b/SPECS/grafana.spec index 45796ce..8d955a6 100644 --- a/SPECS/grafana.spec +++ b/SPECS/grafana.spec @@ -18,7 +18,7 @@ Name: grafana Version: 10.2.6 -Release: 23%{?dist}.1 +Release: 23%{?dist}.2 Summary: Metrics dashboard and graph editor License: AGPL-3.0-only URL: https://grafana.org @@ -78,6 +78,10 @@ Patch15: 0015-Fix-CVE-2026-27877.patch Patch16: 0016-fix-x-net-CVE.patch # https://github.com/grafana/grafana/commit/82ef13993059351bf21de35b8488bbd9b42df4f4 Patch17: 0017-fix-CVE-2026-42127.patch +Patch18: 0018-fix-CVE-2026-33376.patch +Patch19: 0019-fix-CVE-2026-33377.patch +Patch20: 0020-fix-CVE-2026-8609.patch +Patch21: 0021-fix-CVE-2026-33382.patch # Patches affecting the vendor tarball Patch1001: 1001-vendor-patch-removed-backend-crypto.patch @@ -778,6 +782,10 @@ rm -r plugins-bundled %patch -P 15 -p1 %patch -P 16 -p1 %patch -P 17 -p1 +%patch -P 18 -p1 +%patch -P 19 -p1 +%patch -P 20 -p1 +%patch -P 21 -p1 %patch -P 1001 -p1 %if %{enable_fips_mode} @@ -1024,10 +1032,15 @@ done %ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/*/active/modules/200/grafana %changelog -* Wed Aug 12 2026 Andrew Lukoshko - 10.2.6-23.1 -- fix(web/bind): cap the request body size and validate public dashboard access - Backport of upstream grafana/grafana commit 82ef1399 (PR #125789), - VUL-2026-0074 / VUL-2026-0077. Fixes CVE-2026-42127. +* Aug 20 2026 Lauren Chilton - 10.2.6-23.2 +- Resolves RHEL-210991: CVE-2026-33376 +- Resolves RHEL-211022: CVE-2026-33377 +- Resolves RHEL-242865: CVE-2026-8609 +- Resolves RHEL-242866: CVE-2026-33382 + +* Thu Jul 30 2026 RHEL Packaging Agent - 10.2.6-23.1 +- fix CVE-2026-42127: cap request body size and validate public dashboard access +- Resolves RHEL-219382 * Thu Jul 02 2026 Sam Feifer 10.2.6-23 - Resolves RHEL-183803: CVE-2026-39821