summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-29 19:10:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-29 19:10:42 +0000
commit96f7525ebb3d7b15e5d7d2b123564581672814ed (patch)
treed30635c979401c5002f476677c23a534aa73748f
parentbcfbac449a751edb6174233b97b8ce1b73e6f7a2 (diff)
downloadgitlab-ce-96f7525ebb3d7b15e5d7d2b123564581672814ed.tar.gz
Add latest changes from gitlab-org/security/gitlab@13-0-stable-ee
-rw-r--r--app/assets/javascripts/issuables_list/components/issuable.vue61
-rw-r--r--changelogs/unreleased/security-xss-issuables-list.yml5
-rw-r--r--spec/frontend/issuables_list/components/issuable_spec.js14
-rw-r--r--[-rwxr-xr-x]vendor/gitignore/C++.gitignore0
-rw-r--r--[-rwxr-xr-x]vendor/gitignore/Java.gitignore0
5 files changed, 52 insertions, 28 deletions
diff --git a/app/assets/javascripts/issuables_list/components/issuable.vue b/app/assets/javascripts/issuables_list/components/issuable.vue
index 2fd92e009eb..947c7518289 100644
--- a/app/assets/javascripts/issuables_list/components/issuable.vue
+++ b/app/assets/javascripts/issuables_list/components/issuable.vue
@@ -4,7 +4,7 @@
* any changes done to the haml need to be reflected here.
*/
import { escape, isNumber } from 'lodash';
-import { GlLink, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
+import { GlLink, GlTooltipDirective as GlTooltip, GlSprintf } from '@gitlab/ui';
import {
dateInWords,
formatDate,
@@ -20,10 +20,14 @@ import Icon from '~/vue_shared/components/icon.vue';
import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
export default {
+ i18n: {
+ openedAgo: __('opened %{timeAgoString} by %{user}'),
+ },
components: {
Icon,
IssueAssignees,
GlLink,
+ GlSprintf,
},
directives: {
GlTooltip,
@@ -98,23 +102,21 @@ export default {
}
return __('Milestone');
},
- openedAgoByString() {
- const { author, created_at } = this.issuable;
+ issuableAuthor() {
+ return this.issuable.author;
+ },
+ issuableCreatedAt() {
+ return getTimeago().format(this.issuable.created_at);
+ },
+ popoverDataAttrs() {
+ const { id, username, name, avatar_url } = this.issuableAuthor;
- return sprintf(
- __('opened %{timeAgoString} by %{user}'),
- {
- timeAgoString: escape(getTimeago().format(created_at)),
- user: `<a href="${escape(author.web_url)}"
- data-user-id=${escape(author.id)}
- data-username=${escape(author.username)}
- data-name=${escape(author.name)}
- data-avatar-url="${escape(author.avatar_url)}">
- ${escape(author.name)}
- </a>`,
- },
- false,
- );
+ return {
+ 'data-user-id': id,
+ 'data-username': username,
+ 'data-name': name,
+ 'data-avatar-url': avatar_url,
+ };
},
referencePath() {
return this.issuable.references.relative;
@@ -160,7 +162,7 @@ export default {
mounted() {
// TODO: Refactor user popover to use its own component instead of
// spawning event listeners on Vue-rendered elements.
- initUserPopovers([this.$refs.openedAgoByContainer.querySelector('a')]);
+ initUserPopovers([this.$refs.openedAgoByContainer.$el]);
},
methods: {
labelStyle(label) {
@@ -221,17 +223,30 @@ export default {
></i>
<gl-link :href="issuable.web_url">{{ issuable.title }}</gl-link>
</span>
- <span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">{{
- issuable.task_status
- }}</span>
+ <span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">
+ {{ issuable.task_status }}
+ </span>
</div>
<div class="issuable-info">
<span class="js-ref-path">{{ referencePath }}</span>
- <span class="d-none d-sm-inline-block mr-1">
+ <span data-testid="openedByMessage" class="d-none d-sm-inline-block mr-1">
&middot;
- <span ref="openedAgoByContainer" v-html="openedAgoByString"></span>
+ <gl-sprintf :message="$options.i18n.openedAgo">
+ <template #timeAgoString>
+ <span>{{ issuableCreatedAt }}</span>
+ </template>
+ <template #user>
+ <gl-link
+ ref="openedAgoByContainer"
+ v-bind="popoverDataAttrs"
+ :href="issuableAuthor.web_url"
+ >
+ {{ issuableAuthor.name }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
</span>
<gl-link
diff --git a/changelogs/unreleased/security-xss-issuables-list.yml b/changelogs/unreleased/security-xss-issuables-list.yml
new file mode 100644
index 00000000000..b158bce4577
--- /dev/null
+++ b/changelogs/unreleased/security-xss-issuables-list.yml
@@ -0,0 +1,5 @@
+---
+title: Fix security issue when rendering issuable
+merge_request:
+author:
+type: security
diff --git a/spec/frontend/issuables_list/components/issuable_spec.js b/spec/frontend/issuables_list/components/issuable_spec.js
index 980def06078..834d18246a5 100644
--- a/spec/frontend/issuables_list/components/issuable_spec.js
+++ b/spec/frontend/issuables_list/components/issuable_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlLink } from '@gitlab/ui';
+import { GlSprintf } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import { trimText } from 'helpers/text_helper';
import initUserPopovers from '~/user_popovers';
@@ -44,6 +44,10 @@ describe('Issuable component', () => {
baseUrl: TEST_BASE_URL,
...props,
},
+ stubs: {
+ 'gl-sprintf': GlSprintf,
+ 'gl-link': '<a><slot></slot></a>',
+ },
});
};
@@ -66,12 +70,12 @@ describe('Issuable component', () => {
const findConfidentialIcon = () => wrapper.find('.fa-eye-slash');
const findTaskStatus = () => wrapper.find('.task-status');
- const findOpenedAgoContainer = () => wrapper.find({ ref: 'openedAgoByContainer' });
+ const findOpenedAgoContainer = () => wrapper.find('[data-testid="openedByMessage"]');
const findMilestone = () => wrapper.find('.js-milestone');
const findMilestoneTooltip = () => findMilestone().attributes('title');
const findDueDate = () => wrapper.find('.js-due-date');
const findLabelContainer = () => wrapper.find('.js-labels');
- const findLabelLinks = () => findLabelContainer().findAll(GlLink);
+ const findLabelLinks = () => findLabelContainer().findAll('a');
const findWeight = () => wrapper.find('.js-weight');
const findAssignees = () => wrapper.find(IssueAssignees);
const findMergeRequestsCount = () => wrapper.find('.js-merge-requests');
@@ -86,7 +90,7 @@ describe('Issuable component', () => {
factory();
- expect(initUserPopovers).toHaveBeenCalledWith([findOpenedAgoContainer().find('a').element]);
+ expect(initUserPopovers).toHaveBeenCalledWith([wrapper.vm.$refs.openedAgoByContainer.$el]);
});
});
@@ -135,7 +139,7 @@ describe('Issuable component', () => {
});
it('renders fuzzy opened date and author', () => {
- expect(trimText(findOpenedAgoContainer().text())).toEqual(
+ expect(trimText(findOpenedAgoContainer().text())).toContain(
`opened 1 month ago by ${TEST_USER_NAME}`,
);
});
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
index 259148fa18f..259148fa18f 100755..100644
--- a/vendor/gitignore/C++.gitignore
+++ b/vendor/gitignore/C++.gitignore
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index a1c2a238a96..a1c2a238a96 100755..100644
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore