This is an automated email from the ASF dual-hosted git repository. michaelsmolina pushed a commit to branch 5.0 in repository https://gitbox.apache.org/repos/asf/superset.git
commit ad10abd8f24d7f8b999f4309b6f5cfa3ebe2811d Author: Mehmet Salih Yavuz <salih.ya...@proton.me> AuthorDate: Sat Jun 14 11:26:15 2025 +0300 fix(tooltip): Sanitize tooltip html (#33765) (cherry picked from commit 8fd0fd673f3307ae2f7555272d68d8511651e685) --- .../packages/superset-ui-core/src/utils/tooltip.ts | 5 +- .../superset-ui-core/test/utils/tooltip.test.ts | 55 ++++++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts b/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts index 6105af0f71..59d5b08bb5 100644 --- a/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts +++ b/superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts @@ -17,6 +17,7 @@ * under the License. */ import { t } from '../translation'; +import { sanitizeHtml } from './html'; const TRUNCATION_STYLE = ` max-width: 300px; @@ -32,7 +33,7 @@ export function tooltipHtml( const titleRow = title ? `<span style="font-weight: 700;${TRUNCATION_STYLE}">${title}</span>` : ''; - return ` + return sanitizeHtml(` <div> ${titleRow} <table> @@ -53,5 +54,5 @@ export function tooltipHtml( }) .join('')} </table> - </div>`; + </div>`); } diff --git a/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts index fbd29175ec..1941519c02 100644 --- a/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/utils/tooltip.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { tooltipHtml } from '@superset-ui/core'; +import { sanitizeHtml, tooltipHtml } from '@superset-ui/core'; const TITLE_STYLE = 'style="font-weight: 700;max-width:300px;overflow:hidden;text-overflow:ellipsis;"'; @@ -39,7 +39,8 @@ function removeWhitespaces(text: string) { test('should return a table with the given data', () => { const title = 'Title'; const html = removeWhitespaces(tooltipHtml(data, title)); - const expectedHtml = removeWhitespaces(` + const expectedHtml = removeWhitespaces( + sanitizeHtml(` <div> <span ${TITLE_STYLE}>Title</span> <table> @@ -54,7 +55,8 @@ test('should return a table with the given data', () => { <td ${TD_NUMBER_STYLE}>3</td> </tr> </table> - </div>`); + </div>`), + ); expect(html).toMatch(expectedHtml); }); @@ -62,7 +64,8 @@ test('should return a table with the given data and a focused row', () => { const title = 'Title'; const focusedRow = 1; const html = removeWhitespaces(tooltipHtml(data, title, focusedRow)); - const expectedHtml = removeWhitespaces(` + const expectedHtml = removeWhitespaces( + sanitizeHtml(` <div> <span ${TITLE_STYLE}>Title</span> <table> @@ -77,26 +80,30 @@ test('should return a table with the given data and a focused row', () => { <td ${TD_NUMBER_STYLE}>3</td> </tr> </table> - </div>`); + </div>`), + ); expect(html).toMatch(expectedHtml); }); test('should return a table with no data', () => { const title = 'Title'; const html = removeWhitespaces(tooltipHtml([], title)); - const expectedHtml = removeWhitespaces(` + const expectedHtml = removeWhitespaces( + sanitizeHtml(` <div> <span ${TITLE_STYLE}>Title</span> <table> <tr><td>No data</td></tr> </table> - </div>`); + </div>`), + ); expect(html).toMatch(expectedHtml); }); test('should return a table with the given data and no title', () => { const html = removeWhitespaces(tooltipHtml(data)); - const expectedHtml = removeWhitespaces(` + const expectedHtml = removeWhitespaces( + sanitizeHtml(` <div> <table> <tr ${TR_STYLE}> @@ -110,6 +117,36 @@ test('should return a table with the given data and no title', () => { <td ${TD_NUMBER_STYLE}>3</td> </tr> </table> - </div>`); + </div>`), + ); + expect(html).toMatch(expectedHtml); +}); + +test('should sanitize HTML input', () => { + const title = 'Title<script>alert("message");</script>'; + const data = [ + ['<b onclick="alert(\'message\')">B message</b>', 'message2'], + ['<img src="x" onerror="alert(\'message\');" />', '<i>Italic</i>'], + ]; + + const html = removeWhitespaces(tooltipHtml(data, title)); + + const expectedHtml = removeWhitespaces( + sanitizeHtml(` + <div> + <span ${TITLE_STYLE}>Titlealert("message");</span> + <table> + <tr ${TR_STYLE}> + <td ${TD_TEXT_STYLE}><b>B message</b></td> + <td ${TD_NUMBER_STYLE}>message2</td> + </tr> + <tr ${TR_STYLE}> + <td ${TD_TEXT_STYLE}><img src="x" /></td> + <td ${TD_NUMBER_STYLE}><i>Italic</i></td> + </tr> + </table> + </div>`), + ); + expect(html).toMatch(expectedHtml); });