From 43af64decffceecfbb81d712667412e5935445fd Mon Sep 17 00:00:00 2001 From: David Cho-Lerat Date: Mon, 25 Nov 2024 11:58:43 +0100 Subject: SONAR-23741 Backport fixes for SSF-656 & SSF-657 --- server/sonar-web/.eslintrc | 24 +- .../app/components/extensions/exposeLibraries.ts | 5 +- .../main/js/app/components/search/SearchResult.tsx | 11 +- .../__snapshots__/SearchResult-test.tsx.snap | 15 +- .../components/ActivationFormModal.tsx | 14 +- .../components/CustomRuleFormModal.tsx | 14 +- .../components/RuleDetailsDescription.tsx | 44 +-- .../components/RuleDetailsParameters.tsx | 15 +- .../ActivationFormModal-test.tsx.snap | 64 ++--- .../CustomRuleFormModal-test.tsx.snap | 32 +-- .../RuleDetailsParameters-test.tsx.snap | 26 +- .../components/HotspotReviewHistory.tsx | 12 +- .../HotspotReviewHistory-test.tsx.snap | 128 ++++----- .../src/main/js/apps/sessions/components/Login.tsx | 11 +- .../settings/components/DefinitionRenderer.tsx | 13 +- .../components/SubCategoryDefinitionsList.tsx | 18 +- .../__snapshots__/DefinitionRenderer-test.tsx.snap | 96 +++---- .../SubCategoryDefinitionsList-test.tsx.snap | 48 ++-- .../components/inputs/InputForFormattedText.tsx | 15 +- .../src/main/js/apps/web-api/components/Action.tsx | 10 +- .../src/main/js/apps/web-api/components/Domain.tsx | 10 +- .../src/main/js/apps/web-api/components/Params.tsx | 10 +- .../__tests__/__snapshots__/Action-test.tsx.snap | 15 +- .../__tests__/__snapshots__/Domain-test.tsx.snap | 90 +++---- .../__tests__/__snapshots__/Params-test.tsx.snap | 60 ++--- .../js/components/common/AnalysisWarningsModal.tsx | 11 +- .../AnalysisWarningsModal-test.tsx.snap | 63 ++--- .../issue/components/IssueCommentLine.tsx | 13 +- .../__snapshots__/IssueCommentLine-test.tsx.snap | 48 ++-- .../js/components/issue/popups/CommentTile.tsx | 15 +- .../main/js/components/rules/RuleDescription.tsx | 41 +-- .../js/helpers/__tests__/code-difference-test.tsx | 12 +- .../src/main/js/helpers/__tests__/sanitize-test.ts | 160 ----------- .../main/js/helpers/__tests__/sanitize-test.tsx | 295 +++++++++++++++++++++ .../src/main/js/helpers/code-difference.ts | 6 +- server/sonar-web/src/main/js/helpers/sanitize.ts | 57 ---- server/sonar-web/src/main/js/helpers/sanitize.tsx | 124 +++++++++ 37 files changed, 930 insertions(+), 715 deletions(-) delete mode 100644 server/sonar-web/src/main/js/helpers/__tests__/sanitize-test.ts create mode 100644 server/sonar-web/src/main/js/helpers/__tests__/sanitize-test.tsx delete mode 100644 server/sonar-web/src/main/js/helpers/sanitize.ts create mode 100644 server/sonar-web/src/main/js/helpers/sanitize.tsx diff --git a/server/sonar-web/.eslintrc b/server/sonar-web/.eslintrc index ca97fff5e6d..f9fce6b0894 100644 --- a/server/sonar-web/.eslintrc +++ b/server/sonar-web/.eslintrc @@ -3,7 +3,29 @@ "rules": { "camelcase": "off", "promise/no-return-wrap": "warn", + "react/forbid-component-props": [ + "error", + { + "forbid": [ + { + "propName": "dangerouslySetInnerHTML", + "message": "Use the SafeHTMLInjection component instead of 'dangerouslySetInnerHTML', to prevent CSS injection along other XSS attacks" + } + ] + } + ], + "react/forbid-dom-props": [ + "error", + { + "forbid": [ + { + "propName": "dangerouslySetInnerHTML", + "message": "Use the SafeHTMLInjection component instead of 'dangerouslySetInnerHTML', to prevent CSS injection along other XSS attacks" + } + ] + } + ], "react/jsx-curly-brace-presence": "warn", "testing-library/render-result-naming-convention": "off" } -} +} \ No newline at end of file diff --git a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts index 1f58dcb7458..7ac527b9b18 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts @@ -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 { FormattedMessage } from 'react-intl'; import NotFound from '../../../app/components/NotFound'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; @@ -103,7 +104,7 @@ import { postJSONBody, request, } from '../../../helpers/request'; -import { sanitizeStringRestricted } from '../../../helpers/sanitize'; +import { sanitizeHTMLRestricted } from '../../../helpers/sanitize'; import { getStandards, renderCWECategory, @@ -166,7 +167,7 @@ const exposeLibraries = () => { getComponentSecurityHotspotsUrl, getMeasureHistoryUrl, getRulesUrl, - sanitizeStringRestricted, + sanitizeStringRestricted: sanitizeHTMLRestricted, }; }, }); diff --git a/server/sonar-web/src/main/js/app/components/search/SearchResult.tsx b/server/sonar-web/src/main/js/app/components/search/SearchResult.tsx index 1a7b7f91856..0035fe6e482 100644 --- a/server/sonar-web/src/main/js/app/components/search/SearchResult.tsx +++ b/server/sonar-web/src/main/js/app/components/search/SearchResult.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 * as React from 'react'; import Link from '../../../components/common/Link'; import ClockIcon from '../../../components/icons/ClockIcon'; import FavoriteIcon from '../../../components/icons/FavoriteIcon'; import QualifierIcon from '../../../components/icons/QualifierIcon'; +import { SafeHTMLInjection } from '../../../helpers/sanitize'; import { getComponentOverviewUrl } from '../../../helpers/urls'; import { ComponentResult } from './utils'; @@ -60,12 +62,9 @@ export default class SearchResult extends React.PureComponent { {component.match ? ( - + + + ) : ( {component.name} )} diff --git a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.tsx.snap b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.tsx.snap index 18f2646901e..df492ffecc1 100644 --- a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.tsx.snap @@ -80,14 +80,13 @@ exports[`renders match 1`] = ` qualifier="TRK" /> - oo", - } - } - /> + + +
)} {param.htmlDesc !== undefined && ( -
+ +
+ )}
)) 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 )} {param.htmlDesc !== undefined && ( -
+ +
+ )}
); 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 (
{this.props.ruleDetails.htmlNote !== undefined && ( -
+ +
+ )} + {this.props.canWrite && (