From 6d8ff97831f0fc2b5058dc459c29770c1b3e9bf2 Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Thu, 13 Feb 2025 14:06:51 -0500 Subject: [PATCH 1/4] chore(deps): Replace showdown dependency with marked --- package.json | 3 +- packages/dev/package.json | 2 +- packages/module/package.json | 2 +- .../components/markdown-view.tsx | 86 ++++++++----------- .../module/src/QuickStartPanelContent.tsx | 11 ++- .../src/controller/QuickStartTaskHeader.tsx | 13 ++- yarn.lock | 17 ++-- 7 files changed, 66 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index a17d58a6..c0bd8b4f 100644 --- a/package.json +++ b/package.json @@ -112,5 +112,6 @@ "!**/node_modules/**" ], "resolver": "./jest-resolver.js" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/packages/dev/package.json b/packages/dev/package.json index b3770cad..309eaeb1 100644 --- a/packages/dev/package.json +++ b/packages/dev/package.json @@ -22,7 +22,7 @@ "react-dom": "^16.14.0", "react-i18next": "^11.7.3", "react-router-dom": "^5.2.0", - "showdown": "2.1.0", + "marked": "^15.0.6", "lodash-es": "^4.17.21" }, "devDependencies": { diff --git a/packages/module/package.json b/packages/module/package.json index 29cce6dc..fb307255 100644 --- a/packages/module/package.json +++ b/packages/module/package.json @@ -48,7 +48,7 @@ "@patternfly/react-core": ">=4.115.2", "react": ">=16.8.0", "react-dom": ">=16.8.0", - "showdown": ">=2.1.0" + "marked": "^15.0.6" }, "dependencies": { "@patternfly/react-catalog-view-extension": "^4.93.15", diff --git a/packages/module/src/ConsoleInternal/components/markdown-view.tsx b/packages/module/src/ConsoleInternal/components/markdown-view.tsx index f958184f..7aa77952 100644 --- a/packages/module/src/ConsoleInternal/components/markdown-view.tsx +++ b/packages/module/src/ConsoleInternal/components/markdown-view.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import { Converter } from 'showdown'; +import { marked } from 'marked'; import { useForceRender } from '@console/shared'; import { QuickStartContext, QuickStartContextValues } from '../../utils/quick-start-context'; import './_markdown-view.scss'; @@ -16,19 +16,8 @@ type ShowdownExtension = { replace?: (...args: any[]) => string; }; -export const markdownConvert = (markdown, extensions?: ShowdownExtension[]) => { - const converter = new Converter({ - tables: true, - openLinksInNewWindow: true, - strikethrough: true, - emoji: false, - }); - - if (extensions) { - converter.addExtension(extensions); - } - - DOMPurify.addHook('beforeSanitizeElements', function(node) { +export const markdownConvert = async (markdown: string, extensions?: ShowdownExtension[]) => { + DOMPurify.addHook('beforeSanitizeElements', function (node) { // nodeType 1 = element type // transform anchor tags @@ -62,40 +51,26 @@ export const markdownConvert = (markdown, extensions?: ShowdownExtension[]) => { } }); - return DOMPurify.sanitize(converter.makeHtml(markdown), { - USE_PROFILES: { - html: true, - svg: true, - }, - // ALLOWED_TAGS: [ - // 'b', - // 'i', - // 'strike', - // 's', - // 'del', - // 'em', - // 'strong', - // 'a', - // 'p', - // 'h1', - // 'h2', - // 'h3', - // 'h4', - // 'ul', - // 'ol', - // 'li', - // 'code', - // 'pre', - // 'button', - // ...tableTags, - // 'div', - // 'img', - // 'span', - // 'svg', - // ], - // ALLOWED_ATTR: ['href', 'target', 'rel', 'class', 'src', 'alt', 'id'], - // ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|didact):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i, - }); + // Replace code fences with non markdown formatting relates tokens so that marked doesn't try to parse them as code spans + const markdownWithSubstitutedCodeFences = markdown.replace(/```/g, '@@@'); + const parsedMarkdown = await marked.parse(markdownWithSubstitutedCodeFences); + // Swap the temporary tokens back to code fences before we run the extensions + let md = parsedMarkdown.replace(/@@@/g, '```'); + + if (extensions) { + // Convert code spans back to md format before we run the custom extension regexes + md = md.replace(/(.*)<\/code>/g, '`$1`'); + + extensions.forEach(({ regex, replace }) => { + if (regex) { + md = md.replace(regex, replace); + } + }); + + // Convert any remaining backticks back into code spans + md = md.replace(/`(.*)`/g, '$1'); + } + return DOMPurify.sanitize(md); }; type SyncMarkdownProps = { @@ -126,9 +101,18 @@ export const SyncMarkdownView: React.FC = ({ className, }) => { const { getResource } = React.useContext(QuickStartContext); - const markup = React.useMemo(() => { - return markdownConvert(content || emptyMsg || getResource('Not available'), extensions); - }, [content, emptyMsg, extensions, getResource]); + const [markup, setMarkup] = React.useState(''); + + React.useEffect(() => { + async function getMd() { + const md = await markdownConvert( + content || emptyMsg || getResource('Not available'), + extensions, + ); + setMarkup(md); + } + getMd(); + }, [content, emptyMsg, getResource, extensions]); const innerProps: InnerSyncMarkdownProps = { renderExtension: extensions?.length > 0 ? renderExtension : undefined, exactHeight, diff --git a/packages/module/src/QuickStartPanelContent.tsx b/packages/module/src/QuickStartPanelContent.tsx index 949451cc..c8e6e046 100644 --- a/packages/module/src/QuickStartPanelContent.tsx +++ b/packages/module/src/QuickStartPanelContent.tsx @@ -60,6 +60,7 @@ const QuickStartPanelContent: React.FC = ({ QuickStartContext, ); const [contentRef, setContentRef] = React.useState(); + const [displayName, setDisplayName] = React.useState(''); const shadows = useScrollShadows(contentRef); const quickStart = quickStarts.find((qs) => qs.metadata.name === activeQuickStartID); const taskNumber = activeQuickStartState?.taskNumber; @@ -96,6 +97,14 @@ const QuickStartPanelContent: React.FC = ({ } }, [quickStart]); + React.useEffect(() => { + async function getDisplayName() { + const convertedMdDisplayName = await markdownConvert(quickStart?.spec.displayName); + setDisplayName(removeParagraphWrap(convertedMdDisplayName)); + } + getDisplayName(); + }, [quickStart]); + const content = quickStart ? ( = ({ > {' '} diff --git a/packages/module/src/controller/QuickStartTaskHeader.tsx b/packages/module/src/controller/QuickStartTaskHeader.tsx index 4c4e8bcd..178ec9b4 100644 --- a/packages/module/src/controller/QuickStartTaskHeader.tsx +++ b/packages/module/src/controller/QuickStartTaskHeader.tsx @@ -64,12 +64,23 @@ const QuickStartTaskHeader: React.FC = ({ children, }) => { const titleRef = React.useRef(null); + const [parsedTitle, setParsedTitle] = React.useState(''); + React.useEffect(() => { if (isActiveTask) { // Focus the WizardNavItem button element that contains the title titleRef.current.parentNode.focus(); } }, [isActiveTask]); + + React.useEffect(() => { + async function getParsedTitle() { + const convertedMdTitle = await markdownConvert(title); + setParsedTitle(removeParagraphWrap(convertedMdTitle)); + } + getParsedTitle(); + }, [title]); + const classNames = css('pfext-quick-start-task-header__title', { 'pfext-quick-start-task-header__title-success': taskStatus === QuickStartTaskStatus.SUCCESS, 'pfext-quick-start-task-header__title-failed': @@ -92,7 +103,7 @@ const QuickStartTaskHeader: React.FC = ({
- <span dangerouslySetInnerHTML={{ __html: removeParagraphWrap(markdownConvert(title)) }} /> + <span dangerouslySetInnerHTML={{ __html: parsedTitle }} /> {isActiveTask && subtitle && ( <span className="pfext-quick-start-task-header__subtitle" diff --git a/yarn.lock b/yarn.lock index b9b1fbcd..206a1a0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4630,11 +4630,6 @@ commander@^7.0.0, commander@~7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^9.0.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - common-log-format@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/common-log-format/-/common-log-format-0.1.4.tgz#39ba6ccb5c46c2f89aa92f232866d18f18075ba9" @@ -10650,6 +10645,11 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +marked@^15.0.6: + version "15.0.7" + resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.7.tgz#f67d7e34d202ce087e6b879107b5efb04e743314" + integrity sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg== + matcher-collection@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-2.0.1.tgz#90be1a4cf58d6f2949864f65bb3b0f3e41303b29" @@ -14263,13 +14263,6 @@ shortid@^2.2.8: dependencies: nanoid "^2.1.0" -showdown@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/showdown/-/showdown-2.1.0.tgz#1251f5ed8f773f0c0c7bfc8e6fd23581f9e545c5" - integrity sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ== - dependencies: - commander "^9.0.0" - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" From 8751fdf364a34d39a764f89fb17bc4a69c8043e7 Mon Sep 17 00:00:00 2001 From: Austin Sullivan <ausulliv@redhat.com> Date: Fri, 14 Feb 2025 15:17:40 -0500 Subject: [PATCH 2/4] Downgrade marked to support older node versions --- packages/dev/package.json | 2 +- .../src/ConsoleInternal/components/markdown-view.tsx | 4 ++-- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/dev/package.json b/packages/dev/package.json index 309eaeb1..acdc1ab0 100644 --- a/packages/dev/package.json +++ b/packages/dev/package.json @@ -22,7 +22,7 @@ "react-dom": "^16.14.0", "react-i18next": "^11.7.3", "react-router-dom": "^5.2.0", - "marked": "^15.0.6", + "marked": "^3.0.0", "lodash-es": "^4.17.21" }, "devDependencies": { diff --git a/packages/module/src/ConsoleInternal/components/markdown-view.tsx b/packages/module/src/ConsoleInternal/components/markdown-view.tsx index 7aa77952..ad1112d2 100644 --- a/packages/module/src/ConsoleInternal/components/markdown-view.tsx +++ b/packages/module/src/ConsoleInternal/components/markdown-view.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import { marked } from 'marked'; +import { parse } from 'marked'; import { useForceRender } from '@console/shared'; import { QuickStartContext, QuickStartContextValues } from '../../utils/quick-start-context'; import './_markdown-view.scss'; @@ -53,7 +53,7 @@ export const markdownConvert = async (markdown: string, extensions?: ShowdownExt // Replace code fences with non markdown formatting relates tokens so that marked doesn't try to parse them as code spans const markdownWithSubstitutedCodeFences = markdown.replace(/```/g, '@@@'); - const parsedMarkdown = await marked.parse(markdownWithSubstitutedCodeFences); + const parsedMarkdown = await parse(markdownWithSubstitutedCodeFences); // Swap the temporary tokens back to code fences before we run the extensions let md = parsedMarkdown.replace(/@@@/g, '```'); diff --git a/yarn.lock b/yarn.lock index 206a1a0a..fb9d40da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10645,10 +10645,10 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" -marked@^15.0.6: - version "15.0.7" - resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.7.tgz#f67d7e34d202ce087e6b879107b5efb04e743314" - integrity sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg== +marked@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/marked/-/marked-3.0.8.tgz#2785f0dc79cbdc6034be4bb4f0f0a396bd3f8aeb" + integrity sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw== matcher-collection@^2.0.0: version "2.0.1" From bd942cca6781f0d2c85c7a32ced5419b02663ba6 Mon Sep 17 00:00:00 2001 From: Austin Sullivan <ausulliv@redhat.com> Date: Fri, 14 Feb 2025 15:38:01 -0500 Subject: [PATCH 3/4] Fix lint errors --- .../module/src/ConsoleInternal/components/markdown-view.tsx | 2 +- packages/module/src/controller/QuickStartTaskHeader.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/module/src/ConsoleInternal/components/markdown-view.tsx b/packages/module/src/ConsoleInternal/components/markdown-view.tsx index ad1112d2..8f046477 100644 --- a/packages/module/src/ConsoleInternal/components/markdown-view.tsx +++ b/packages/module/src/ConsoleInternal/components/markdown-view.tsx @@ -17,7 +17,7 @@ type ShowdownExtension = { }; export const markdownConvert = async (markdown: string, extensions?: ShowdownExtension[]) => { - DOMPurify.addHook('beforeSanitizeElements', function (node) { + DOMPurify.addHook('beforeSanitizeElements', function(node) { // nodeType 1 = element type // transform anchor tags diff --git a/packages/module/src/controller/QuickStartTaskHeader.tsx b/packages/module/src/controller/QuickStartTaskHeader.tsx index 178ec9b4..8eebc12c 100644 --- a/packages/module/src/controller/QuickStartTaskHeader.tsx +++ b/packages/module/src/controller/QuickStartTaskHeader.tsx @@ -80,7 +80,7 @@ const QuickStartTaskHeader: React.FC<QuickStartTaskHeaderProps> = ({ } getParsedTitle(); }, [title]); - + const classNames = css('pfext-quick-start-task-header__title', { 'pfext-quick-start-task-header__title-success': taskStatus === QuickStartTaskStatus.SUCCESS, 'pfext-quick-start-task-header__title-failed': From ef37fca2b44669bfe81e1a87990dc774cfa58679 Mon Sep 17 00:00:00 2001 From: Austin Sullivan <ausulliv@redhat.com> Date: Fri, 14 Feb 2025 15:44:12 -0500 Subject: [PATCH 4/4] Update to marked 4 to prevent security issues --- packages/dev/package.json | 2 +- packages/module/package.json | 2 +- .../src/ConsoleInternal/components/markdown-view.tsx | 4 ++-- packages/module/src/declaration.d.ts | 1 + yarn.lock | 8 ++++---- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/dev/package.json b/packages/dev/package.json index acdc1ab0..da782abf 100644 --- a/packages/dev/package.json +++ b/packages/dev/package.json @@ -22,7 +22,7 @@ "react-dom": "^16.14.0", "react-i18next": "^11.7.3", "react-router-dom": "^5.2.0", - "marked": "^3.0.0", + "marked": "^4.0.0", "lodash-es": "^4.17.21" }, "devDependencies": { diff --git a/packages/module/package.json b/packages/module/package.json index fb307255..db3a202c 100644 --- a/packages/module/package.json +++ b/packages/module/package.json @@ -48,7 +48,7 @@ "@patternfly/react-core": ">=4.115.2", "react": ">=16.8.0", "react-dom": ">=16.8.0", - "marked": "^15.0.6" + "marked": "^4.0.0" }, "dependencies": { "@patternfly/react-catalog-view-extension": "^4.93.15", diff --git a/packages/module/src/ConsoleInternal/components/markdown-view.tsx b/packages/module/src/ConsoleInternal/components/markdown-view.tsx index 8f046477..3db12086 100644 --- a/packages/module/src/ConsoleInternal/components/markdown-view.tsx +++ b/packages/module/src/ConsoleInternal/components/markdown-view.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import { parse } from 'marked'; +import { marked } from 'marked'; import { useForceRender } from '@console/shared'; import { QuickStartContext, QuickStartContextValues } from '../../utils/quick-start-context'; import './_markdown-view.scss'; @@ -53,7 +53,7 @@ export const markdownConvert = async (markdown: string, extensions?: ShowdownExt // Replace code fences with non markdown formatting relates tokens so that marked doesn't try to parse them as code spans const markdownWithSubstitutedCodeFences = markdown.replace(/```/g, '@@@'); - const parsedMarkdown = await parse(markdownWithSubstitutedCodeFences); + const parsedMarkdown = await marked.parse(markdownWithSubstitutedCodeFences); // Swap the temporary tokens back to code fences before we run the extensions let md = parsedMarkdown.replace(/@@@/g, '```'); diff --git a/packages/module/src/declaration.d.ts b/packages/module/src/declaration.d.ts index 93f92c51..76d4074e 100644 --- a/packages/module/src/declaration.d.ts +++ b/packages/module/src/declaration.d.ts @@ -1,2 +1,3 @@ declare module '*.scss'; declare module '*.json'; +declare module 'marked'; diff --git a/yarn.lock b/yarn.lock index fb9d40da..5b0a4cff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10645,10 +10645,10 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" -marked@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/marked/-/marked-3.0.8.tgz#2785f0dc79cbdc6034be4bb4f0f0a396bd3f8aeb" - integrity sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw== +marked@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== matcher-collection@^2.0.0: version "2.0.1"