summaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2019-06-17 18:05:43 +0200
committersonartech <sonartech@sonarsource.com>2019-06-28 08:45:43 +0200
commitd835fdaa1d594451b554af805db9869134c716df (patch)
tree7376c601320e5cb687df7e921c5d151972639796 /server/sonar-web
parent73e1d5bb27a020277499fe0be5d059184c59c233 (diff)
downloadsonarqube-d835fdaa1d594451b554af805db9869134c716df.tar.gz
sonarqube-d835fdaa1d594451b554af805db9869134c716df.zip
SONAR-12196 Fix SSF-79
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/package.json3
-rw-r--r--server/sonar-web/src/main/js/app/components/search/SearchResult.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx21
-rw-r--r--server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap19
-rw-r--r--server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx33
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap83
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/App.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap76
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts105
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/Definition.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx30
-rw-r--r--server/sonar-web/src/main/js/apps/settings/utils.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx48
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap51
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap63
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap90
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap90
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap17
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap63
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap60
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Action.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Params.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx17
-rw-r--r--server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx21
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx1
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts12
-rw-r--r--server/sonar-web/yarn.lock38
46 files changed, 990 insertions, 307 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index 652397674a7..6a94ebb3ea1 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -5,6 +5,7 @@
"repository": "SonarSource/sonarqube",
"license": "LGPL-3.0",
"dependencies": {
+ "@types/dompurify": "^0.0.32",
"classnames": "2.2.6",
"clipboard": "2.0.1",
"core-js": "3.0.0",
@@ -16,6 +17,7 @@
"d3-shape": "1.2.2",
"d3-zoom": "1.7.3",
"date-fns": "1.29.0",
+ "dompurify": "^1.0.11",
"formik": "1.2.0",
"history": "3.3.0",
"intl-relativeformat": "2.1.0",
@@ -78,6 +80,7 @@
"@types/react-router": "3.0.20",
"@types/react-select": "1.2.6",
"@types/react-virtualized": "9.21.0",
+ "@types/sanitize-html": "1.20.0",
"@types/valid-url": "1.0.2",
"@typescript-eslint/parser": "1.5.0",
"autoprefixer": "9.5.0",
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 5ffab5ec6a4..20fd40ae953 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
@@ -137,6 +137,7 @@ export default class SearchResult extends React.PureComponent<Props, State> {
{component.match ? (
<span
className="navbar-search-item-match"
+ // Safe: comes from the backend
dangerouslySetInnerHTML={{ __html: component.match }}
/>
) : (
diff --git a/server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx b/server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx
index 1c3932538f3..d528a1a7369 100644
--- a/server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx
+++ b/server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx
@@ -18,9 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import * as classNames from 'classnames';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
interface Props {
allowMore: boolean;
@@ -61,15 +62,15 @@ export default class SearchShowMore extends React.PureComponent<Props> {
href="#"
onClick={this.handleMoreClick}
onMouseEnter={this.handleMoreMouseEnter}>
- <div
- className="pull-right text-muted-2 menu-footer-note"
- dangerouslySetInnerHTML={{
- __html: translateWithParameters(
- 'search.show_more.hint',
- '<span class="shortcut-button shortcut-button-small">Enter</span>'
- )
- }}
- />
+ <div className="pull-right text-muted-2 menu-footer-note">
+ <FormattedMessage
+ defaultMessage={translate('search.show_more.hint')}
+ id={'search.show_more.hint'}
+ values={{
+ key: <span className="shortcut-button shortcut-button-small">Enter</span>
+ }}
+ />
+ </div>
<span>{translate('show_more')}</span>
</a>
</DeferredSpinner>
diff --git a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap
index 9a3d97e3982..71e30b63d9b 100644
--- a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap
@@ -19,12 +19,21 @@ exports[`should render 1`] = `
>
<div
className="pull-right text-muted-2 menu-footer-note"
- dangerouslySetInnerHTML={
- Object {
- "__html": "search.show_more.hint.<span class=\\"shortcut-button shortcut-button-small\\">Enter</span>",
+ >
+ <FormattedMessage
+ defaultMessage="search.show_more.hint"
+ id="search.show_more.hint"
+ values={
+ Object {
+ "key": <span
+ className="shortcut-button shortcut-button-small"
+ >
+ Enter
+ </span>,
+ }
}
- }
- />
+ />
+ </div>
<span>
show_more
</span>
diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx b/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx
index 9e4e5f939bc..4c8f05066db 100644
--- a/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx
+++ b/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx
@@ -158,7 +158,11 @@ export class AboutApp extends React.PureComponent<Props, State> {
</div>
{customText && (
- <div className="about-page-section" dangerouslySetInnerHTML={{ __html: customText }} />
+ <div
+ className="about-page-section"
+ // Safe: Defined by instance admin
+ dangerouslySetInnerHTML={{ __html: customText }}
+ />
)}
<AboutLanguages />
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 ac5b6325bb9..490b971bab5 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
@@ -224,6 +224,7 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
)}
<div
className="note"
+ // Safe: defined by rule creator (instance admin?)
dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }}
/>
</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 afb3a99881f..42b022b64e7 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
@@ -304,6 +304,7 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
)}
<div
className="modal-field-description"
+ // Safe: defined by rule creator (instance admin?)
dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }}
/>
</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 0f22be47412..fc0ec48363e 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
@@ -112,6 +112,7 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
{this.props.ruleDetails.htmlNote !== undefined && (
<div
className="rule-desc spacer-bottom markdown"
+ // Safe: defined by rule creator (instance admin?)
dangerouslySetInnerHTML={{ __html: this.props.ruleDetails.htmlNote }}
/>
)}
@@ -193,6 +194,7 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
{hasDescription ? (
<div
className="coding-rules-detail-description rule-desc markdown"
+ // Safe: defined by rule creator (instance admin?)
dangerouslySetInnerHTML={{ __html: ruleDetails.htmlDesc || '' }}
/>
) : (
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 2679b6f3fd8..57f42d438a1 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
@@ -29,7 +29,9 @@ export default class RuleDetailsParameters extends React.PureComponent<Props> {
<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">
- <p dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }} />
+ <p // Safe: defined by rule creator (instance admin?)
+ dangerouslySetInnerHTML={{ __html: param.htmlDesc || '' }}
+ />
{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__/RuleDetailsParameters-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx
new file mode 100644
index 00000000000..1e017749c54
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * 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 { shallow } from 'enzyme';
+import RuleDetailsParameters from '../RuleDetailsParameters';
+import { mockRuleDetailsParameter } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<RuleDetailsParameters['props']> = {}) {
+ const params = [mockRuleDetailsParameter(), mockRuleDetailsParameter()];
+
+ return shallow(<RuleDetailsParameters params={params} {...props} />);
+}
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
new file mode 100644
index 00000000000..374a33057d7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap
@@ -0,0 +1,83 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="js-rule-parameters"
+>
+ <h3
+ className="coding-rules-detail-title"
+ >
+ coding_rules.parameters
+ </h3>
+ <table
+ className="coding-rules-detail-parameters"
+ >
+ <tbody>
+ <tr
+ className="coding-rules-detail-parameter"
+ key="1"
+ >
+ <td
+ className="coding-rules-detail-parameter-name"
+ >
+ 1
+ </td>
+ <td
+ className="coding-rules-detail-parameter-description"
+ >
+ <p
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "description",
+ }
+ }
+ />
+ <div
+ className="note spacer-top"
+ >
+ coding_rules.parameters.default_value
+ <br />
+ <span
+ className="coding-rules-detail-parameter-value"
+ >
+ 1
+ </span>
+ </div>
+ </td>
+ </tr>
+ <tr
+ className="coding-rules-detail-parameter"
+ key="1"
+ >
+ <td
+ className="coding-rules-detail-parameter-name"
+ >
+ 1
+ </td>
+ <td
+ className="coding-rules-detail-parameter-description"
+ >
+ <p
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "description",
+ }
+ }
+ />
+ <div
+ className="note spacer-top"
+ >
+ coding_rules.parameters.default_value
+ <br />
+ <span
+ className="coding-rules-detail-parameter-value"
+ >
+ 1
+ </span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
index d63d251974a..2ca069c1a46 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
@@ -119,6 +119,7 @@ export default class IssuesSourceViewer extends React.PureComponent<Props> {
const component = selectedLocation ? selectedLocation.component : openIssue.component;
const allMessagesEmpty =
locations !== undefined && locations.every(location => !location.msg);
+
const highlightedLocations = locations.filter(location => location.component === component);
// do not load issues when open another file for a location
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
index fd091c1c940..c1213f0f1d6 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import Helmet from 'react-helmet';
+import { FormattedMessage } from 'react-intl';
+import * as classNames from 'classnames';
import { getMigrationStatus, getSystemStatus, migrateDatabase } from '../../../api/system';
import DateFromNow from '../../../components/intl/DateFromNow';
import TimeFormatter from '../../../components/intl/TimeFormatter';
@@ -201,18 +202,32 @@ export default class App extends React.PureComponent<Props, State> {
</h1>
{!isSonarCloud() && (
<>
- <p
- className="maintenance-text"
- dangerouslySetInnerHTML={{
- __html: translate('maintenance.sonarqube_is_under_maintenance.1')
- }}
- />
- <p
- className="maintenance-text"
- dangerouslySetInnerHTML={{
- __html: translate('maintenance.sonarqube_is_under_maintenance.2')
- }}
- />
+ <p className="maintenance-text">
+ <FormattedMessage
+ defaultMessage={translate('maintenance.sonarqube_is_under_maintenance.1')}
+ id="maintenance.sonarqube_is_under_maintenance.1"
+ values={{
+ link: (
+ <a href="https://redirect.sonarsource.com/doc/plugin-library.html">
+ {translate('maintenance.sonarqube_is_under_maintenance_link.1')}
+ </a>
+ )
+ }}
+ />
+ </p>
+ <p className="maintenance-text">
+ <FormattedMessage
+ defaultMessage={translate('maintenance.sonarqube_is_under_maintenance.2')}
+ id="maintenance.sonarqube_is_under_maintenance.2"
+ values={{
+ link: (
+ <a href="https://redirect.sonarsource.com/doc/upgrading.html">
+ {translate('maintenance.sonarqube_is_under_maintenance_link.2')}
+ </a>
+ )
+ }}
+ />
+ </p>
</>
)}
</>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
index 7c718fdcb7e..e0606efd1df 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -24,20 +24,38 @@ exports[`Maintenance Page should render DB_MIGRATION_NEEDED status 1`] = `
</h1>
<p
className="maintenance-text"
- dangerouslySetInnerHTML={
- Object {
- "__html": "maintenance.sonarqube_is_under_maintenance.1",
+ >
+ <FormattedMessage
+ defaultMessage="maintenance.sonarqube_is_under_maintenance.1"
+ id="maintenance.sonarqube_is_under_maintenance.1"
+ values={
+ Object {
+ "link": <a
+ href="https://redirect.sonarsource.com/doc/plugin-library.html"
+ >
+ maintenance.sonarqube_is_under_maintenance_link.1
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="maintenance-text"
- dangerouslySetInnerHTML={
- Object {
- "__html": "maintenance.sonarqube_is_under_maintenance.2",
+ >
+ <FormattedMessage
+ defaultMessage="maintenance.sonarqube_is_under_maintenance.2"
+ id="maintenance.sonarqube_is_under_maintenance.2"
+ values={
+ Object {
+ "link": <a
+ href="https://redirect.sonarsource.com/doc/upgrading.html"
+ >
+ maintenance.sonarqube_is_under_maintenance_link.2
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
</div>
</Fragment>
@@ -67,20 +85,38 @@ exports[`Maintenance Page should render DB_MIGRATION_RUNNING status 1`] = `
</h1>
<p
className="maintenance-text"
- dangerouslySetInnerHTML={
- Object {
- "__html": "maintenance.sonarqube_is_under_maintenance.1",
+ >
+ <FormattedMessage
+ defaultMessage="maintenance.sonarqube_is_under_maintenance.1"
+ id="maintenance.sonarqube_is_under_maintenance.1"
+ values={
+ Object {
+ "link": <a
+ href="https://redirect.sonarsource.com/doc/plugin-library.html"
+ >
+ maintenance.sonarqube_is_under_maintenance_link.1
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="maintenance-text"
- dangerouslySetInnerHTML={
- Object {
- "__html": "maintenance.sonarqube_is_under_maintenance.2",
+ >
+ <FormattedMessage
+ defaultMessage="maintenance.sonarqube_is_under_maintenance.2"
+ id="maintenance.sonarqube_is_under_maintenance.2"
+ values={
+ Object {
+ "link": <a
+ href="https://redirect.sonarsource.com/doc/upgrading.html"
+ >
+ maintenance.sonarqube_is_under_maintenance_link.2
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
</div>
</Fragment>
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
index e27eda543b5..7dadf716969 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
@@ -21,6 +21,7 @@ import * as React from 'react';
import { setSimpleSettingValue, resetSettingValue } from '../../../api/settings';
import { Button, SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { sanitizeTranslation } from '../../settings/utils';
interface Props {
branch?: string;
@@ -96,7 +97,9 @@ export default class SettingForm extends React.PureComponent<Props, State> {
<div className="modal-body">
<div
className="big-spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: translate(`property.${setting.key}.description`) }}
+ dangerouslySetInnerHTML={{
+ __html: sanitizeTranslation(translate(`property.${setting.key}.description`))
+ }}
/>
<div className="modal-field">
<input
diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
index 5d4ae3ae9cd..642fb09c98f 100644
--- a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
@@ -17,7 +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 { getEmptyValue, getDefaultValue } from '../utils';
+import { getEmptyValue, getDefaultValue, sanitizeTranslation } from '../utils';
const fields = [
{ key: 'foo', type: 'STRING' } as T.SettingFieldDefinition,
@@ -76,3 +76,106 @@ describe('#getDefaultValue()', () => {
it('should work for boolean field when passing "false"', () =>
check('false', 'settings.boolean.false'));
});
+
+describe('sanitizeTranslation', () => {
+ it('should preserve formatting tags', () => {
+ const allowed = `
+ Hi this is <i>in italics</i> and <ul>
+ <li> lists </li>
+ <li> are allowed</li>
+ </ul>
+ <p>
+ as well. This is <b>Amazing</b> and this <strong>bold</strong> <br>
+ and <code>code.is.accepted too</code>
+ </p>
+ `;
+
+ const clean = sanitizeTranslation(allowed);
+ expect(clean).toBe(allowed);
+ });
+
+ /*
+ * Test code borrowed from OWASP's sanitizer tests
+ * https://github.com/OWASP/java-html-sanitizer/blob/master/src/test/resources/org/owasp/html/htmllexerinput1.html
+ */
+ it('should strip everything else', () => {
+ const clean = sanitizeTranslation(`<?xml version="not-even-close"?>
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+ <!-- a test input for HtmlLexer -->
+
+ <html>
+ <head>
+ <title>Test File For HtmlLexer &amp; HtmlParser</title>
+ <link rel=stylesheet type="text/css" src=foo/bar.css />
+ <body
+ bgcolor=white
+ linkcolor = "blue"
+ onload="document.writeln(
+ &quot;&lt;p&gt;properly escaped code in a handler&lt;/p&gt;&quot;);"
+ >
+
+ <script type="text/javascript"><!--
+ document.writeln("<p>Some initialization code in global context</p>");
+ --></script>
+
+ <script type="text/javascript">
+ // hi there
+ document.writeln("<p>More initialization</p>");
+ </script>
+
+ <div id=clickydiv onclick="handleClicky(event)"
+ ondblclick=this.onclick(event);return(false)>
+ Clicky
+ </div>
+
+ <input id=foo>
+ <gxp:attr name="onchange">alert("&lt;b&gt;hi&lt;/b&gt;");</gxp:attr>
+ </input>
+
+ <pre>&lt;div id=notarealtag onclick=notcode()&gt;</pre>
+
+ <!-- some tokenization corner cases -->
+
+ < notatag <atag/>
+
+ </ notatag> </redundantlyclosed/>
+
+ <messyattributes a=b=c d="e"f=g h =i j= k l = m checked n="o"/>
+
+ < < < all in one text block > > >
+
+ <xmp>Make sure that <!-- comments don't obscure the xmp close</xmp>
+ <% # some php code here
+ write("<pre>$horriblySyntacticConstruct1</pre>\n\n");
+ %>
+ <script type="text/javascript"><!--
+ alert("hello world");
+ // --></script>
+
+ <script>/* </script> */alert('hi');</script>
+ <script><!--/* </script> */alert('hi');--></script>
+
+ <xmp style=color:blue><!--/* </xmp> */alert('hi');--></xmp>
+
+ <style><!-- p { contentf: '</style>' } --></style>
+ <style>Foo<!-- > </style> --></style>
+ <textarea><!-- Zoicks </textarea>--></textarea>
+ <!-- An escaping text span start may share its U+002D HYPHEN-MINUS characters
+ - with its corresponding escaping text span end. -->
+ <script><!--></script>
+ <script><!---></script>
+ <script><!----></script>
+ </body>
+ </html>
+ <![CDATA[ No such thing as a CDATA> section in HTML ]]>
+ <script>a<b</script>
+ <img src=foo.gif /><a href=><a href=/>
+ <span title=malformed attribs' do=don't id=foo checked onclick="a<b">Bar</span>`);
+
+ expect(clean.replace(/\s+/g, '')).toBe(
+ `Clickyalert("&lt;b&gt;hi&lt;/b&gt;");&lt;divid=notarealtagonclick=notcode()&gt;&lt;notatag&lt;&lt;&lt;allinonetextblock&gt;&gt;&gt;Makesurethat&lt;%#somephpcodeherewrite("$horriblySyntacticConstruct1");%&gt;*/alert('hi');*/alert('hi');--&gt;*/alert('hi');--&gt;'}--&gt;--&gt;&lt;!--Zoicks--&gt;sectioninHTML]]&gt;Bar`
+ );
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
index d17d9ead8a1..9be3fb3820b 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
@@ -26,7 +26,8 @@ import {
getPropertyName,
getPropertyDescription,
getSettingValue,
- isDefaultOrInherited
+ isDefaultOrInherited,
+ sanitizeTranslation
} from '../utils';
import AlertErrorIcon from '../../../components/icons-components/AlertErrorIcon';
import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
@@ -142,7 +143,7 @@ export class Definition extends React.PureComponent<Props, State> {
{description && (
<div
className="markdown small spacer-top"
- dangerouslySetInnerHTML={{ __html: description }}
+ dangerouslySetInnerHTML={{ __html: sanitizeTranslation(description) }}
/>
)}
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 9e5dc895b7b..7733c37c709 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
@@ -21,7 +21,7 @@ import * as React from 'react';
import { groupBy, isEqual, sortBy } from 'lodash';
import DefinitionsList from './DefinitionsList';
import EmailForm from './EmailForm';
-import { getSubCategoryName, getSubCategoryDescription } from '../utils';
+import { getSubCategoryName, getSubCategoryDescription, sanitizeTranslation } from '../utils';
interface Props {
category: string;
@@ -74,7 +74,7 @@ export default class SubCategoryDefinitionsList extends React.PureComponent<Prop
{subCategory.description != null && (
<div
className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={{ __html: subCategory.description }}
+ dangerouslySetInnerHTML={{ __html: sanitizeTranslation(subCategory.description) }}
/>
)}
<DefinitionsList
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
index a11187e7a45..84b235cd2fc 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
@@ -76,10 +76,32 @@ export default class GenerateSecretKeyForm extends React.PureComponent<Props, St
<ClipboardButton className="little-spacer-left" copyValue={secretKey} />
</div>
<h3 className="spacer-bottom">{translate('encryption.how_to_use')}</h3>
- <div
- className="markdown"
- dangerouslySetInnerHTML={{ __html: translate('encryption.how_to_use.content') }}
- />
+ <div className="markdown">
+ <ul>
+ <li>
+ <FormattedMessage
+ defaultMessage={translate('encryption.how_to_use.content1')}
+ id="encryption.how_to_use.content1"
+ values={{
+ secret_file: <code>~/.sonar/sonar-secret.txt</code>,
+ property: <code>sonar.secretKeyPath</code>,
+ propreties_file: <code>conf/sonar.properties</code>
+ }}
+ />
+ </li>
+ <li>{translate('encryption.how_to_use.content2')}</li>
+ <li>
+ <FormattedMessage
+ defaultMessage={translate('encryption.how_to_use.content3')}
+ id="encryption.how_to_use.content3"
+ values={{
+ property: <code>sonar.secretKeyPath</code>
+ }}
+ />
+ </li>
+ <li>{translate('encryption.how_to_use.content4')}</li>
+ </ul>
+ </div>
</>
) : (
<form id="generate-secret-key-form" onSubmit={this.handleSubmit}>
diff --git a/server/sonar-web/src/main/js/apps/settings/utils.ts b/server/sonar-web/src/main/js/apps/settings/utils.ts
index 09484304223..990a6933591 100644
--- a/server/sonar-web/src/main/js/apps/settings/utils.ts
+++ b/server/sonar-web/src/main/js/apps/settings/utils.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 { sanitize } from 'dompurify';
import { translate, hasMessage } from '../../helpers/l10n';
export const DEFAULT_CATEGORY = 'general';
@@ -35,6 +36,12 @@ export interface DefaultInputProps {
value: any;
}
+export function sanitizeTranslation(html: string) {
+ return sanitize(html, {
+ ALLOWED_TAGS: ['b', 'br', 'code', 'i', 'li', 'p', 'strong', 'ul']
+ });
+}
+
export function getPropertyName(definition: T.SettingDefinition) {
const key = `property.${definition.key}.name`;
return hasMessage(key) ? translate(key) : definition.name;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx
index 829c09c02d6..3f16344c420 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/urls';
@@ -38,12 +39,15 @@ export default function BuildWrapper(props: Props) {
<h4 className="spacer-bottom">
{translate('onboarding.analysis.build_wrapper.header', props.os)}
</h4>
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{
- __html: translate('onboarding.analysis.build_wrapper.text', props.os)
- }}
- />
+ <p className="spacer-bottom markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.build_wrapper.text')}
+ id="onboarding.analysis.build_wrapper.text"
+ values={{
+ env_var: <code>{props.os === 'win' ? '%PATH%' : 'PATH'}</code>
+ }}
+ />
+ </p>
<p>
<a
className="button"
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx
index c36574af7d8..43958932470 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import SQScanner from './SQScanner';
import BuildWrapper from './BuildWrapper';
import CodeSnippet from '../../../../components/common/CodeSnippet';
@@ -63,19 +64,26 @@ export default function ClangGCC(props: Props) {
{translate('onboarding.analysis.sq_scanner.execute')}
</h4>
<InstanceMessage message={translate('onboarding.analysis.sq_scanner.execute.text')}>
- {transformedMessage => (
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: transformedMessage }}
- />
- )}
+ {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
</InstanceMessage>
<CodeSnippet isOneLine={props.small} snippet={command1} />
<CodeSnippet isOneLine={props.os === 'win'} snippet={command2} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.sq_scanner.docs') }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.sq_scanner.docs')}
+ id="onboarding.analysis.sq_scanner.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.sq_scanner.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx
index 1bf82ddb0b3..a6fe98e0c2e 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import MSBuildScanner from './MSBuildScanner';
import CodeSnippet from '../../../../components/common/CodeSnippet';
import InstanceMessage from '../../../../components/common/InstanceMessage';
@@ -52,20 +53,27 @@ export default function DotNet(props: Props) {
{translate('onboarding.analysis.msbuild.execute')}
</h4>
<InstanceMessage message={translate('onboarding.analysis.msbuild.execute.text')}>
- {transformedMessage => (
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: transformedMessage }}
- />
- )}
+ {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
</InstanceMessage>
<CodeSnippet isOneLine={true} snippet={command1} />
<CodeSnippet isOneLine={false} snippet={command2} />
<CodeSnippet isOneLine={props.small} snippet={command3} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.docs') }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.docs')}
+ id="onboarding.analysis.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.msbuild.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx
index d33568362bb..dfe6ff42834 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import CodeSnippet from '../../../../components/common/CodeSnippet';
import InstanceMessage from '../../../../components/common/InstanceMessage';
import { translate } from '../../../../helpers/l10n';
@@ -45,10 +46,16 @@ export default function JavaGradle(props: Props) {
<h4 className="spacer-bottom">{translate('onboarding.analysis.java.gradle.header')}</h4>
<InstanceMessage message={translate('onboarding.analysis.java.gradle.text.1')}>
{transformedMessage => (
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: transformedMessage }}
- />
+ <p className="spacer-bottom markdown">
+ <FormattedMessage
+ defaultMessage={transformedMessage}
+ id="onboarding.analysis.java.gradle.text.1"
+ values={{
+ plugin_code: <code>org.sonarqube</code>,
+ filename: <code>build.gradle</code>
+ }}
+ />
+ </p>
)}
</InstanceMessage>
<CodeSnippet snippet={config} />
@@ -56,18 +63,27 @@ export default function JavaGradle(props: Props) {
{translate('onboarding.analysis.java.gradle.text.2')}
</p>
<CodeSnippet snippet={command} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.java.gradle.docs') }}
- />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{
- __html: props.projectKey
- ? translate('onboarding.analysis.auto_refresh_after_analysis')
- : translate('onboarding.analysis.browse_url_after_analysis')
- }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.docs')}
+ id="onboarding.analysis.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/gradle.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.java.gradle.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
+ <p className="big-spacer-top markdown">
+ {props.projectKey
+ ? translate('onboarding.analysis.auto_refresh_after_analysis')
+ : translate('onboarding.analysis.browse_url_after_analysis')}
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx
index 3bc233aa041..778796e0eb0 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import CodeSnippet from '../../../../components/common/CodeSnippet';
import InstanceMessage from '../../../../components/common/InstanceMessage';
import { translate } from '../../../../helpers/l10n';
@@ -45,18 +46,27 @@ export default function JavaMaven(props: Props) {
<InstanceMessage message={translate('onboarding.analysis.java.maven.text')} />
</p>
<CodeSnippet snippet={command} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.java.maven.docs') }}
- />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{
- __html: props.projectKey
- ? translate('onboarding.analysis.auto_refresh_after_analysis')
- : translate('onboarding.analysis.browse_url_after_analysis')
- }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.docs')}
+ id="onboarding.analysis.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.java.maven.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
+ <p className="big-spacer-top markdown">
+ {props.projectKey
+ ? translate('onboarding.analysis.auto_refresh_after_analysis')
+ : translate('onboarding.analysis.browse_url_after_analysis')}
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx
index 86ea954d2ca..02c69437a2c 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import { translate } from '../../../../helpers/l10n';
interface Props {
@@ -28,10 +29,13 @@ export default function MSBuildScanner(props: Props) {
return (
<div className={props.className}>
<h4 className="spacer-bottom">{translate('onboarding.analysis.msbuild.header')}</h4>
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.text') }}
- />
+ <p className="spacer-bottom markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.msbuild.text')}
+ id="onboarding.analysis.msbuild.text"
+ values={{ code: <code>%PATH%</code> }}
+ />
+ </p>
<p>
<a
className="button"
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx
index 5066cc1ede3..36fbab52778 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import MSBuildScanner from './MSBuildScanner';
import BuildWrapper from './BuildWrapper';
import CodeSnippet from '../../../../components/common/CodeSnippet';
@@ -55,20 +56,27 @@ export default function Msvc(props: Props) {
{translate('onboarding.analysis.msbuild.execute')}
</h4>
<InstanceMessage message={translate('onboarding.analysis.msbuild.execute.text')}>
- {transformedMessage => (
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: transformedMessage }}
- />
- )}
+ {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
</InstanceMessage>
<CodeSnippet isOneLine={true} snippet={command1} />
<CodeSnippet isOneLine={props.small} snippet={command2} />
<CodeSnippet isOneLine={props.small} snippet={command3} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.msbuild.docs') }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.docs')}
+ id="onboarding.analysis.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.msbuild.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx
index 8c4a6529ea7..c9445156179 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import SQScanner from './SQScanner';
import CodeSnippet from '../../../../components/common/CodeSnippet';
import InstanceMessage from '../../../../components/common/InstanceMessage';
@@ -51,18 +52,25 @@ export default function Other(props: Props) {
{translate('onboarding.analysis.sq_scanner.execute')}
</h4>
<InstanceMessage message={translate('onboarding.analysis.sq_scanner.execute.text')}>
- {transformedMessage => (
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{ __html: transformedMessage }}
- />
- )}
+ {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
</InstanceMessage>
<CodeSnippet isOneLine={props.os === 'win'} snippet={command} />
- <p
- className="big-spacer-top markdown"
- dangerouslySetInnerHTML={{ __html: translate('onboarding.analysis.sq_scanner.docs') }}
- />
+ <p className="big-spacer-top markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.sq_scanner.docs')}
+ id="onboarding.analysis.sq_scanner.docs"
+ values={{
+ link: (
+ <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('onboarding.analysis.sq_scanner.docs_link')}
+ </a>
+ )
+ }}
+ />
+ </p>
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx
index 0ce14360992..674b59e3d50 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import { translate } from '../../../../helpers/l10n';
interface Props {
@@ -31,12 +32,16 @@ export default function SQScanner(props: Props) {
<h4 className="spacer-bottom">
{translate('onboarding.analysis.sq_scanner.header', props.os)}
</h4>
- <p
- className="spacer-bottom markdown"
- dangerouslySetInnerHTML={{
- __html: translate('onboarding.analysis.sq_scanner.text', props.os)
- }}
- />
+ <p className="spacer-bottom markdown">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.analysis.sq_scanner.text')}
+ id="onboarding.analysis.sq_scanner.text"
+ values={{
+ dir: <code>bin</code>,
+ env_var: <code>{props.os === 'win' ? '%PATH%' : 'PATH'}</code>
+ }}
+ />
+ </p>
<p>
<a
className="button"
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap
index 07e8eb45f0a..85f98bb085d 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap
@@ -9,12 +9,19 @@ exports[`renders correctly 1`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.build_wrapper.text.win",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.build_wrapper.text"
+ id="onboarding.analysis.build_wrapper.text"
+ values={
+ Object {
+ "env_var": <code>
+ %PATH%
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
@@ -37,12 +44,19 @@ exports[`renders correctly 2`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.build_wrapper.text.linux",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.build_wrapper.text"
+ id="onboarding.analysis.build_wrapper.text"
+ values={
+ Object {
+ "env_var": <code>
+ PATH
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
@@ -65,12 +79,19 @@ exports[`renders correctly 3`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.build_wrapper.text.mac",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.build_wrapper.text"
+ id="onboarding.analysis.build_wrapper.text"
+ values={
+ Object {
+ "env_var": <code>
+ PATH
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
index 7a765aa9112..be959ee6158 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
@@ -38,12 +38,23 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -85,12 +96,23 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -133,11 +155,22 @@ exports[`renders correctly 3`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
index b296291fc1c..f455da670c8 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
@@ -39,12 +39,23 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.msbuild.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.msbuild.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -88,11 +99,22 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.msbuild.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.msbuild.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
index 55ca37b75f9..7e1208e8dce 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
@@ -35,20 +35,28 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.gradle.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/gradle.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.gradle.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.browse_url_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.browse_url_after_analysis
+ </p>
</div>
`;
@@ -87,20 +95,28 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.gradle.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/gradle.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.gradle.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.browse_url_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.browse_url_after_analysis
+ </p>
</div>
`;
@@ -139,19 +155,27 @@ exports[`renders with projectKey 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.gradle.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/gradle.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.gradle.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.auto_refresh_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.auto_refresh_after_analysis
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
index af3ff510ca6..7ee4d3becba 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
@@ -27,20 +27,28 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.maven.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.maven.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.browse_url_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.browse_url_after_analysis
+ </p>
</div>
`;
@@ -71,20 +79,28 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.maven.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.maven.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.browse_url_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.browse_url_after_analysis
+ </p>
</div>
`;
@@ -115,19 +131,27 @@ exports[`renders with projectKey 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.java.maven.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.java.maven.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.auto_refresh_after_analysis",
- }
- }
- />
+ >
+ onboarding.analysis.auto_refresh_after_analysis
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
index 740e26ed12b..094b637da72 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
@@ -9,12 +9,19 @@ exports[`renders correctly 1`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.msbuild.text",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.msbuild.text"
+ id="onboarding.analysis.msbuild.text"
+ values={
+ Object {
+ "code": <code>
+ %PATH%
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap
index 875ae78edc4..3181496e14b 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap
@@ -43,12 +43,23 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.msbuild.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.msbuild.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -97,11 +108,22 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.msbuild.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.docs"
+ id="onboarding.analysis.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.msbuild.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap
index 8fe780fe094..bcd28ca14b2 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap
@@ -30,12 +30,23 @@ exports[`renders correctly 1`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -69,12 +80,23 @@ exports[`renders correctly 2`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
@@ -108,11 +130,22 @@ exports[`renders correctly 3`] = `
/>
<p
className="big-spacer-top markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.docs",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.docs"
+ id="onboarding.analysis.sq_scanner.docs"
+ values={
+ Object {
+ "link": <a
+ href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.analysis.sq_scanner.docs_link
+ </a>,
+ }
}
- }
- />
+ />
+ </p>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
index cf6a8de4ce3..6ad4425a0a8 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
@@ -9,12 +9,22 @@ exports[`renders correctly 1`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.text.win",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.text"
+ id="onboarding.analysis.sq_scanner.text"
+ values={
+ Object {
+ "dir": <code>
+ bin
+ </code>,
+ "env_var": <code>
+ %PATH%
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
@@ -37,12 +47,22 @@ exports[`renders correctly 2`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.text.linux",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.text"
+ id="onboarding.analysis.sq_scanner.text"
+ values={
+ Object {
+ "dir": <code>
+ bin
+ </code>,
+ "env_var": <code>
+ PATH
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
@@ -65,12 +85,22 @@ exports[`renders correctly 3`] = `
</h4>
<p
className="spacer-bottom markdown"
- dangerouslySetInnerHTML={
- Object {
- "__html": "onboarding.analysis.sq_scanner.text.mac",
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.analysis.sq_scanner.text"
+ id="onboarding.analysis.sq_scanner.text"
+ values={
+ Object {
+ "dir": <code>
+ bin
+ </code>,
+ "env_var": <code>
+ PATH
+ </code>,
+ }
}
- }
- />
+ />
+ </p>
<p>
<a
className="button"
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 304f28eb2f7..bd6c63de706 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
@@ -171,6 +171,7 @@ export default class Action extends React.PureComponent<Props, State> {
<div className="boxed-group-inner">
<div
className="web-api-action-description markdown"
+ // Safe: comes from the backend
dangerouslySetInnerHTML={{ __html: action.description }}
/>
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 6df2b62096f..d7e4ab6ee4d 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
@@ -52,6 +52,7 @@ export default function Domain({ domain, query }: Props) {
{domain.description && (
<div
className="web-api-domain-description markdown"
+ // Safe: comes from the backend
dangerouslySetInnerHTML={{ __html: domain.description }}
/>
)}
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 5cf2d7c0a90..7dbf8b77860 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
@@ -99,6 +99,7 @@ export default class Params extends React.PureComponent<Props> {
<td>
<div
className="markdown"
+ // Safe: comes from the backend
dangerouslySetInnerHTML={{ __html: param.description }}
/>
</td>
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx b/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx
index 5d82b9c919a..bf21da958f5 100644
--- a/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx
+++ b/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx
@@ -84,10 +84,6 @@ export default class TreeMapRect extends React.PureComponent<Props> {
const isTextVisible = this.props.width >= 40 && this.props.height >= 45;
const isIconVisible = this.props.width >= 24 && this.props.height >= 26;
- const label = this.props.prefix
- ? `${this.props.prefix}<br>${this.props.label.substr(this.props.prefix.length)}`
- : this.props.label;
-
return (
<div
className="treemap-cell"
@@ -101,9 +97,16 @@ export default class TreeMapRect extends React.PureComponent<Props> {
{this.props.icon}
</span>
)}
- {isTextVisible && (
- <span className="treemap-text" dangerouslySetInnerHTML={{ __html: label }} />
- )}
+ {isTextVisible &&
+ (this.props.prefix ? (
+ <span className="treemap-text">
+ {this.props.prefix}
+ <br />
+ {this.props.label.substr(this.props.prefix.length)}
+ </span>
+ ) : (
+ <span className="treemap-text">{this.props.label}</span>
+ ))}
</div>
{this.renderLink()}
</div>
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx
index e6ae0718d07..0f5959e5fe2 100644
--- a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx
+++ b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx
@@ -18,17 +18,28 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import TreeMap from '../TreeMap';
+import TreeMapRect from '../TreeMapRect';
+import { mockEvent } from '../../../helpers/testMocks';
-it('should display', () => {
+it('should render correctly', () => {
const items = [
{ key: '1', size: 10, color: '#777', label: 'SonarQube :: Server' },
{ key: '2', size: 30, color: '#777', label: 'SonarQube :: Web' },
{ key: '3', size: 20, color: '#777', label: 'SonarQube :: Search' }
];
- const chart = shallow(
- <TreeMap height={100} items={items} onRectangleClick={() => {}} width={100} />
+ const onRectClick = jest.fn();
+ const chart = mount(
+ <TreeMap height={100} items={items} onRectangleClick={onRectClick} width={100} />
);
- expect(chart.find('TreeMapRect')).toHaveLength(3);
+ const rects = chart.find(TreeMapRect);
+ expect(rects).toHaveLength(3);
+
+ const event: React.MouseEvent<HTMLAnchorElement> = mockEvent({ stopPropagation: jest.fn() });
+ (rects.first().instance() as TreeMapRect).handleLinkClick(event);
+ expect(event.stopPropagation).toHaveBeenCalled();
+
+ (rects.first().instance() as TreeMapRect).handleRectClick();
+ expect(onRectClick).toHaveBeenCalledWith('2');
});
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx
index 66de3381072..09de825c56a 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx
@@ -89,6 +89,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State>
</div>
<div
className="issue-comment-text markdown"
+ // Safe: Comes from the backend, after markdown transformation to html
dangerouslySetInnerHTML={{ __html: comment.htmlText }}
/>
<div className="issue-comment-age">
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index 66c6a639e6f..9fd2c05f8f9 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -619,6 +619,18 @@ export function mockRuleDetails(overrides: Partial<T.RuleDetails> = {}): T.RuleD
};
}
+export function mockRuleDetailsParameter(
+ overrides: Partial<T.RuleParameter> = {}
+): T.RuleParameter {
+ return {
+ defaultValue: '1',
+ htmlDesc: 'description',
+ key: '1',
+ type: 'number',
+ ...overrides
+ };
+}
+
export function mockShortLivingBranch(
overrides: Partial<T.ShortLivingBranch> = {}
): T.ShortLivingBranch {
diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock
index f3cd22159f4..41f5e5e3342 100644
--- a/server/sonar-web/yarn.lock
+++ b/server/sonar-web/yarn.lock
@@ -967,6 +967,23 @@
"@types/d3-interpolate" "*"
"@types/d3-selection" "*"
+"@types/domhandler@*":
+ version "2.4.1"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/@types/domhandler/-/domhandler-2.4.1.tgz#7b3b347f7762180fbcb1ece1ce3dd0ebbb8c64cf"
+ integrity sha1-ezs0f3diGA+8sezhzj3Q67uMZM8=
+
+"@types/dompurify@^0.0.32":
+ version "0.0.32"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/@types/dompurify/-/dompurify-0.0.32.tgz#ccc4148d7f2e0598006611e82e840806fab4b268"
+ integrity sha1-zMQUjX8uBZgAZhHoLoQIBvq0smg=
+
+"@types/domutils@*":
+ version "1.7.2"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/@types/domutils/-/domutils-1.7.2.tgz#89422e579c165994ad5c09ce90325da596cc105d"
+ integrity sha1-iUIuV5wWWZStXAnOkDJdpZbMEF0=
+ dependencies:
+ "@types/domhandler" "*"
+
"@types/enzyme@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.9.1.tgz#3a0ce07e30066dbc26cd3474c8e680af2d249e26"
@@ -994,6 +1011,15 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-3.2.3.tgz#2416fee5cac641da2d05a905de5af5cb50162f60"
integrity sha512-s4SNWd31cmFP52ilv3LKCh344ayIXmfmcfExsegGspgnk/pQh75Yo6v49uzSE1oFMXp+Sz4GVnesL7rgybX9tQ==
+"@types/htmlparser2@*":
+ version "3.10.0"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/@types/htmlparser2/-/htmlparser2-3.10.0.tgz#b94d3b08f813c9eec12990ac6a34d8b17a7aebc9"
+ integrity sha1-uU07CPgTye7BKZCsajTYsXp668k=
+ dependencies:
+ "@types/domhandler" "*"
+ "@types/domutils" "*"
+ "@types/node" "*"
+
"@types/istanbul-lib-coverage@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a"
@@ -1106,6 +1132,13 @@
"@types/prop-types" "*"
csstype "^2.2.0"
+"@types/sanitize-html@1.20.0":
+ version "1.20.0"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/@types/sanitize-html/-/sanitize-html-1.20.0.tgz#b3beaa9eacab0e0fa5022d5faa727fea8fb9fc5d"
+ integrity sha1-s76qnqyrDg+lAi1fqnJ/6o+5/F0=
+ dependencies:
+ "@types/htmlparser2" "*"
+
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -3408,6 +3441,11 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
+dompurify@^1.0.11:
+ version "1.0.11"
+ resolved "https://repox.jfrog.io/repox/api/npm/npm/dompurify/-/dompurify-1.0.11.tgz#fe0f4a40d147f7cebbe31a50a1357539cfc1eb4d"
+ integrity sha1-/g9KQNFH98674xpQoTV1Oc/B600=
+
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"