From 29ccfe5d20dfdb1e71b53b23a6077f3235d4fd6e Mon Sep 17 00:00:00 2001
From: Wouter Admiraal
<45544358+wouter-admiraal-sonarsource@users.noreply.github.com>
Date: Fri, 29 Mar 2019 09:09:29 +0100
Subject: [PATCH] SONAR-11867, SSF-74 Fix XSS in project links on
account/projects (#3203)
---
.../js/apps/account/projects/ProjectCard.tsx | 37 ++++++++++++-------
.../projects/__tests__/ProjectCard-test.js | 2 +-
.../main/js/apps/overview/meta/MetaLink.d.ts | 34 +++++++++++++++++
.../main/js/apps/overview/meta/MetaLink.js | 9 +++--
.../overview/meta/__tests__/MetaLink-test.js | 1 +
.../__snapshots__/MetaLink-test.js.snap | 32 ++++++++++++----
.../main/js/apps/project-admin/links/utils.js | 2 +-
7 files changed, 90 insertions(+), 27 deletions(-)
create mode 100644 server/sonar-web/src/main/js/apps/overview/meta/MetaLink.d.ts
diff --git a/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx
index cfa10ff8e37..a0d2959306a 100644
--- a/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx
+++ b/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx
@@ -18,22 +18,42 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { sortBy } from 'lodash';
import { Link } from 'react-router';
import { Project } from './types';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Level from '../../../components/ui/Level';
import Tooltip from '../../../components/controls/Tooltip';
+import MetaLink from '../../overview/meta/MetaLink';
+import { orderLinks } from '../../project-admin/links/utils';
import { translateWithParameters, translate } from '../../../helpers/l10n';
interface Props {
project: Project;
}
+type ProjectLink = {
+ id: string;
+ name: string;
+ url: string;
+ type: string;
+};
+
export default function ProjectCard({ project }: Props) {
const isAnalyzed = project.lastAnalysisDate != null;
- const links = sortBy(project.links, 'type');
+
+ const { links } = project;
+ const orderedLinks: ProjectLink[] = orderLinks(
+ links.map((link, i) => {
+ const { href, name, type } = link;
+ return {
+ id: `link-${i}`,
+ name,
+ type,
+ url: href
+ };
+ })
+ );
return (
@@ -70,17 +90,8 @@ export default function ProjectCard({ project }: Props) {
{links.length > 0 && (
- {links.map(link => (
- -
-
-
-
-
+ {orderedLinks.map(link => (
+
))}
diff --git a/server/sonar-web/src/main/js/apps/account/projects/__tests__/ProjectCard-test.js b/server/sonar-web/src/main/js/apps/account/projects/__tests__/ProjectCard-test.js
index c714da617dc..997c6acd505 100644
--- a/server/sonar-web/src/main/js/apps/account/projects/__tests__/ProjectCard-test.js
+++ b/server/sonar-web/src/main/js/apps/account/projects/__tests__/ProjectCard-test.js
@@ -82,5 +82,5 @@ it('should render links', () => {
links: [{ name: 'n', type: 't', href: 'h' }]
};
const output = shallow(
);
- expect(output.find('.account-project-links').find('li').length).toBe(1);
+ expect(output.find('MetaLink').length).toBe(1);
});
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.d.ts b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.d.ts
new file mode 100644
index 00000000000..963030cdb27
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.d.ts
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+
+type Link = {
+ id: string;
+ name: string;
+ url: string;
+ type: string;
+};
+
+interface Props {
+ iconOnly?: boolean;
+ link: Link;
+}
+
+export default class MetaLink extends React.Component
{}
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js
index 2f368484c9b..08d1c4db53c 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js
@@ -39,6 +39,7 @@ type State = {|
export default class MetaLink extends React.PureComponent {
/*:: props: {
+ iconOnly?: boolean,
link: Link
};
*/
@@ -66,7 +67,7 @@ export default class MetaLink extends React.PureComponent {
}
render() {
- const { link } = this.props;
+ const { iconOnly, link } = this.props;
return (
@@ -74,10 +75,10 @@ export default class MetaLink extends React.PureComponent {
className="link-with-icon"
href={link.url}
target="_blank"
- onClick={!isClickable(link) && this.handleClick}>
+ onClick={!isClickable(link) && this.handleClick}
+ title={link.name}>
{this.renderLinkIcon(link)}
-
- {link.name}
+ {!iconOnly && `\u00A0${link.name}`}
{this.state.expanded && (
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaLink-test.js b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaLink-test.js
index 2499c2e3c36..3af59d19e93 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaLink-test.js
+++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaLink-test.js
@@ -31,6 +31,7 @@ it('should match snapshot', () => {
};
expect(shallow(
)).toMatchSnapshot();
+ expect(shallow(
)).toMatchSnapshot();
});
it('should expand and collapse link', () => {
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap
index 416a8104ae2..931f9d3f9c1 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap
@@ -7,12 +7,12 @@ exports[`should expand and collapse link 1`] = `
href="scm:git:git@github.com"
onClick={[Function]}
target="_blank"
+ title="Foo"
>
- Â
- Foo
+ Â Foo
`;
@@ -24,12 +24,12 @@ exports[`should expand and collapse link 2`] = `
href="scm:git:git@github.com"
onClick={[Function]}
target="_blank"
+ title="Foo"
>
- Â
- Foo
+ Â Foo
- Â
- Foo
+ Â Foo
`;
@@ -69,12 +69,28 @@ exports[`should match snapshot 1`] = `
href="http://example.com"
onClick={false}
target="_blank"
+ title="Foo"
+ >
+
+ Â Foo
+
+
+`;
+
+exports[`should match snapshot 2`] = `
+
+
- Â
- Foo
`;
diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/utils.js b/server/sonar-web/src/main/js/apps/project-admin/links/utils.js
index 9ae5e6ef3d7..c8772b917cc 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/links/utils.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/links/utils.js
@@ -29,7 +29,7 @@ export function orderLinks(links) {
const [provided, unknown] = partition(links, isProvided);
return [
...sortBy(provided, link => PROVIDED_TYPES.indexOf(link.type)),
- ...sortBy(unknown, link => link.name.toLowerCase())
+ ...sortBy(unknown, link => link.name && link.name.toLowerCase())
];
}
--
2.39.5