From 8922f3bf238a5155ed8a277a70f6773fb487fe34 Mon Sep 17 00:00:00 2001 From: mattfeng6 Date: Sun, 26 Oct 2025 18:24:37 -0400 Subject: [PATCH] add query html wrapper --- .../superset-ui-core/src/utils/html.test.tsx | 44 +++++++++++++++---- .../superset-ui-core/src/utils/html.tsx | 27 +++++++++--- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx b/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx index 3f38e8c9c162..16adc2dd7edd 100644 --- a/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx +++ b/superset-frontend/packages/superset-ui-core/src/utils/html.test.tsx @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import React from 'react'; +import { render } from '@testing-library/react'; import { sanitizeHtml, isProbablyHTML, @@ -24,6 +26,7 @@ import { removeHTMLTags, isJsonString, getParagraphContents, + escapeHtml, } from './html'; describe('sanitizeHtml', () => { @@ -65,16 +68,33 @@ describe('sanitizeHtmlIfNeeded', () => { }); }); +describe('escapeHtml', () => { + test('should escape HTML special characters', () => { + const htmlString = '
test
'; + const escaped = escapeHtml(htmlString); + expect(escaped).toBe('<div>test</div>'); + }); + + test('should escape all special characters', () => { + const testString = '<>&"\'/'; + const escaped = escapeHtml(testString); + expect(escaped).toBe('<>&"'/'); + }); + + test('should not escape regular text', () => { + const plainText = 'Just a plain text'; + const escaped = escapeHtml(plainText); + expect(escaped).toBe(plainText); + }); +}); + describe('safeHtmlSpan', () => { - test('should return a safe HTML span when the input is HTML', () => { + test('should render HTML content as a span element to display as text', () => { const htmlString = '
Some HTML content
'; - const safeSpan = safeHtmlSpan(htmlString); - expect(safeSpan).toEqual( - , - ); + const result = safeHtmlSpan(htmlString); + const { container } = render(result as React.ReactElement); + // The span should contain the raw HTML string as text content + expect(container.textContent).toBe(htmlString); }); test('should return the input string as is when it is not HTML', () => { @@ -82,6 +102,14 @@ describe('safeHtmlSpan', () => { const result = safeHtmlSpan(plainText); expect(result).toEqual(plainText); }); + + test('should preserve HTML tags as visible text', () => { + const htmlString = '
test
'; + const result = safeHtmlSpan(htmlString); + const { container } = render(result as React.ReactElement); + // Should display
test
as text + expect(container.textContent).toBe(htmlString); + }); }); describe('removeHTMLTags', () => { diff --git a/superset-frontend/packages/superset-ui-core/src/utils/html.tsx b/superset-frontend/packages/superset-ui-core/src/utils/html.tsx index 723c0dc3d968..045e9b85f30d 100644 --- a/superset-frontend/packages/superset-ui-core/src/utils/html.tsx +++ b/superset-frontend/packages/superset-ui-core/src/utils/html.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import React from 'react'; import { FilterXSS, getDefaultWhiteList } from 'xss'; const xssFilter = new FilterXSS({ @@ -70,15 +71,27 @@ export function sanitizeHtmlIfNeeded(htmlString: string) { return isProbablyHTML(htmlString) ? sanitizeHtml(htmlString) : htmlString; } -export function safeHtmlSpan(possiblyHtmlString: string) { +/** + * Escapes HTML special characters to display them as text + * Converts < to <, > to >, & to &, etc. + */ +export function escapeHtml(text: string): string { + const map: Record = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + }; + return text.replace(/[&<>"'/]/g, char => map[char]); +} + +export function safeHtmlSpan(possiblyHtmlString: string): string | JSX.Element { const isHtml = isProbablyHTML(possiblyHtmlString); if (isHtml) { - return ( - - ); + // Render as plain text to display the raw HTML string without interpreting it + return {possiblyHtmlString}; } return possiblyHtmlString; }