]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12196 Fix SSF-79
authorJeremy Davis <jeremy.davis@sonarsource.com>
Mon, 17 Jun 2019 16:05:43 +0000 (18:05 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 28 Jun 2019 06:45:43 +0000 (08:45 +0200)
48 files changed:
server/sonar-docs/src/templates/page.tsx
server/sonar-web/package.json
server/sonar-web/src/main/js/app/components/search/SearchResult.tsx
server/sonar-web/src/main/js/app/components/search/SearchShowMore.tsx
server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchShowMore-test.tsx.snap
server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
server/sonar-web/src/main/js/apps/settings/utils.ts
server/sonar-web/src/main/js/apps/tutorials/components/commands/BuildWrapper.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/ClangGCC.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/Msvc.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/BuildWrapper-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Msvc-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
server/sonar-web/src/main/js/apps/web-api/components/Action.tsx
server/sonar-web/src/main/js/apps/web-api/components/Domain.tsx
server/sonar-web/src/main/js/apps/web-api/components/Params.tsx
server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx
server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx
server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx
server/sonar-web/src/main/js/helpers/testMocks.ts
server/sonar-web/yarn.lock
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index d52ce259704bd6d10f0dd73a33743ae13a69940c..61651aa4fdc37bd16bd344996363843e19e248d8 100644 (file)
@@ -104,7 +104,11 @@ export default class Page extends React.PureComponent<Props> {
         </Helmet>
         <HeaderList headers={realHeadingsList} />
         <h1>{pageTitle || mainTitle}</h1>
-        <div className="markdown-content" dangerouslySetInnerHTML={{ __html: htmlPageContent }} />
+        <div
+          className="markdown-content"
+          // Safe: comes from the backend
+          dangerouslySetInnerHTML={{ __html: htmlPageContent }}
+        />
       </>
     );
   }
index 652397674a7d291b3c3102107df4b1f4b6af7f0b..6a94ebb3ea16b19212995fadea14448f05f486ec 100644 (file)
@@ -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",
index 5ffab5ec6a42c0b3acde37534df8bdee3c48e0cb..20fd40ae953fd862e1a73e669dd49bb8bb3825b7 100644 (file)
@@ -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 }}
                 />
               ) : (
index 1c3932538f3bb07dd6cc656241f9526b0805dce9..d528a1a73692fc0331a789ce73db731011c87051 100644 (file)
  * 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>
index 9a3d97e398297500d461ba250a3e396071dff38d..71e30b63d9bc3718731ef03a02593082a242a3db 100644 (file)
@@ -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>
index 9e4e5f939bcf101171941d6e16207bd32be1675f..4c8f05066dba32c1073751cafc2edd8f6695defa 100644 (file)
@@ -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 />
index ac5b6325bb93f028da3455b760a6ef3f714b0938..490b971bab572b2746c23e8113818c96068c9efc 100644 (file)
@@ -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>
index afb3a99881f7af3acd82e1576d67e1a1f04ca0c7..42b022b64e752fb4628807dae8ec247929129e80 100644 (file)
@@ -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>
index 0f22be474126c7dac7836b41bccbbdbf5c2e34a3..fc0ec48363e7da53eef99ca022b9a5e949e74983 100644 (file)
@@ -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 || '' }}
           />
         ) : (
index 2679b6f3fd864500379ec5aeb32df4802af49da5..57f42d438a1192ba61b858cac145396496360627 100644 (file)
@@ -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 (file)
index 0000000..1e01774
--- /dev/null
@@ -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 (file)
index 0000000..374a330
--- /dev/null
@@ -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>
+`;
index d63d251974ae39acf175e7403ee608bf2c23e8c6..2ca069c1a465d69b68eb75b2c67e4dc6228c76fc 100644 (file)
@@ -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
index fd091c1c9405ff6860bcf08a29895dacb9ed18ee..c1213f0f1d6ca7dd0f91cd76a9d5fe515cc23852 100644 (file)
@@ -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>
                   </>
                 )}
               </>
index 7c718fdcb7e526dc796a15cdfad90b867cb649d5..e0606efd1df9a75733461e14ce5b50b6aadf0b85 100644 (file)
@@ -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>
index e27eda543b5ca1456804a5ba47d32d41df448903..7dadf7169692a350cfeaf8a7e70043d01b0446dd 100644 (file)
@@ -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
index 5d4ae3ae9cd10c40b6853c8db0817d33417a0dd8..642fb09c98fece25ae0400d4b3bc3108197b2acb 100644 (file)
@@ -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`
+    );
+  });
+});
index d17d9ead8a1cfdd1b82440ae4f3e2c09614910eb..9be3fb3820bad5b79efdd7e5c5d419fa454458c0 100644 (file)
@@ -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) }}
             />
           )}
 
