XSS in Grafana Explore stack trace
Description
Stack traces in Grafana's Explore Traces view can be rendered as raw HTML, and thus inject malicious JavaScript in the browser. This would require malicious JavaScript to be entered into the stack trace field.
Only datasources with the Jaeger HTTP API appear to be affected; Jaeger gRPC and Tempo do not appear affected whatsoever.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2025-41117 is a stored cross-site scripting vulnerability in Grafana's Explore Traces view, where stack traces from Jaeger HTTP API datasources are rendered as raw HTML without sanitization.
Vulnerability
Overview CVE-2025-41117 is a stored cross-site scripting (XSS) vulnerability in Grafana's Explore Traces view. The root cause is that stack trace data retrieved from datasources using the Jaeger HTTP API is rendered as raw HTML in the TraceView component, without proper sanitization of user-controlled values [1]. The vulnerability exists because the KeyValuesTable component inserted stack trace content directly into the DOM using dangerouslySetInnerHTML without first sanitizing the input, as shown in the GitHub commit patches [3][4].
Exploitation
Prerequisites To exploit this vulnerability, an attacker must first introduce malicious JavaScript into a stack trace field that is stored and later retrieved by a Jaeger HTTP API datasource. This could happen if a user or automated process ingests a trace containing a maliciously crafted stack trace into Jaeger gRPC and Tempo datasources are not affected, as they do not expose the same rendering path [1]. The attack does not require authentication to Grafana authentication if the data source itself accepts unauthenticated writes, but an authenticated user must view the affected trace in the Explore Traces Explore view for the payload to execute.
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's browser session. This could lead to session hijacking, exfiltration of dashboard data, or other malicious actions within the Grafana instance. Since the payload is stored in trace data, it affects any user who views the malicious trace in the Explore Traces interface.
Mitigation
Status Grafana has addressed the vulnerability by introducing HTML sanitization using DOMPurify in the KeyValuesTable component. The fix is included in versions 12.2.5 and 12.3.3, as evidenced by the referenced commits [3][4]. Users should upgrade to these versions or later to eliminate the risk. No workaround is available besides upgrading or avoiding usage of Jaeger HTTP API datasources.
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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/grafana/grafanaGo | >= 12.2.0, < 12.2.5 | 12.2.5 |
github.com/grafana/grafanaGo | >= 12.3.0, < 12.3.3 | 12.3.3 |
Affected products
3- Grafana/grafana/grafanav5Range: 12.2.0
- Grafana/grafana/grafana-enterprisev5Range: 12.2.0
Patches
38dfa64469428Security: Sanitize TraceView html (#117853)
1 file changed · +8 −11
public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx+8 −11 modified@@ -14,6 +14,7 @@ import { css } from '@emotion/css'; import cx from 'classnames'; +import DOMPurify from 'dompurify'; import { PropsWithChildren } from 'react'; import { GrafanaTheme2, PluginExtensionLink, TraceKeyValuePair } from '@grafana/data'; @@ -126,22 +127,18 @@ export default function KeyValuesTable(props: KeyValuesTableProps) { <table className={styles.table}> <tbody className={styles.body}> {data.map((row, i) => { - let markup = { __html: '' }; + let html = ''; if (row.type === 'code') { - markup = { - __html: `<pre style="border: none; background: none">${row.value}</pre>`, - }; + html = `<pre style="border: none; background: none">${row.value}</pre>`; } else if (row.type === 'text') { - markup = { - __html: `<span style="white-space: pre-wrap;">${row.value}</span>`, - }; + html = `<span style="white-space: pre-wrap;">${row.value}</span>`; } else { - markup = { - __html: jsonMarkup(parseIfComplexJson(row.value)), - }; + html = jsonMarkup(parseIfComplexJson(row.value)); } - const jsonTable = <div className={styles.jsonTable} dangerouslySetInnerHTML={markup} />; + const jsonTable = ( + <div className={styles.jsonTable} dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} /> + ); const links = linksGetter?.(data, i); let valueMarkup; if (links && links.length) {
ecff0d88680c[release-12.2.5] Security(TraceView): Sanitize html (#117867)
1 file changed · +8 −11
public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx+8 −11 modified@@ -14,6 +14,7 @@ import { css } from '@emotion/css'; import cx from 'classnames'; +import DOMPurify from 'dompurify'; import { PropsWithChildren } from 'react'; import { GrafanaTheme2, PluginExtensionLink, TraceKeyValuePair } from '@grafana/data'; @@ -128,22 +129,18 @@ export default function KeyValuesTable(props: KeyValuesTableProps) { <table className={styles.table}> <tbody className={styles.body}> {data.map((row, i) => { - let markup = { __html: '' }; + let html = ''; if (row.type === 'code') { - markup = { - __html: `<pre style="border: none; background: none">${row.value}</pre>`, - }; + html = `<pre style="border: none; background: none">${row.value}</pre>`; } else if (row.type === 'text') { - markup = { - __html: `<span style="white-space: pre-wrap;">${row.value}</span>`, - }; + html = `<span style="white-space: pre-wrap;">${row.value}</span>`; } else { - markup = { - __html: jsonMarkup(parseIfComplexJson(row.value)), - }; + html = jsonMarkup(parseIfComplexJson(row.value)); } - const jsonTable = <div className={styles.jsonTable} dangerouslySetInnerHTML={markup} />; + const jsonTable = ( + <div className={styles.jsonTable} dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} /> + ); const links = linksGetter?.(data, i); let valueMarkup; if (links && links.length) {
4f624a5a0140[release-12.3.3] Security(TraceView): Sanitize html (#117866)
1 file changed · +8 −11
public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx+8 −11 modified@@ -14,6 +14,7 @@ import { css } from '@emotion/css'; import cx from 'classnames'; +import DOMPurify from 'dompurify'; import { PropsWithChildren } from 'react'; import { GrafanaTheme2, PluginExtensionLink, TraceKeyValuePair } from '@grafana/data'; @@ -128,22 +129,18 @@ export default function KeyValuesTable(props: KeyValuesTableProps) { <table className={styles.table}> <tbody className={styles.body}> {data.map((row, i) => { - let markup = { __html: '' }; + let html = ''; if (row.type === 'code') { - markup = { - __html: `<pre style="border: none; background: none">${row.value}</pre>`, - }; + html = `<pre style="border: none; background: none">${row.value}</pre>`; } else if (row.type === 'text') { - markup = { - __html: `<span style="white-space: pre-wrap;">${row.value}</span>`, - }; + html = `<span style="white-space: pre-wrap;">${row.value}</span>`; } else { - markup = { - __html: jsonMarkup(parseIfComplexJson(row.value)), - }; + html = jsonMarkup(parseIfComplexJson(row.value)); } - const jsonTable = <div className={styles.jsonTable} dangerouslySetInnerHTML={markup} />; + const jsonTable = ( + <div className={styles.jsonTable} dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} /> + ); const links = linksGetter?.(data, i); let valueMarkup; if (links && links.length) {
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
7- github.com/advisories/GHSA-cqp7-wf4c-3xgcghsaADVISORY
- grafana.com/security/security-advisories/cve-2025-41117mitrevendor-advisory
- nvd.nist.gov/vuln/detail/CVE-2025-41117ghsaADVISORY
- github.com/grafana/grafana/commit/4f624a5a01404da45d60063ae1ee2f184818cd42ghsaWEB
- github.com/grafana/grafana/commit/8dfa6446942873d76cd94c63a2d6b71a25e880daghsaWEB
- github.com/grafana/grafana/commit/ecff0d88680cea4ad32709cb3b94b790a7f58d25ghsaWEB
- grafana.com/security/security-advisories/CVE-2025-41117ghsaWEB
News mentions
0No linked articles in our index yet.