From f59c5143630e948c91e88fa3e9b621a2b1d346b5 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 14 Jan 2025 22:07:19 +0100 Subject: [PATCH 01/11] fix: privacy violations --- src/shared/containers/Profile.jsx | 15 ++++++- src/shared/containers/ProfileStats.jsx | 11 ++++- src/shared/containers/Toastr/index.jsx | 8 +++- src/shared/utils/markdown.js | 56 +++++++++++++++++++++++--- 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx index af011788f8..be4497a268 100644 --- a/src/shared/containers/Profile.jsx +++ b/src/shared/containers/Profile.jsx @@ -144,7 +144,20 @@ class ProfileContainer extends React.Component { { info ? ( ) : } diff --git a/src/shared/containers/ProfileStats.jsx b/src/shared/containers/ProfileStats.jsx index 56ae4ce25e..03c87fb7da 100644 --- a/src/shared/containers/ProfileStats.jsx +++ b/src/shared/containers/ProfileStats.jsx @@ -99,7 +99,16 @@ class ProfileStatsContainer extends React.Component { isLoading ? : ( ) } diff --git a/src/shared/containers/Toastr/index.jsx b/src/shared/containers/Toastr/index.jsx index ba313e9de8..4c68fcf82b 100644 --- a/src/shared/containers/Toastr/index.jsx +++ b/src/shared/containers/Toastr/index.jsx @@ -78,7 +78,13 @@ class ExtendedReduxToastr extends ReduxToastr { inMemory={this.toastrFired} addToMemory={() => this._addToMemory(item.id)} item={mergedItem} - {...this.props} + toastrs={this.props.toastrs} + preventDuplicates={this.props.preventDuplicates} + position={this.props.position} + transitionIn={this.props.transitionIn} + transitionOut={this.props.transitionOut} + progressBar={this.props.progressBar} + showCloseButton={this.props.showCloseButton} /> {item.options && item.options.attention && ( diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index 5e0883bf04..db74cb9f7f 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -13,6 +13,7 @@ import { Button, PrimaryButton, SecondaryButton } from 'topcoder-react-ui-kit'; import { Link } from 'topcoder-react-utils'; import hljs from 'highlight.js'; import ReactHtmlParser from 'react-html-parser'; +import xss from 'xss'; import sub from 'markdown-it-sub'; import sup from 'markdown-it-sup'; import 'highlight.js/styles/github.css'; @@ -77,6 +78,31 @@ const buttonThemes = { bs, }; +const safeHtmlTags = [ + // Content Sectioning + "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4", "h5", "h6", "main", "nav", "section", + + // Text Content + "blockquote", "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "li", "ol", "p", "pre", "ul", + + // Inline Text Semantics + "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", + "ruby", "s", "samp", "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", + + // Image and Multimedia + "img", "audio", "video", "source", "track", "picture", + + // Table Content + "caption", "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", + + // Forms and Interactive Elements + "button", "fieldset", "form", "input", "label", "legend", "meter", "optgroup", "option", "output", "progress", + "select", "textarea", + + // Scripting and No-Scripting + "noscript" +]; + /** * Add new Custom Components here. * @@ -165,6 +191,24 @@ function getProps(token, key) { return normalizeProps(res); } +/** + * Check if the tag is safe to render. + * @param {String} tag + * @returns + */ +function checkForSafeTag(tag) { + return safeHtmlTags.includes(tag); +} + +/** + * Sanitize content + * @param {String} content + * @returns + */ +function sanitizeContent(content) { + return xss(content); +} + /** * Renders tokens with zero nesting. * @param {Object} tokens @@ -184,7 +228,7 @@ function renderToken(tokens, index, md) { return renderTokens(token.children, 0, md); /* eslint-enable no-use-before-define */ case 'text': - return token.content; + return sanitizeContent(token.content); case 'fence': return Highlighter({ codeString: token.content, @@ -204,9 +248,9 @@ function renderToken(tokens, index, md) { } default: return React.createElement( - token.tag, + checkForSafeTag(token.tag) ? token.tag : 'div', getProps(token, index), - token.content || undefined, + sanitizeContent(token.content) || undefined, ); } } @@ -232,7 +276,7 @@ function renderTokens(tokens, startFrom, md) { } else if (level === 0) { if (token.nesting === 1) { output.push(React.createElement( - token.tag, + checkForSafeTag(token.tag) ? token.tag : "div", getProps(token, pos), renderTokens(tokens, 1 + pos, md), )); @@ -252,11 +296,11 @@ function renderTokens(tokens, startFrom, md) { } props = normalizeProps(props); if (selfClosing) { - output.push(React.createElement(tag, { key: pos, ...props })); + output.push(React.createElement(checkForSafeTag(tag) ? tag : "div", { key: pos, ...props })); } else { level += 1; output.push(React.createElement( - tag, + checkForSafeTag(tag) ? tag : "div", { key: pos, ...props }, renderTokens(tokens, pos + 1, md), )); From 39565d7522a2a3922056ac46b8888dd1c2339395 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 14 Jan 2025 22:46:20 +0100 Subject: [PATCH 02/11] fix: lint --- src/shared/containers/Profile.jsx | 32 +++++++++++++++----------- src/shared/containers/ProfileStats.jsx | 27 ++++++++++++++-------- src/shared/utils/markdown.js | 18 +++++++-------- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx index be4497a268..2a8df70e4f 100644 --- a/src/shared/containers/Profile.jsx +++ b/src/shared/containers/Profile.jsx @@ -135,6 +135,7 @@ class ProfileContainer extends React.Component { const title = `${handleParam} | Community Profile | Topcoder`; const description = `Meet Topcoder member ${handleParam} and view their skills and development and design activity. You can also see wins and tenure with Topcoder.`; + const {copilot, externalAccounts, externalLinks, challenges, skills, stats, lookupData, badges, meta, tcAcademyCertifications, tcAcademyCourses} = this.props; return ( ) : } @@ -183,6 +183,8 @@ ProfileContainer.defaultProps = { ProfileContainer.propTypes = { achievements: PT.arrayOf(PT.shape()), + badges: PT.shape(), + challenges: PT.arrayOf(PT.shape()), copilot: PT.bool, country: PT.string, externalAccounts: PT.shape(), @@ -201,6 +203,8 @@ ProfileContainer.propTypes = { lookupData: PT.shape().isRequired, meta: PT.shape(), auth: PT.shape(), + tcAcademyCertifications: PT.arrayOf(PT.shape()), + tcAcademyCourses: PT.arrayOf(PT.shape()), }; const mapStateToProps = (state, ownProps) => ({ diff --git a/src/shared/containers/ProfileStats.jsx b/src/shared/containers/ProfileStats.jsx index 03c87fb7da..527e149224 100644 --- a/src/shared/containers/ProfileStats.jsx +++ b/src/shared/containers/ProfileStats.jsx @@ -81,6 +81,13 @@ class ProfileStatsContainer extends React.Component { handleParam, track, subTrack, + stats, + tab, + setTab, + info, + statsDistribution, + statsHistory, + isAlreadyLoadChallenge, } = this.props; if (loadingError || !isValidTrack(track, subTrack)) { @@ -99,16 +106,16 @@ class ProfileStatsContainer extends React.Component { isLoading ? : ( ) } diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index db74cb9f7f..0144420f58 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -80,27 +80,27 @@ const buttonThemes = { const safeHtmlTags = [ // Content Sectioning - "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4", "h5", "h6", "main", "nav", "section", + 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section', // Text Content - "blockquote", "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "li", "ol", "p", "pre", "ul", + 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', // Inline Text Semantics - "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", - "ruby", "s", "samp", "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", + 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', + 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', // Image and Multimedia - "img", "audio", "video", "source", "track", "picture", + 'img', 'audio', 'video', 'source', 'track', 'picture', // Table Content - "caption", "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", + 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', // Forms and Interactive Elements - "button", "fieldset", "form", "input", "label", "legend", "meter", "optgroup", "option", "output", "progress", - "select", "textarea", + 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', + 'select', 'textarea', // Scripting and No-Scripting - "noscript" + 'noscript' ]; /** From 33f4564ca696b5b3762b717abd84f66605470ef6 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 14 Jan 2025 23:01:27 +0100 Subject: [PATCH 03/11] fix: lint --- src/shared/containers/Profile.jsx | 25 ++++++++++++++++++------- src/shared/utils/markdown.js | 18 +++++++++--------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx index 2a8df70e4f..d3f91eb194 100644 --- a/src/shared/containers/Profile.jsx +++ b/src/shared/containers/Profile.jsx @@ -135,7 +135,20 @@ class ProfileContainer extends React.Component { const title = `${handleParam} | Community Profile | Topcoder`; const description = `Meet Topcoder member ${handleParam} and view their skills and development and design activity. You can also see wins and tenure with Topcoder.`; - const {copilot, externalAccounts, externalLinks, challenges, skills, stats, lookupData, badges, meta, tcAcademyCertifications, tcAcademyCourses} = this.props; + const { + copilot, + externalAccounts, + externalLinks, + challenges, + skills, + stats, + lookupData, + badges, + meta, + tcAcademyCertifications, + tcAcademyCourses + } = this.props; + return ( ({ challenges: state.members[ownProps.match.params.handle] ? state.members[ownProps.match.params.handle].userMarathons : null, - achievements: state.profile.achievements, copilot: state.profile.copilot, - country: state.profile.country, externalAccounts: state.profile.externalAccounts, externalLinks: state.profile.externalLinks, handleParam: ownProps.match.params.handle, diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index 0144420f58..9d33046561 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -96,11 +96,11 @@ const safeHtmlTags = [ 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', // Forms and Interactive Elements - 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', + 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', 'select', 'textarea', // Scripting and No-Scripting - 'noscript' + 'noscript', ]; /** @@ -193,8 +193,8 @@ function getProps(token, key) { /** * Check if the tag is safe to render. - * @param {String} tag - * @returns + * @param {String} tag + * @returns */ function checkForSafeTag(tag) { return safeHtmlTags.includes(tag); @@ -202,8 +202,8 @@ function checkForSafeTag(tag) { /** * Sanitize content - * @param {String} content - * @returns + * @param {String} content + * @returns */ function sanitizeContent(content) { return xss(content); @@ -276,7 +276,7 @@ function renderTokens(tokens, startFrom, md) { } else if (level === 0) { if (token.nesting === 1) { output.push(React.createElement( - checkForSafeTag(token.tag) ? token.tag : "div", + checkForSafeTag(token.tag) ? token.tag : 'div', getProps(token, pos), renderTokens(tokens, 1 + pos, md), )); @@ -296,11 +296,11 @@ function renderTokens(tokens, startFrom, md) { } props = normalizeProps(props); if (selfClosing) { - output.push(React.createElement(checkForSafeTag(tag) ? tag : "div", { key: pos, ...props })); + output.push(React.createElement(checkForSafeTag(tag) ? tag : 'div', { key: pos, ...props })); } else { level += 1; output.push(React.createElement( - checkForSafeTag(tag) ? tag : "div", + checkForSafeTag(tag) ? tag : 'div', { key: pos, ...props }, renderTokens(tokens, pos + 1, md), )); From d35c1ff44e2f52783cd90fe8be5a5fc16971faa8 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 14 Jan 2025 23:06:30 +0100 Subject: [PATCH 04/11] fix: lint --- src/shared/containers/Profile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx index d3f91eb194..cbc047eecd 100644 --- a/src/shared/containers/Profile.jsx +++ b/src/shared/containers/Profile.jsx @@ -146,7 +146,7 @@ class ProfileContainer extends React.Component { badges, meta, tcAcademyCertifications, - tcAcademyCourses + tcAcademyCourses, } = this.props; return ( From 1eff28dd3ef7263b8e59e28c3c7c7f64882b37cc Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 21:21:58 +0100 Subject: [PATCH 05/11] fix: unit tests --- src/shared/utils/markdown.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index 9d33046561..e0e02f0b91 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -83,7 +83,7 @@ const safeHtmlTags = [ 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section', // Text Content - 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', + 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', 'big', 'tt', 'del', 'strike', 'ins', // Inline Text Semantics 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', @@ -101,6 +101,9 @@ const safeHtmlTags = [ // Scripting and No-Scripting 'noscript', + + // Custom tags + 'ThemedButton', ]; /** From 95230a6f56c275f329680006fb4f6cc1385661b6 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 21:36:06 +0100 Subject: [PATCH 06/11] fix: removed password protection feature from contentful --- .../Contentful/PasswordScreen/index.jsx | 110 ------------------ .../Contentful/PasswordScreen/style.scss | 51 -------- src/shared/components/Contentful/Route.jsx | 32 ++--- 3 files changed, 9 insertions(+), 184 deletions(-) delete mode 100644 src/shared/components/Contentful/PasswordScreen/index.jsx delete mode 100644 src/shared/components/Contentful/PasswordScreen/style.scss diff --git a/src/shared/components/Contentful/PasswordScreen/index.jsx b/src/shared/components/Contentful/PasswordScreen/index.jsx deleted file mode 100644 index c574520af5..0000000000 --- a/src/shared/components/Contentful/PasswordScreen/index.jsx +++ /dev/null @@ -1,110 +0,0 @@ -/** - * High order component that apply front-side password protection - * before loading a Contentful Viewport. It uses sessionStorage for working. - */ -import PT from 'prop-types'; -import React from 'react'; -import Viewport from 'components/Contentful/Viewport'; -import TextInput from 'components/GUIKit/TextInput'; - -import './style.scss'; - -export default class PasswordScreen extends React.Component { - state = {}; - - constructor(props) { - super(props); - - this.onSubmit = this.onSubmit.bind(this); - this.onPasswordInput = this.onPasswordInput.bind(this); - } - - onSubmit() { - const { password } = this.props; - this.setState((state) => { - const { inputVal } = state; - return { - authorized: password === inputVal, - errorMsg: password === inputVal ? '' : 'Password incorrect', - }; - }); - } - - onPasswordInput(inputVal) { - const update = { - inputVal, - }; - if (!inputVal) update.errorMsg = ''; - this.setState(update); - } - - render() { - const { - authorized, errorMsg, inputVal, - } = this.state; - const { - viewPortId, preview, spaceName, environment, baseUrl, title, btnText, content, - } = this.props; - return authorized ? ( - - ) : ( -
-
-

{title || 'GET ACCESS WITH PASSWORD'}

-

Please enter the password you were provided

- this.onPasswordInput(val)} - errorMsg={errorMsg} - required - type="password" - onEnterKey={this.onSubmit} - /> -
- -
-
- { - content ? ( - - ) : null - } -
- ); - } -} - -PasswordScreen.defaultProps = { - preview: false, - spaceName: null, - environment: null, - baseUrl: '', - title: 'GET ACCESS WITH PASSWORD', - btnText: 'SUBMIT', - content: null, -}; - -PasswordScreen.propTypes = { - password: PT.string.isRequired, - viewPortId: PT.string.isRequired, - preview: PT.bool, - spaceName: PT.string, - environment: PT.string, - baseUrl: PT.string, - title: PT.string, - btnText: PT.string, - content: PT.shape(), -}; diff --git a/src/shared/components/Contentful/PasswordScreen/style.scss b/src/shared/components/Contentful/PasswordScreen/style.scss deleted file mode 100644 index 74d6ddfd05..0000000000 --- a/src/shared/components/Contentful/PasswordScreen/style.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import "~styles/mixins"; -@import "~components/Contentful/default"; -@import "~components/GUIKit/Assets/Styles/default"; - -.wrapper { - background-color: #e9e9e9; - padding: 86px 0 121px 0; - min-height: 100vh; - - .container { - text-align: center; - border-radius: 10px; - max-width: 544px; - max-height: 371px; - margin: 0 auto; - padding: 31px 65px; - background-color: #fff; - - @include gui-kit-headers; - @include gui-kit-content; - - h4 { - margin-bottom: 5px; - } - - .hint { - font-size: 14px; - margin-bottom: 30px; - } - - .cta { - margin: 50px auto 29px auto; - } - - .submit { - outline: none; - - @include primary-green; - @include md; - - &:disabled, - &:hover:disabled { - background-color: #e9e9e9 !important; - border: none !important; - text-decoration: none !important; - color: #fafafb !important; - box-shadow: none !important; - } - } - } -} diff --git a/src/shared/components/Contentful/Route.jsx b/src/shared/components/Contentful/Route.jsx index 4b88d43585..2d1996b52b 100644 --- a/src/shared/components/Contentful/Route.jsx +++ b/src/shared/components/Contentful/Route.jsx @@ -12,7 +12,6 @@ import PT from 'prop-types'; import React from 'react'; import { Route, Switch, Redirect } from 'react-router-dom'; import Viewport from 'components/Contentful/Viewport'; -import PasswordScreen from 'components/Contentful/PasswordScreen'; import { isomorphy, config } from 'topcoder-react-utils'; import cookies from 'browser-cookies'; import { removeTrailingSlash } from 'utils/url'; @@ -57,28 +56,15 @@ function ChildRoutesLoader(props) { { // eslint-disable-next-line no-nested-ternary fields.viewport - ? (!fields.password ? ( - - ) : ( - - ) - ) : + ? ( + + ) : }
)} From 98a38bfa8c7d12eb301e39da33a3296f4dda1356 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 22:00:30 +0100 Subject: [PATCH 07/11] fix: lint --- src/shared/components/Contentful/Route.jsx | 16 ++++++++-------- src/shared/utils/markdown.js | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/shared/components/Contentful/Route.jsx b/src/shared/components/Contentful/Route.jsx index 2d1996b52b..0d9dccaf47 100644 --- a/src/shared/components/Contentful/Route.jsx +++ b/src/shared/components/Contentful/Route.jsx @@ -57,14 +57,14 @@ function ChildRoutesLoader(props) { // eslint-disable-next-line no-nested-ternary fields.viewport ? ( - - ) : + + ) : }
)} diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index e0e02f0b91..e9afd6a9ea 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -103,7 +103,7 @@ const safeHtmlTags = [ 'noscript', // Custom tags - 'ThemedButton', + 'Button', ]; /** From 549e21cd70158661144d6c096505de885b675366 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 22:11:44 +0100 Subject: [PATCH 08/11] fix: unit test --- src/shared/utils/markdown.js | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index e9afd6a9ea..bde95592a4 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -78,34 +78,6 @@ const buttonThemes = { bs, }; -const safeHtmlTags = [ - // Content Sectioning - 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section', - - // Text Content - 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', 'big', 'tt', 'del', 'strike', 'ins', - - // Inline Text Semantics - 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', - 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', - - // Image and Multimedia - 'img', 'audio', 'video', 'source', 'track', 'picture', - - // Table Content - 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', - - // Forms and Interactive Elements - 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', - 'select', 'textarea', - - // Scripting and No-Scripting - 'noscript', - - // Custom tags - 'Button', -]; - /** * Add new Custom Components here. * @@ -162,6 +134,34 @@ const customComponents = { MMLeaderboard: attrs => ({ type: MMLeaderboard, props: attrs }), }; +const safeHtmlTags = [ + // Content Sectioning + 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section', + + // Text Content + 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', 'big', 'tt', 'del', 'strike', 'ins', + + // Inline Text Semantics + 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', + 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', + + // Image and Multimedia + 'img', 'audio', 'video', 'source', 'track', 'picture', + + // Table Content + 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', + + // Forms and Interactive Elements + 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', + 'select', 'textarea', + + // Scripting and No-Scripting + 'noscript', + + // Custom tags + ...Object.keys(customTags), +]; + /** * The following functions are only used internally and should not need to be * changed for new components. From 52aaa6faabb604aea2e169bf104e86db9a2aafe9 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 22:20:56 +0100 Subject: [PATCH 09/11] fix: unit test --- src/shared/utils/markdown.js | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index bde95592a4..1b23da60d2 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -134,32 +134,9 @@ const customComponents = { MMLeaderboard: attrs => ({ type: MMLeaderboard, props: attrs }), }; -const safeHtmlTags = [ - // Content Sectioning - 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section', - - // Text Content - 'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure', 'hr', 'li', 'ol', 'p', 'pre', 'ul', 'big', 'tt', 'del', 'strike', 'ins', - - // Inline Text Semantics - 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', - 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', - - // Image and Multimedia - 'img', 'audio', 'video', 'source', 'track', 'picture', - - // Table Content - 'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', - - // Forms and Interactive Elements - 'button', 'fieldset', 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', 'output', 'progress', - 'select', 'textarea', - - // Scripting and No-Scripting - 'noscript', - - // Custom tags - ...Object.keys(customTags), +const unsafeHtmlTags = [ + 'script', 'style', 'iframe', 'object', 'embed', 'applet', 'base', + 'form', 'meta', 'frame', 'frameset', 'marquee', 'svg', ]; /** @@ -200,7 +177,7 @@ function getProps(token, key) { * @returns */ function checkForSafeTag(tag) { - return safeHtmlTags.includes(tag); + return !unsafeHtmlTags.includes(tag); } /** From 29655d32d09eb826ba991683511e70638e7c87c6 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 22:37:55 +0100 Subject: [PATCH 10/11] fix: unit test --- src/shared/utils/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index 1b23da60d2..f7168349f6 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -208,7 +208,7 @@ function renderToken(tokens, index, md) { return renderTokens(token.children, 0, md); /* eslint-enable no-use-before-define */ case 'text': - return sanitizeContent(token.content); + return token.content; case 'fence': return Highlighter({ codeString: token.content, From e574f3c2790d46872c54e19d7f1693bd2c1b78bc Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Wed, 15 Jan 2025 22:46:27 +0100 Subject: [PATCH 11/11] fix: unit test --- __tests__/shared/utils/__snapshots__/markdown.js.snap | 4 ++-- src/shared/utils/markdown.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/shared/utils/__snapshots__/markdown.js.snap b/__tests__/shared/utils/__snapshots__/markdown.js.snap index 449da8d15c..5dc7969857 100644 --- a/__tests__/shared/utils/__snapshots__/markdown.js.snap +++ b/__tests__/shared/utils/__snapshots__/markdown.js.snap @@ -1203,7 +1203,7 @@ Array [ A list item with a code block:

- <code goes here> + <code> @@ -1294,7 +1294,7 @@ end tell ampersands and angle brackets. For example, this:

, - <div class="footer"> + <div> © 2004 Foo Corporation </div> diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js index f7168349f6..1b23da60d2 100644 --- a/src/shared/utils/markdown.js +++ b/src/shared/utils/markdown.js @@ -208,7 +208,7 @@ function renderToken(tokens, index, md) { return renderTokens(token.children, 0, md); /* eslint-enable no-use-before-define */ case 'text': - return token.content; + return sanitizeContent(token.content); case 'fence': return Highlighter({ codeString: token.content,