index 9e5dc895b7b9b2301a8c737f6bcd29dfd04d6d38..7733c37c709af1ccb46514349b57870d7e9e3792 100644 (file)
@@ -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
index a11187e7a45feb23c021f16d26f8547ec4b72053..84b235cd2fc1c8c66430e96d500ea2eedefb96ff 100644 (file)
@@ -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}>
index 09484304223eecb6646598dd8272e3bbf11def76..990a69335912ee484b38e3532be8cd72e71257ab 100644 (file)
@@ -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;
index 829c09c02d6ac75076e84555f2a0f96f884343f9..3f16344c4207c92d7f25726dd3e897af252ac6d8 100644 (file)
@@ -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"
index c36574af7d82b512a761378a0c84f778a8b3c9f9..439589324701078b3e59bac8860e348de9450812 100644 (file)
@@ -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>
   );
 }
index 1bf82ddb0b3c455654d104cf344fea93a8f8b92a..a6fe98e0c2e63883ca9e3f39e9f80c22b3edc9a2 100644 (file)
@@ -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>
   );
 }
index d33568362bbd93c764ae83a2395cdf4e938ec413..dfe6ff428341d3f593488cba78a61bc6e7c2f00b 100644 (file)
@@ -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>
   );
 }
index 3bc233aa041729350136ed7b21ee43d5ea116496..778796e0eb00acde7ba2eeb3dd03a0948ed9b1aa 100644 (file)
@@ -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>
   );
 }
index 86ea954d2caf9847fb46a5c69606be1b532d793b..02c69437a2cb024b0a7569302de9112529d76288 100644 (file)
@@ -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"
index 5066cc1ede3395d900495b6dfc4ff1227a38f0a9..36fbab52778d2b10ea6eef018737690194e39f52 100644 (file)
@@ -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>
   );
 }
index 8c4a6529ea742981c8d3c3c410e17f31e65b61a0..c9445156179a993dd06b953ce177fe3c02bef0ea 100644 (file)
@@ -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>
   );
 }
index 0ce14360992f5a83c3f82490b3c8ecfd26f4538c..674b59e3d50a7b78581562bb9dc8ea0fd53bf02c 100644 (file)
@@ -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"
index 07e8eb45f0a58c05d13682140a4366af070e5ddc..85f98bb085d381c8452d58b9b982f6c60098ac24 100644 (file)
@@ -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"
index 7a765aa9112745036b6a2b87adca07e312edbdc5..be959ee615823e033c9e30a89db7962fbd1d3134 100644 (file)
@@ -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>
 `;
index b296291fc1c0a9b96f651aa9d6e05160a29294a3..f455da670c87409009838eee65ce6cb989159198 100644 (file)
@@ -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>
 `;
index 55ca37b75f9f4a0036ef56507e9cb62cd4d57798..7e1208e8dceb983759faab5d360d5656a5ad4b81 100644 (file)
@@ -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>
 `;
index af3ff510ca6b6d4b6452ecb22da96eaf7da4d3c1..7ee4d3becba9430232819f81d16a2704dfdcfbf6 100644 (file)
@@ -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>
 `;
