From 327393517041139946b96d03c90252e6f5fe4063 Mon Sep 17 00:00:00 2001 From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:57:37 +0200 Subject: [PATCH 1/3] remote: validate snappy decoded length before allocation in read endpoint Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> --- storage/remote/codec.go | 8 ++++++++ storage/remote/codec_test.go | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/storage/remote/codec.go b/storage/remote/codec.go index c689a51164..dec07dd6bd 100644 --- a/storage/remote/codec.go +++ b/storage/remote/codec.go @@ -67,6 +67,14 @@ func DecodeReadRequest(r *http.Request) (*prompb.ReadRequest, error) { return nil, err } + decodedLen, err := snappy.DecodedLen(compressed) + if err != nil { + return nil, err + } + if decodedLen > decodeReadLimit { + return nil, fmt.Errorf("snappy: decoded length %d exceeds limit %d", decodedLen, decodeReadLimit) + } + reqBuf, err := snappy.Decode(nil, compressed) if err != nil { return nil, err diff --git a/storage/remote/codec_test.go b/storage/remote/codec_test.go index 5da8c8176c..e0f2fe0321 100644 --- a/storage/remote/codec_test.go +++ b/storage/remote/codec_test.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "io" + "net/http" "sync" "testing" @@ -729,6 +730,17 @@ func TestMergeLabels(t *testing.T) { } } +func TestDecodeReadRequestTooLarge(t *testing.T) { + // 5-byte snappy stream whose header claims 256 MiB decoded length, + // well above decodeReadLimit (32 MiB). + bomb := []byte{0x80, 0x80, 0x80, 0x80, 0x01} + req, err := http.NewRequest(http.MethodPost, "/", bytes.NewReader(bomb)) + require.NoError(t, err) + + _, err = DecodeReadRequest(req) + require.ErrorContains(t, err, "exceeds limit") +} + func TestDecodeWriteRequest(t *testing.T) { buf, _, _, err := buildWriteRequest(nil, writeRequestFixture.Timeseries, nil, nil, nil, nil, "snappy") require.NoError(t, err) From 38f23b9075ced1de2b82d2dad8b2bebb1ecd5b7d Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 22 Apr 2026 17:31:29 +0200 Subject: [PATCH 2/3] ui: fix stored XSS in old UI heatmap chart tick labels This fixes the stored XSS as described in: https://github.com/prometheus/prometheus/security/advisories/GHSA-fw8g-cg8f-9j28 Signed-off-by: Julius Volz Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> --- web/ui/react-app/src/pages/graph/Graph.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/ui/react-app/src/pages/graph/Graph.tsx b/web/ui/react-app/src/pages/graph/Graph.tsx index 332b3f4762..4422790cb9 100644 --- a/web/ui/react-app/src/pages/graph/Graph.tsx +++ b/web/ui/react-app/src/pages/graph/Graph.tsx @@ -10,6 +10,7 @@ import { Button } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { GraphDisplayMode } from './Panel'; +import { escapeHTML } from '../../utils'; require('../../vendor/flot/jquery.flot'); require('../../vendor/flot/jquery.flot.stack'); @@ -151,7 +152,7 @@ class Graph extends PureComponent { if (options.yaxis && isHeatmap) { options.yaxis.ticks = () => new Array(data.length + 1).fill(0).map((_el, i) => i); - options.yaxis.tickFormatter = (val) => `${val ? data[val - 1].labels.le : ''}`; + options.yaxis.tickFormatter = (val) => `${val ? escapeHTML(data[val - 1].labels.le) : ''}`; options.yaxis.min = 0; options.yaxis.max = data.length; options.series.lines = { show: false }; From 5ba354575305e087c09a647e8a62e38be01d17c5 Mon Sep 17 00:00:00 2001 From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:48:10 +0200 Subject: [PATCH 3/3] Release 3.11.3 Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> --- CHANGELOG.md | 13 +++++++++++++ VERSION | 2 +- web/ui/mantine-ui/package.json | 4 ++-- web/ui/module/codemirror-promql/package.json | 4 ++-- web/ui/module/lezer-promql/package.json | 2 +- web/ui/package-lock.json | 14 +++++++------- web/ui/package.json | 2 +- 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97cd81d170..821c133eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 3.11.3 / 2026-04-27 + +This release fixes mutiple security issues. + +We would like to thank the following people for the responsible disclosures: +- Shadowbyte (4c1dr3aper) - Charlie Lewis for the Remote-Read snappy decode vulnerability. +- Brett Gervasoni for the AzureAD OAuth `client_secret` vulnerability. +- @iiihaiii and @Ngocnn97 for the Old UI XSS vulnerability. + +- [SECURITY] AzureAD remote write: Fix OAuth `client_secret` being exposed in plaintext via `/-/config` endpoint. GHSA-wg65-39gg-5wfj / CVE-2026-42151 #18590 +- [SECURITY] Remote-read: Reject snappy-compressed requests whose declared decoded length exceeds the decode limit. GHSA-8rm2-7qqf-34qm / CVE-2026-42154 #18584 +- [SECURITY] UI: Fix stored XSS via unescaped `le` label values in old UI heatmap chart tick labels. GHSA-fw8g-cg8f-9j28 #18588 + ## 3.11.2 / 2026-04-13 This release has a fix for a Stored XSS vulnerability that can be triggered via crafted metric names and label values in Prometheus web UI tooltips and metrics explorer. Thanks to Duc Anh Nguyen from TinyxLab for reporting it. diff --git a/VERSION b/VERSION index 1e33456831..d2c96c0ab8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.11.2 +3.11.3 diff --git a/web/ui/mantine-ui/package.json b/web/ui/mantine-ui/package.json index 6af3419aca..cae1dc41f0 100644 --- a/web/ui/mantine-ui/package.json +++ b/web/ui/mantine-ui/package.json @@ -1,7 +1,7 @@ { "name": "@prometheus-io/mantine-ui", "private": true, - "version": "0.311.2", + "version": "0.311.3", "type": "module", "scripts": { "start": "vite", @@ -28,7 +28,7 @@ "@microsoft/fetch-event-source": "^2.0.1", "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", - "@prometheus-io/codemirror-promql": "0.311.2", + "@prometheus-io/codemirror-promql": "0.311.3", "@reduxjs/toolkit": "^2.11.2", "@tabler/icons-react": "^3.40.0", "@tanstack/react-query": "^5.95.2", diff --git a/web/ui/module/codemirror-promql/package.json b/web/ui/module/codemirror-promql/package.json index 8f12a5c31e..32b9e7fd2d 100644 --- a/web/ui/module/codemirror-promql/package.json +++ b/web/ui/module/codemirror-promql/package.json @@ -1,6 +1,6 @@ { "name": "@prometheus-io/codemirror-promql", - "version": "0.311.2", + "version": "0.311.3", "description": "a CodeMirror mode for the PromQL language", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/prometheus/prometheus/blob/main/web/ui/module/codemirror-promql/README.md", "dependencies": { - "@prometheus-io/lezer-promql": "0.311.2", + "@prometheus-io/lezer-promql": "0.311.3", "lru-cache": "^11.2.7" }, "devDependencies": { diff --git a/web/ui/module/lezer-promql/package.json b/web/ui/module/lezer-promql/package.json index 23818f2bba..d3f93ff577 100644 --- a/web/ui/module/lezer-promql/package.json +++ b/web/ui/module/lezer-promql/package.json @@ -1,6 +1,6 @@ { "name": "@prometheus-io/lezer-promql", - "version": "0.311.2", + "version": "0.311.3", "description": "lezer-based PromQL grammar", "main": "dist/index.cjs", "type": "module", diff --git a/web/ui/package-lock.json b/web/ui/package-lock.json index d171bc249f..2746578217 100644 --- a/web/ui/package-lock.json +++ b/web/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "prometheus-io", - "version": "0.311.2", + "version": "0.311.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prometheus-io", - "version": "0.311.2", + "version": "0.311.3", "workspaces": [ "mantine-ui", "module/*" @@ -24,7 +24,7 @@ }, "mantine-ui": { "name": "@prometheus-io/mantine-ui", - "version": "0.311.2", + "version": "0.311.3", "dependencies": { "@codemirror/autocomplete": "^6.20.1", "@codemirror/language": "^6.12.3", @@ -42,7 +42,7 @@ "@microsoft/fetch-event-source": "^2.0.1", "@nexucis/fuzzy": "^0.5.1", "@nexucis/kvsearch": "^0.9.1", - "@prometheus-io/codemirror-promql": "0.311.2", + "@prometheus-io/codemirror-promql": "0.311.3", "@reduxjs/toolkit": "^2.11.2", "@tabler/icons-react": "^3.40.0", "@tanstack/react-query": "^5.95.2", @@ -172,10 +172,10 @@ }, "module/codemirror-promql": { "name": "@prometheus-io/codemirror-promql", - "version": "0.311.2", + "version": "0.311.3", "license": "Apache-2.0", "dependencies": { - "@prometheus-io/lezer-promql": "0.311.2", + "@prometheus-io/lezer-promql": "0.311.3", "lru-cache": "^11.2.7" }, "devDependencies": { @@ -205,7 +205,7 @@ }, "module/lezer-promql": { "name": "@prometheus-io/lezer-promql", - "version": "0.311.2", + "version": "0.311.3", "license": "Apache-2.0", "devDependencies": { "@lezer/generator": "^1.8.0", diff --git a/web/ui/package.json b/web/ui/package.json index cad35c9697..2dbe4cf049 100644 --- a/web/ui/package.json +++ b/web/ui/package.json @@ -1,7 +1,7 @@ { "name": "prometheus-io", "description": "Monorepo for the Prometheus UI", - "version": "0.311.2", + "version": "0.311.3", "private": true, "scripts": { "build": "bash build_ui.sh --all",