diff --git a/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx b/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx index 4db48d426533d..8be73295b4971 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/SafeMarkdown.tsx @@ -20,7 +20,7 @@ import React, { useMemo } from 'react'; import ReactMarkdown from 'react-markdown'; import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; import rehypeRaw from 'rehype-raw'; -import { merge } from 'lodash'; +import { mergeWith, isArray } from 'lodash'; import { FeatureFlag, isFeatureEnabled } from '../utils'; interface SafeMarkdownProps { @@ -29,6 +29,15 @@ interface SafeMarkdownProps { htmlSchemaOverrides?: typeof defaultSchema; } +export function getOverrideHtmlSchema( + originalSchema: typeof defaultSchema, + htmlSchemaOverrides: SafeMarkdownProps['htmlSchemaOverrides'], +) { + return mergeWith(originalSchema, htmlSchemaOverrides, (objValue, srcValue) => + isArray(objValue) ? objValue.concat(srcValue) : undefined, + ); +} + function SafeMarkdown({ source, htmlSanitization = true, @@ -42,7 +51,10 @@ function SafeMarkdown({ if (displayHtml && !escapeHtml) { rehypePlugins.push(rehypeRaw); if (htmlSanitization) { - const schema = merge(defaultSchema, htmlSchemaOverrides); + const schema = getOverrideHtmlSchema( + defaultSchema, + htmlSchemaOverrides, + ); rehypePlugins.push([rehypeSanitize, schema]); } } diff --git a/superset-frontend/packages/superset-ui-core/test/components/SafeMarkdown.test.ts b/superset-frontend/packages/superset-ui-core/test/components/SafeMarkdown.test.ts new file mode 100644 index 0000000000000..4b4c826923bc5 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/test/components/SafeMarkdown.test.ts @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { getOverrideHtmlSchema } from '../../src/components/SafeMarkdown'; + +describe('getOverrideHtmlSchema', () => { + it('should append the override items', () => { + const original = { + attributes: { + '*': ['size'], + }, + clobberPrefix: 'original-prefix', + tagNames: ['h1', 'h2', 'h3'], + }; + const result = getOverrideHtmlSchema(original, { + attributes: { '*': ['src'], h1: ['style'] }, + clobberPrefix: 'custom-prefix', + tagNames: ['iframe'], + }); + expect(result.clobberPrefix).toEqual('custom-prefix'); + expect(result.attributes).toEqual({ '*': ['size', 'src'], h1: ['style'] }); + expect(result.tagNames).toEqual(['h1', 'h2', 'h3', 'iframe']); + }); +});