diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
21 files changed, 375 insertions, 370 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx index 70851441576..531c0162257 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import classNames from 'classnames'; import * as React from 'react'; import { OptionTypeBase } from 'react-select'; @@ -26,7 +27,7 @@ import Modal from '../../../components/controls/Modal'; import Select from '../../../components/controls/Select'; import { Alert } from '../../../components/ui/Alert'; import { translate } from '../../../helpers/l10n'; -import { sanitizeString } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { Dict, Rule, RuleActivation, RuleDetails } from '../../../types/types'; import { sortProfiles } from '../../quality-profiles/utils'; import { SeveritySelect } from './SeveritySelect'; @@ -218,11 +219,12 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat /> )} {param.htmlDesc !== undefined && ( - <div - className="note" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }} - /> + <SafeHTMLInjection + htmlAsString={param.htmlDesc} + sanitizeLevel={SanitizeLevel.FORBID_SVG_MATHML} + > + <div className="note" /> + </SafeHTMLInjection> )} </div> )) diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx index 57c8f242761..55d67cebfad 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import { components, OptionProps, OptionTypeBase, SingleValueProps } from 'react-select'; import { createRule, updateRule } from '../../../api/rules'; @@ -31,7 +32,7 @@ import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsEx import { RULE_STATUSES, RULE_TYPES } from '../../../helpers/constants'; import { csvEscape } from '../../../helpers/csv'; import { translate } from '../../../helpers/l10n'; -import { sanitizeString } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { latinize } from '../../../helpers/strings'; import { Dict, RuleDetails, RuleParameter } from '../../../types/types'; import { SeveritySelect } from './SeveritySelect'; @@ -317,11 +318,12 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat /> )} {param.htmlDesc !== undefined && ( - <div - className="modal-field-description" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }} - /> + <SafeHTMLInjection + htmlAsString={param.htmlDesc} + sanitizeLevel={SanitizeLevel.FORBID_SVG_MATHML} + > + <div className="modal-field-description" /> + </SafeHTMLInjection> )} </div> ); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx index a7fd2cb2c89..8fe97276bc6 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx @@ -17,13 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import { updateRule } from '../../../api/rules'; import FormattingTips from '../../../components/common/FormattingTips'; import { Button, ResetButtonLink } from '../../../components/controls/buttons'; import RuleTabViewer from '../../../components/rules/RuleTabViewer'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { sanitizeString, sanitizeUserInput } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { RuleDetails } from '../../../types/types'; import { RuleDescriptionSections } from '../rule'; import RemoveExtendedDescriptionModal from './RemoveExtendedDescriptionModal'; @@ -112,14 +113,14 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S renderExtendedDescription = () => ( <div id="coding-rules-detail-description-extra"> {this.props.ruleDetails.htmlNote !== undefined && ( - <div - className="rule-desc spacer-bottom markdown" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ - __html: sanitizeUserInput(this.props.ruleDetails.htmlNote), - }} - /> + <SafeHTMLInjection + htmlAsString={this.props.ruleDetails.htmlNote} + sanitizeLevel={SanitizeLevel.USER_INPUT} + > + <div className="rule-desc spacer-bottom markdown" /> + </SafeHTMLInjection> )} + {this.props.canWrite && ( <Button id="coding-rules-detail-extend-description" @@ -216,23 +217,28 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S return ( <div className="js-rule-description"> {defaultSection && ( - <section - className="coding-rules-detail-description markdown" - key={defaultSection.key} - /* eslint-disable-next-line react/no-danger */ - dangerouslySetInnerHTML={{ __html: sanitizeString(defaultSection.content) }} - /> + <SafeHTMLInjection + htmlAsString={defaultSection.content} + sanitizeLevel={SanitizeLevel.FORBID_SVG_MATHML} + > + <section + className="coding-rules-detail-description markdown" + key={defaultSection.key} + /> + </SafeHTMLInjection> )} {hasDescriptionSection && !defaultSection && ( <> {introductionSection && ( - <div - className="rule-desc" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeString(introductionSection) }} - /> + <SafeHTMLInjection + htmlAsString={introductionSection} + sanitizeLevel={SanitizeLevel.FORBID_SVG_MATHML} + > + <div className="rule-desc" /> + </SafeHTMLInjection> )} + <RuleTabViewer ruleDetails={ruleDetails} /> </> )} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx index 512219441a3..50a50c4adb5 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx @@ -17,9 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import { translate } from '../../../helpers/l10n'; -import { sanitizeString } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { RuleParameter } from '../../../types/types'; interface Props { @@ -30,13 +31,17 @@ export default class RuleDetailsParameters extends React.PureComponent<Props> { renderParameter = (param: RuleParameter) => ( <tr className="coding-rules-detail-parameter" key={param.key}> <td className="coding-rules-detail-parameter-name">{param.key}</td> + <td className="coding-rules-detail-parameter-description"> {param.htmlDesc !== undefined && ( - <p - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }} - /> + <SafeHTMLInjection + htmlAsString={param.htmlDesc} + sanitizeLevel={SanitizeLevel.FORBID_SVG_MATHML} + > + <p /> + </SafeHTMLInjection> )} + {param.defaultValue !== undefined && ( <div className="note spacer-top"> {translate('coding_rules.parameters.default_value')} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap index 218e1a3d16e..680fb4d5d02 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap @@ -160,14 +160,14 @@ exports[`should render correctly: default 1`] = ` type="text" value="1" /> - <div - className="note" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="note" + /> + </SafeHTMLInjection> </div> <div className="modal-field" @@ -281,14 +281,14 @@ exports[`should render correctly: submitting 1`] = ` type="text" value="1" /> - <div - className="note" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="note" + /> + </SafeHTMLInjection> </div> <div className="modal-field" @@ -400,14 +400,14 @@ exports[`should render correctly: update mode 1`] = ` type="text" value="1" /> - <div - className="note" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="note" + /> + </SafeHTMLInjection> </div> <div className="modal-field" @@ -555,14 +555,14 @@ exports[`should render correctly: with deep profiles 1`] = ` type="text" value="1" /> - <div - className="note" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="note" + /> + </SafeHTMLInjection> </div> <div className="modal-field" diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap index 77ad50e79a5..c376249c83a 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap @@ -210,14 +210,14 @@ exports[`should handle re-activation 1`] = ` type="text" value="" /> - <div - className="modal-field-description" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="modal-field-description" + /> + </SafeHTMLInjection> </div> <div className="modal-field" @@ -465,14 +465,14 @@ exports[`should render correctly: default 1`] = ` type="text" value="" /> - <div - className="modal-field-description" - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <div + className="modal-field-description" + /> + </SafeHTMLInjection> </div> <div className="modal-field" diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap index 1c441e4738e..b6f155022ac 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap @@ -25,13 +25,12 @@ exports[`should render correctly 1`] = ` <td className="coding-rules-detail-parameter-description" > - <p - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <p /> + </SafeHTMLInjection> <div className="note spacer-top" > @@ -57,13 +56,12 @@ exports[`should render correctly 1`] = ` <td className="coding-rules-detail-parameter-description" > - <p - dangerouslySetInnerHTML={ - { - "__html": "description", - } - } - /> + <SafeHTMLInjection + htmlAsString="description" + sanitizeLevel={1} + > + <p /> + </SafeHTMLInjection> <div className="note spacer-top" > diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx index f0dfe918a19..d08a688e1d2 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import classNames from 'classnames'; import * as React from 'react'; import { Button, ButtonLink, DeleteButton, EditButton } from '../../../components/controls/buttons'; @@ -27,7 +28,7 @@ import IssueChangelogDiff from '../../../components/issue/components/IssueChange import Avatar from '../../../components/ui/Avatar'; import { PopupPlacement } from '../../../components/ui/popups'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { sanitizeUserInput } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { Hotspot, ReviewHistoryType } from '../../../types/security-hotspots'; import { getHotspotReviewHistory } from '../utils'; import HotspotCommentPopup from './HotspotCommentPopup'; @@ -103,11 +104,10 @@ export default function HotspotReviewHistory(props: HotspotReviewHistoryProps) { {type === ReviewHistoryType.Comment && key && html && markdown && ( <div className="spacer-top display-flex-space-between"> - <div - className="markdown" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeUserInput(html) }} - /> + <SafeHTMLInjection htmlAsString={html} sanitizeLevel={SanitizeLevel.USER_INPUT}> + <div className="markdown" /> + </SafeHTMLInjection> + {updatable && ( <div> <div className="dropdown"> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap index 98e92766989..01b25206020 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap @@ -113,14 +113,14 @@ exports[`should render correctly: default 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -155,14 +155,14 @@ exports[`should render correctly: default 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -197,14 +197,14 @@ exports[`should render correctly: default 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> </ul> @@ -358,14 +358,14 @@ exports[`should render correctly: show full list 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -400,14 +400,14 @@ exports[`should render correctly: show full list 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -442,14 +442,14 @@ exports[`should render correctly: show full list 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -484,14 +484,14 @@ exports[`should render correctly: show full list 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> </div> </li> <li @@ -526,14 +526,14 @@ exports[`should render correctly: show full list 1`] = ` <div className="spacer-top display-flex-space-between" > - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "<strong>TEST</strong>", - } - } - /> + <SafeHTMLInjection + htmlAsString="<strong>TEST</strong>" + sanitizeLevel={2} + > + <div + className="markdown" + /> + </SafeHTMLInjection> <div> <div className="dropdown" diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx index e6943d126bf..d7b134d53db 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx @@ -17,12 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import { Location } from '../../../components/hoc/withRouter'; import { Alert } from '../../../components/ui/Alert'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; -import { sanitizeUserInput } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { getReturnUrl } from '../../../helpers/urls'; import { IdentityProvider } from '../../../types/types'; import './Login.css'; @@ -59,11 +60,9 @@ export default function Login(props: LoginProps) { )} {message && ( - <div - className="login-message markdown big-padded spacer-top huge-spacer-bottom" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeUserInput(message) }} - /> + <SafeHTMLInjection htmlAsString={message} sanitizeLevel={SanitizeLevel.USER_INPUT}> + <div className="login-message markdown big-padded spacer-top huge-spacer-bottom" /> + </SafeHTMLInjection> )} {identityProviders.length > 0 && ( diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionRenderer.tsx index 13163d279ca..3a8a0c43eac 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionRenderer.tsx @@ -17,13 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import classNames from 'classnames'; import * as React from 'react'; import Tooltip from '../../../components/controls/Tooltip'; import AlertErrorIcon from '../../../components/icons/AlertErrorIcon'; import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { sanitizeStringRestricted } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { ExtendedSettingDefinition, SettingValue } from '../../../types/settings'; import { combineDefinitionAndSettingValue, @@ -52,7 +53,7 @@ export interface DefinitionRendererProps { const formNoop = (e: React.FormEvent<HTMLFormElement>) => e.preventDefault(); -export default function DefinitionRenderer(props: DefinitionRendererProps) { +export default function DefinitionRenderer(props: Readonly<DefinitionRendererProps>) { const { changedValue, loading, validationMessage, settingValue, success, definition, isEditing } = props; @@ -78,11 +79,9 @@ export default function DefinitionRenderer(props: DefinitionRendererProps) { </h3> {description && ( - <div - className="markdown small spacer-top" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeStringRestricted(description) }} - /> + <SafeHTMLInjection htmlAsString={description} sanitizeLevel={SanitizeLevel.RESTRICTED}> + <div className="markdown small spacer-top" /> + </SafeHTMLInjection> )} <Tooltip overlay={translateWithParameters('settings.key_x', definition.key)}> diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx index 3267c22cdb9..65664b469ae 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import { groupBy, sortBy } from 'lodash'; import * as React from 'react'; import { Location, withRouter } from '../../../components/hoc/withRouter'; -import { sanitizeStringRestricted } from '../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../helpers/sanitize'; import { SettingDefinitionAndValue } from '../../../types/settings'; import { Component } from '../../../types/types'; import { getSubCategoryDescription, getSubCategoryName } from '../utils'; @@ -91,15 +92,16 @@ export class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryD {subCategory.name} </h2> )} + {subCategory.description != null && ( - <div - className="settings-sub-category-description markdown" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ - __html: sanitizeStringRestricted(subCategory.description), - }} - /> + <SafeHTMLInjection + htmlAsString={subCategory.description} + sanitizeLevel={SanitizeLevel.RESTRICTED} + > + <div className="settings-sub-category-description markdown" /> + </SafeHTMLInjection> )} + <DefinitionsList component={component} scrollToDefinition={this.scrollToSubCategoryOrDefinition} diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap index 021e807d1af..31031f47af8 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap @@ -14,14 +14,14 @@ exports[`should render correctly: changed value 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > @@ -105,14 +105,14 @@ exports[`should render correctly: in error 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > @@ -205,14 +205,14 @@ exports[`should render correctly: loading 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > @@ -303,14 +303,14 @@ exports[`should render correctly: original value 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > @@ -395,14 +395,14 @@ exports[`should render correctly: success 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > @@ -493,14 +493,14 @@ exports[`should render correctly: with description 1`] = ` > property.foo.name </h3> - <div - className="markdown small spacer-top" - dangerouslySetInnerHTML={ - { - "__html": "property.foo.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.foo.description" + sanitizeLevel={3} + > + <div + className="markdown small spacer-top" + /> + </SafeHTMLInjection> <Tooltip overlay="settings.key_x.foo" > diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap index 9e70241ec02..2e7ab454375 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap @@ -13,14 +13,14 @@ exports[`should render correctly 1`] = ` > property.category.general.email </h2> - <div - className="settings-sub-category-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "property.category.general.email.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.category.general.email.description" + sanitizeLevel={3} + > + <div + className="settings-sub-category-description markdown" + /> + </SafeHTMLInjection> <DefinitionsList scrollToDefinition={[Function]} settings={ @@ -55,14 +55,14 @@ exports[`should render correctly 1`] = ` > property.category.general.qg </h2> - <div - className="settings-sub-category-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "property.category.general.qg.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.category.general.qg.description" + sanitizeLevel={3} + > + <div + className="settings-sub-category-description markdown" + /> + </SafeHTMLInjection> <DefinitionsList scrollToDefinition={[Function]} settings={ @@ -101,14 +101,14 @@ exports[`should render correctly: subcategory 1`] = ` > property.category.general.qg </h2> - <div - className="settings-sub-category-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "property.category.general.qg.description", - } - } - /> + <SafeHTMLInjection + htmlAsString="property.category.general.qg.description" + sanitizeLevel={3} + > + <div + className="settings-sub-category-description markdown" + /> + </SafeHTMLInjection> <DefinitionsList scrollToDefinition={[Function]} settings={ diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForFormattedText.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForFormattedText.tsx index 56ed64d587a..699a9fc56da 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForFormattedText.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForFormattedText.tsx @@ -17,12 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import FormattingTipsWithLink from '../../../../components/common/FormattingTipsWithLink'; import { Button } from '../../../../components/controls/buttons'; import EditIcon from '../../../../components/icons/EditIcon'; import { translate } from '../../../../helpers/l10n'; -import { sanitizeUserInput } from '../../../../helpers/sanitize'; +import { SafeHTMLInjection, SanitizeLevel } from '../../../../helpers/sanitize'; import { DefaultSpecializedInputProps } from '../../utils'; export default function InputForFormattedText(props: DefaultSpecializedInputProps) { @@ -51,11 +52,13 @@ export default function InputForFormattedText(props: DefaultSpecializedInputProp </div> ) : ( <> - <div - className="markdown-preview markdown" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: sanitizeUserInput(formattedValue ?? '') }} - /> + <SafeHTMLInjection + htmlAsString={formattedValue ?? ''} + sanitizeLevel={SanitizeLevel.USER_INPUT} + > + <div className="markdown-preview markdown" /> + </SafeHTMLInjection> + <Button className="spacer-top" onClick={props.onEditing}> <EditIcon className="spacer-right" /> {translate('edit')} diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx index 3d0ce681a53..691e2118a82 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/Action.tsx @@ -17,11 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import classNames from 'classnames'; import * as React from 'react'; import Link from '../../../components/common/Link'; import LinkIcon from '../../../components/icons/LinkIcon'; import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { SafeHTMLInjection } from '../../../helpers/sanitize'; import { queryToSearch } from '../../../helpers/urls'; import { WebApi } from '../../../types/types'; import { getActionKey, serializeQuery } from '../utils'; @@ -177,11 +179,9 @@ export default class Action extends React.PureComponent<Props, State> { </header> <div className="boxed-group-inner"> - <div - className="web-api-action-description markdown" - // Safe: comes from the backend - dangerouslySetInnerHTML={{ __html: action.description }} - /> + <SafeHTMLInjection htmlAsString={action.description}> + <div className="web-api-action-description markdown" /> + </SafeHTMLInjection> {this.renderTabs()} diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx index aa072b597ce..d7fed0b0053 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; +import { SafeHTMLInjection } from '../../../helpers/sanitize'; import { WebApi } from '../../../types/types'; import { actionsFilter, getActionKey, Query } from '../utils'; import Action from './Action'; @@ -51,11 +53,9 @@ export default function Domain({ domain, query }: Props) { </header> {domain.description && ( - <div - className="web-api-domain-description markdown" - // Safe: comes from the backend - dangerouslySetInnerHTML={{ __html: domain.description }} - /> + <SafeHTMLInjection htmlAsString={domain.description}> + <div className="web-api-domain-description markdown" /> + </SafeHTMLInjection> )} <div className="web-api-domain-actions"> diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx b/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx index 71af96386ce..da31aa4a883 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/Params.tsx @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import * as React from 'react'; import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { SafeHTMLInjection } from '../../../helpers/sanitize'; import { WebApi } from '../../../types/types'; import DeprecatedBadge from './DeprecatedBadge'; import InternalBadge from './InternalBadge'; @@ -98,11 +100,9 @@ export default class Params extends React.PureComponent<Props> { {this.renderKey(param)} <td> - <div - className="markdown" - // Safe: comes from the backend - dangerouslySetInnerHTML={{ __html: param.description }} - /> + <SafeHTMLInjection htmlAsString={param.description}> + <div className="markdown" /> + </SafeHTMLInjection> </td> <td style={{ width: 250 }}> diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap index 6a646251d00..0e9b4f2e5f9 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap @@ -119,14 +119,13 @@ exports[`should render correctly 1`] = ` <div className="boxed-group-inner" > - <div - className="web-api-action-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "Foo Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="Foo Desc" + > + <div + className="web-api-action-description markdown" + /> + </SafeHTMLInjection> <ul className="web-api-action-actions tabs" > diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.tsx.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.tsx.snap index 36349a995c9..38aa9c97656 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.tsx.snap @@ -13,14 +13,13 @@ exports[`should also render actions with a description matching the query 1`] = api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" > @@ -139,14 +138,13 @@ exports[`should not render deprecated actions 1`] = ` api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" /> @@ -166,14 +164,13 @@ exports[`should not render internal actions 1`] = ` api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" /> @@ -193,14 +190,13 @@ exports[`should render deprecated actions 1`] = ` api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" > @@ -256,14 +252,13 @@ exports[`should render internal actions 1`] = ` api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" > @@ -317,14 +312,13 @@ exports[`should render only actions matching the query 1`] = ` api </h2> </header> - <div - className="web-api-domain-description markdown" - dangerouslySetInnerHTML={ - { - "__html": "API Desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="API Desc" + > + <div + className="web-api-domain-description markdown" + /> + </SafeHTMLInjection> <div className="web-api-domain-actions" > diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Params-test.tsx.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Params-test.tsx.snap index c38b355494b..338fc2de1f2 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Params-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Params-test.tsx.snap @@ -44,14 +44,13 @@ exports[`should render deprecated and internal parameters 1`] = ` </div> </td> <td> - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "Foo desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="Foo desc" + > + <div + className="markdown" + /> + </SafeHTMLInjection> </td> <td style={ @@ -94,14 +93,13 @@ exports[`should render deprecated and internal parameters 1`] = ` </div> </td> <td> - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "Foo desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="Foo desc" + > + <div + className="markdown" + /> + </SafeHTMLInjection> </td> <td style={ @@ -163,14 +161,13 @@ exports[`should render deprecated key 1`] = ` </div> </td> <td> - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "Foo desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="Foo desc" + > + <div + className="markdown" + /> + </SafeHTMLInjection> </td> <td style={ @@ -212,14 +209,13 @@ exports[`should render different value constraints 1`] = ` </div> </td> <td> - <div - className="markdown" - dangerouslySetInnerHTML={ - { - "__html": "Foo desc", - } - } - /> + <SafeHTMLInjection + htmlAsString="Foo desc" + > + <div + className="markdown" + /> + </SafeHTMLInjection> </td> <td style={ |