index 740e26ed12bfdeb20256d550c1c7a58d809afa7a..094b637da721522114787c8b2c8e6e748e1ddf08 100644 (file)
@@ -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"
index 875ae78edc41b971eb777ec1d37c0fa444ab44c3..3181496e14b0dfca4c266f4e4fae5ce1cc7fcaec 100644 (file)
@@ -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>
 `;
index 8fe780fe0948c0f64952499f0d0c5dd556bf6002..bcd28ca14b2866aac41b5b0743dccde71f9c456a 100644 (file)
@@ -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>
 `;
index cf6a8de4ce37fbc2e3c2a77edfb286aa5765a2c5..6ad4425a0a8a9e67e4112ab1e17655d1e752d809 100644 (file)
@@ -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"
index 304f28eb2f79c4282881fdd65564268e4a5132d2..bd6c63de706f869743970f67e891007024e7c4ab 100644 (file)
@@ -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 }}
           />
 
index 6df2b62096f8f5deb1dfd06f96e7ebaf95e6dd9a..d7e4ab6ee4d24fc23fe68d372352f024357a6b2c 100644 (file)
@@ -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 }}
         />
       )}
index 5cf2d7c0a9039e1e996001925f21052115ae0886..7dbf8b77860db603edc5013ceb79a3051f2b6362 100644 (file)
@@ -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>
index 5d82b9c919a3b2d6b0b3ea72e75c20d72548b9a7..bf21da958f5dbb4862fa196f41f4dd196deafa8f 100644 (file)
@@ -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>
index e6ae0718d075fb0084718bf3b8150f622665fb91..0f5959e5fe2f81528721bdc6ce1039251ef0ba50 100644 (file)
  * 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');
 });
