diff --git a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts
index 816ddf7578..afb09bfb4b 100644
--- a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts
+++ b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts
@@ -76,7 +76,7 @@ const formatLabels = (labels: { [key: string]: string }): string => `
.filter((k) => k !== "__name__")
.map(
(k) =>
- `
${escapeHTML(k)}: ${escapeHTML(labels[k])}
`
+ `${escapeHTML(k)}: ${escapeHTML(labels[k])}
`,
)
.join("")}
`;
@@ -153,7 +153,7 @@ const tooltipPlugin = (useLocalTime: boolean, data: AlignedData) => {
${formatTimestamp(ts, useLocalTime)}
- ${labels.__name__ ? labels.__name__ + ": " : " "}${value}
+ ${labels.__name__ ? escapeHTML(labels.__name__) + ": " : " "}${value}
${formatLabels(labels)}
`.trimEnd();
@@ -193,7 +193,7 @@ const autoPadLeft = (
u: uPlot,
values: string[],
axisIdx: number,
- cycleNum: number
+ cycleNum: number,
) => {
const axis = u.axes[axisIdx];
@@ -208,7 +208,7 @@ const autoPadLeft = (
// Find longest tick text.
const longestVal = (values ?? []).reduce(
(acc, val) => (val.length > acc.length ? val : acc),
- ""
+ "",
);
if (longestVal != "") {
@@ -228,7 +228,7 @@ const onlyDrawPointsForDisconnectedSamplesFilter = (
u: uPlot,
seriesIdx: number,
show: boolean,
- gaps?: null | number[][]
+ gaps?: null | number[][],
) => {
const filtered = [];
@@ -287,7 +287,7 @@ export const getUPlotOptions = (
useLocalTime: boolean,
yAxisMin: number | null,
light: boolean,
- onSelectRange: (_start: number, _end: number) => void
+ onSelectRange: (_start: number, _end: number) => void,
): uPlot.Options => ({
width: width - 30,
height: 550,
@@ -314,7 +314,7 @@ export const getUPlotOptions = (
markers: {
fill: (
_u: uPlot,
- seriesIdx: number
+ seriesIdx: number,
): CSSStyleDeclaration["borderColor"] =>
// Because the index here is coming from uPlot, we need to subtract 1. Series 0
// represents the X axis, so we need to skip it.
@@ -411,7 +411,7 @@ export const getUPlotOptions = (
// @ts-expect-error - uPlot doesn't have a field for labels, but we just attach some anyway.
labels: r.metric,
stroke: getSeriesColor(idx, light),
- })
+ }),
),
],
hooks: {
@@ -421,7 +421,7 @@ export const getUPlotOptions = (
const leftVal = self.posToVal(self.select.left, "x");
const rightVal = Math.max(
self.posToVal(self.select.left + self.select.width, "x"),
- leftVal + 1
+ leftVal + 1,
);
onSelectRange(leftVal, rightVal);
@@ -441,7 +441,7 @@ export const getUPlotData = (
inputData: RangeSamples[],
startTime: number,
endTime: number,
- resolution: number
+ resolution: number,
): uPlot.AlignedData => {
const timeData: number[] = [];
for (let t = startTime; t <= endTime; t += resolution) {
diff --git a/web/ui/react-app/src/pages/graph/GraphHelpers.ts b/web/ui/react-app/src/pages/graph/GraphHelpers.ts
index 21bb768f52..60b38d5ab0 100644
--- a/web/ui/react-app/src/pages/graph/GraphHelpers.ts
+++ b/web/ui/react-app/src/pages/graph/GraphHelpers.ts
@@ -118,10 +118,10 @@ export const getOptions = (stacked: boolean, useLocalTime: boolean): jquery.flot
const formatLabels = (labels: { [key: string]: string }): string => `
${Object.keys(labels).length === 0 ? '
no labels
' : ''}
- ${labels['__name__'] ? `
${labels['__name__']}
` : ''}
+ ${labels['__name__'] ? `
${escapeHTML(labels['__name__'])}
` : ''}
${Object.keys(labels)
.filter((k) => k !== '__name__')
- .map((k) => `
${k}: ${escapeHTML(labels[k])}
`)
+ .map((k) => `
${escapeHTML(k)}: ${escapeHTML(labels[k])}
`)
.join('')}
`;
@@ -129,7 +129,7 @@ export const getOptions = (stacked: boolean, useLocalTime: boolean): jquery.flot
${dateTime.format('YYYY-MM-DD HH:mm:ss Z')}
- ${labels.__name__ || 'value'}: ${yval}
+ ${labels.__name__ ? escapeHTML(labels.__name__) : 'value'}: ${yval}
${'seriesLabels' in both ? 'Trace exemplar:' : 'Series:'}
${formatLabels(labels)}
diff --git a/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx b/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx
index 1959fa8265..053c630f38 100644
--- a/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx
+++ b/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx
@@ -2,7 +2,7 @@ import React, { Component, ChangeEvent } from 'react';
import { Modal, ModalBody, ModalHeader, Input } from 'reactstrap';
import { Fuzzy, FuzzyResult } from '@nexucis/fuzzy';
-const fuz = new Fuzzy({ pre: '', post: '', shouldSort: true });
+const fuz = new Fuzzy({ pre: '', post: '', shouldSort: true, escapeHTML: true });
interface MetricsExplorerProps {
show: boolean;
diff --git a/web/ui/react-app/src/vendor/flot/jquery.flot.heatmap.js b/web/ui/react-app/src/vendor/flot/jquery.flot.heatmap.js
index 29d5c81ef7..be21821fdd 100644
--- a/web/ui/react-app/src/vendor/flot/jquery.flot.heatmap.js
+++ b/web/ui/react-app/src/vendor/flot/jquery.flot.heatmap.js
@@ -6,6 +6,7 @@ See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3384 for more deta
import moment from 'moment-timezone';
import {formatValue} from "../../pages/graph/GraphHelpers";
+import {escapeHTML} from '../../utils';
const TOOLTIP_ID = 'heatmap-tooltip';
const GRADIENT_STEPS = 16;
@@ -82,7 +83,7 @@ const GRADIENT_STEPS = 16;
tooltip.className = cssClass;
const timeHtml = `${dateTime.join('
')}
`
- const labelHtml = `Bucket: ${label || 'value'}
`
+ const labelHtml = `Bucket: ${label ? escapeHTML(label) : 'value'}
`
const valueHtml = `Value: ${value}
`
tooltip.innerHTML = `${timeHtml}
${labelHtml}${valueHtml}
`;