index 66de33810724356bebb1be535556466deec137a5..09de825c56a7ee4e10c6c8cdec08e7e6cabd27cf 100644 (file)
@@ -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">
index 66c6a639e6fdbc5c20705353b6431283273d77e0..9fd2c05f8f921d86f1a741cb877c801acd1c162d 100644 (file)
@@ -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 {
index f3cd22159f45637e6a92701c0738875d2ebe1605..41f5e5e33421fa96e59a4d7a2a6392bd9a6891fd 100644 (file)
     "@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"
   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"
     "@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"
index 0264f73fad3b3fdbcd1c2da4fe4943936d06f46b..0e1ef8fc0fd2cc306d52ced570e926e881026a2c 100644 (file)
@@ -936,6 +936,13 @@ property.error.notRegexp=Not a valid Java regular expression
 property.error.notInOptions=Not a valid option
 property.category.scm=SCM
 property.sonar.leak.period.description=Period used to compare measures and track new issues. Values are:<ul class='bullet'><li>Number of days before  analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example '1.2' or 'BASELINE'</li></ul><p>When specifying a number of days or a date, the snapshot selected as the baseline for comparison is the first one available inside the corresponding time range. Specifically, the first analysis in the range is considered to be before the New Code Period. </p><p>Changing this property only takes effect after subsequent project analyses.<p/>
+property.sonar.leak.period.description.intro=Period used to compare measures and track new issues. Values are:
+property.sonar.leak.period.description.item1=Number of days before  analysis, for example 5.
+property.sonar.leak.period.description.item2=A custom date. Format is yyyy-MM-dd, for example 2010-12-25
+property.sonar.leak.period.description.item3='previous_version' to compare to the previous version in the project history
+property.sonar.leak.period.description.item4=A version, for example '1.2' or 'BASELINE'
+property.sonar.leak.period.description.details1=When specifying a number of days or a date, the snapshot selected as the baseline for comparison is the first one available inside the corresponding time range. Specifically, the first analysis in the range is considered to be before the New Code Period. 
+property.sonar.leak.period.description.details2=Changing this property only takes effect after subsequent project analyses.
 property.sonar.branch.longLivedBranches.regex.description=Regular expression used to detect whether a branch is a long living branch (as opposed to short living branch), based on its name. This applies only during first analysis, the type of a branch cannot be changed later.
 
 
@@ -945,7 +952,7 @@ property.sonar.branch.longLivedBranches.regex.description=Regular expression use
 #
 #------------------------------------------------------------------------------
 search.shortcut_hint=Hint: Press {shortcut} from anywhere to open this search bar.
-search.show_more.hint=Press {0} to display
+search.show_more.hint=Press {key} to display
 search.placeholder=Search for projects and files...
 search.search_for_projects=Search for projects...
 search.search_for_users=Search for users...
@@ -2938,37 +2945,36 @@ onboarding.language.os.win=Windows
 onboarding.language.os.mac=macOS
 onboarding.language.project_key=Define a unique project key
 
+onboarding.analysis.docs=Please visit the {link} for more details.
+
 onboarding.analysis.java.maven.header=Execute the Scanner for Maven from your computer
 onboarding.analysis.java.maven.text=Running a {instance} analysis with Maven is straighforward. You just need to run the following command in your project's folder.
-onboarding.analysis.java.maven.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html" target="_blank">official documentation of the Scanner for Maven</a> for more details.
+onboarding.analysis.java.maven.docs_link=official documentation of the Scanner for Maven
 
 onboarding.analysis.java.gradle.header=Execute the Scanner for Gradle from your computer
-onboarding.analysis.java.gradle.text.1=Running an analysis with Gradle is straighforward. You just need to declare the <code>org.sonarqube</code> plugin in your <code>build.gradle</code> file:
+onboarding.analysis.java.gradle.text.1=Running an analysis with Gradle is straighforward. You just need to declare the {plugin_code} plugin in your {filename} file:
 onboarding.analysis.java.gradle.text.2=and run the following command:
-onboarding.analysis.java.gradle.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/gradle.html" target="_blank">official documentation of the Scanner for Gradle</a> for more details.
+onboarding.analysis.java.gradle.docs_link=official documentation of the Scanner for Gradle
 
 onboarding.analysis.msbuild.header=Download and unzip the Scanner for MSBuild
-onboarding.analysis.msbuild.text=And add the executable's directory to the <code>%PATH%</code> environment variable
+onboarding.analysis.msbuild.text=And add the executable's directory to the {code} environment variable
 onboarding.analysis.msbuild.execute=Execute the Scanner for MSBuild from your computer
 onboarding.analysis.msbuild.execute.text=Running a {instance} analysis is straighforward. You just need to execute the following commands at the root of your solution.
-onboarding.analysis.msbuild.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html" target="_blank">official documentation of the Scanner for MSBuild</a> for more details.
+onboarding.analysis.msbuild.docs_link=official documentation of the Scanner for MSBuild
 
 onboarding.analysis.build_wrapper.header.linux=Download and unzip the Build Wrapper for Linux
 onboarding.analysis.build_wrapper.header.win=Download and unzip the Build Wrapper for Windows
 onboarding.analysis.build_wrapper.header.mac=Download and unzip the Build Wrapper for macOS
-onboarding.analysis.build_wrapper.text.linux=And add the executable's directory to the <code>PATH</code> environment variable
-onboarding.analysis.build_wrapper.text.win=And add the executable's directory to the <code>%PATH%</code> environment variable
-onboarding.analysis.build_wrapper.text.mac=And add the executable's directory to the <code>PATH</code> environment variable
+onboarding.analysis.build_wrapper.text=And add the executable's directory to the {env_var} environment variable
 
 onboarding.analysis.sq_scanner.header.linux=Download and unzip the Scanner for Linux
 onboarding.analysis.sq_scanner.header.win=Download and unzip the Scanner for Windows
 onboarding.analysis.sq_scanner.header.mac=Download and unzip the Scanner for macOS
-onboarding.analysis.sq_scanner.text.linux=And add the <code>bin</code> directory to the <code>PATH</code> environment variable
-onboarding.analysis.sq_scanner.text.win=And add the <code>bin</code> directory to the <code>%PATH%</code> environment variable
-onboarding.analysis.sq_scanner.text.mac=And add the <code>bin</code> directory to the <code>PATH</code> environment variable
+onboarding.analysis.sq_scanner.text=And add the {dir} directory to the {env_var} environment variable
 onboarding.analysis.sq_scanner.execute=Execute the Scanner from your computer
 onboarding.analysis.sq_scanner.execute.text=Running a {instance} analysis is straighforward. You just need to execute the following commands in your project's folder.
-onboarding.analysis.sq_scanner.docs=Please visit the <a href="http://redirect.sonarsource.com/doc/install-configure-scanner.html" target="_blank">official documentation of the Scanner</a> for more details.
+onboarding.analysis.sq_scanner.docs=Please visit the {link} for more details.
+onboarding.analysis.sq_scanner.docs_link=official documentation of the Scanner
 
 onboarding.project_watcher.not_started=Once your project is analyzed, this page will refresh automatically.
 onboarding.project_watcher.in_progress=Analysis is in progress, please wait...
@@ -3062,8 +3068,10 @@ encryption.encrypt=Encrypt
 encryption.secret_key_description=Secret key is required to be able to encrypt properties. {moreInformationLink}
 encryption.secret_key=Secret Key
 encryption.how_to_use=How To Use
-encryption.how_to_use.content=<ul><li>Store the secret key in the file <code>~/.sonar/sonar-secret.txt</code> of the server. This file can be relocated by defining the property <code>sonar.secretKeyPath</code> in <code>conf/sonar.properties</code></li><li>Restrict access to this file by making it readable and by owner only</li><li>Restart the server if the property <code>sonar.secretKeyPath</code> has been set or changed.</li><li>For each property that you want to encrypt, generate the encrypted value and replace the original value wherever it is stored (configuration files, command lines).</li></ul>
-
+encryption.how_to_use.content1=Store the secret key in the file {secret_file} of the server. This file can be relocated by defining the property {property} in {propreties_file}
+encryption.how_to_use.content2=Restrict access to this file by making it readable and by owner only
+encryption.how_to_use.content3=Restart the server if the property {property} has been set or changed.
+encryption.how_to_use.content4=For each property that you want to encrypt, generate the encrypted value and replace the original value wherever it is stored (configuration files, command lines).
 
 #------------------------------------------------------------------------------
 #
@@ -3152,8 +3160,10 @@ maintenance.is_down={instance} is down
 maintenance.sonarqube_is_down.text=Something went wrong. Please contact your system administrator.
 maintenance.try_again=Try Again
 maintenance.is_under_maintenance={instance} is under maintenance
-maintenance.sonarqube_is_under_maintenance.1=While waiting, you might want to investigate <a href="https://redirect.sonarsource.com/doc/plugin-library.html">new plugins</a> to extend the current functionality.
-maintenance.sonarqube_is_under_maintenance.2=If you are an administrator and have no idea why this message is being shown, you should read the <a href="https://redirect.sonarsource.com/doc/upgrading.html">upgrade guide</a>.
+maintenance.sonarqube_is_under_maintenance.1=While waiting, you might want to investigate {link} to extend the current functionality.
+maintenance.sonarqube_is_under_maintenance_link.1=new plugins
+maintenance.sonarqube_is_under_maintenance.2=If you are an administrator and have no idea why this message is being shown, you should read the {link}.
+maintenance.sonarqube_is_under_maintenance_link.2=upgrade guide
 maintenance.is_starting={instance} is starting
 maintenance.is_up={instance} is up
 maintenance.all_systems_opetational=All systems operational.