提交 96f7525e 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/security/gitlab@13-0-stable-ee

上级 bcfbac44
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* any changes done to the haml need to be reflected here. * any changes done to the haml need to be reflected here.
*/ */
import { escape, isNumber } from 'lodash'; import { escape, isNumber } from 'lodash';
import { GlLink, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; import { GlLink, GlTooltipDirective as GlTooltip, GlSprintf } from '@gitlab/ui';
import { import {
dateInWords, dateInWords,
formatDate, formatDate,
...@@ -20,10 +20,14 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -20,10 +20,14 @@ import Icon from '~/vue_shared/components/icon.vue';
import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
export default { export default {
i18n: {
openedAgo: __('opened %{timeAgoString} by %{user}'),
},
components: { components: {
Icon, Icon,
IssueAssignees, IssueAssignees,
GlLink, GlLink,
GlSprintf,
}, },
directives: { directives: {
GlTooltip, GlTooltip,
...@@ -98,23 +102,21 @@ export default { ...@@ -98,23 +102,21 @@ export default {
} }
return __('Milestone'); return __('Milestone');
}, },
openedAgoByString() { issuableAuthor() {
const { author, created_at } = this.issuable; return this.issuable.author;
},
issuableCreatedAt() {
return getTimeago().format(this.issuable.created_at);
},
popoverDataAttrs() {
const { id, username, name, avatar_url } = this.issuableAuthor;
return sprintf( return {
__('opened %{timeAgoString} by %{user}'), 'data-user-id': id,
{ 'data-username': username,
timeAgoString: escape(getTimeago().format(created_at)), 'data-name': name,
user: `<a href="${escape(author.web_url)}" 'data-avatar-url': avatar_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,
);
}, },
referencePath() { referencePath() {
return this.issuable.references.relative; return this.issuable.references.relative;
...@@ -160,7 +162,7 @@ export default { ...@@ -160,7 +162,7 @@ export default {
mounted() { mounted() {
// TODO: Refactor user popover to use its own component instead of // TODO: Refactor user popover to use its own component instead of
// spawning event listeners on Vue-rendered elements. // spawning event listeners on Vue-rendered elements.
initUserPopovers([this.$refs.openedAgoByContainer.querySelector('a')]); initUserPopovers([this.$refs.openedAgoByContainer.$el]);
}, },
methods: { methods: {
labelStyle(label) { labelStyle(label) {
...@@ -221,17 +223,30 @@ export default { ...@@ -221,17 +223,30 @@ export default {
></i> ></i>
<gl-link :href="issuable.web_url">{{ issuable.title }}</gl-link> <gl-link :href="issuable.web_url">{{ issuable.title }}</gl-link>
</span> </span>
<span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">{{ <span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">
issuable.task_status {{ issuable.task_status }}
}}</span> </span>
</div> </div>
<div class="issuable-info"> <div class="issuable-info">
<span class="js-ref-path">{{ referencePath }}</span> <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; &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> </span>
<gl-link <gl-link
......
---
title: Fix security issue when rendering issuable
merge_request:
author:
type: security
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
...@@ -44,6 +44,10 @@ describe('Issuable component', () => { ...@@ -44,6 +44,10 @@ describe('Issuable component', () => {
baseUrl: TEST_BASE_URL, baseUrl: TEST_BASE_URL,
...props, ...props,
}, },
stubs: {
'gl-sprintf': GlSprintf,
'gl-link': '<a><slot></slot></a>',
},
}); });
}; };
...@@ -66,12 +70,12 @@ describe('Issuable component', () => { ...@@ -66,12 +70,12 @@ describe('Issuable component', () => {
const findConfidentialIcon = () => wrapper.find('.fa-eye-slash'); const findConfidentialIcon = () => wrapper.find('.fa-eye-slash');
const findTaskStatus = () => wrapper.find('.task-status'); 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 findMilestone = () => wrapper.find('.js-milestone');
const findMilestoneTooltip = () => findMilestone().attributes('title'); const findMilestoneTooltip = () => findMilestone().attributes('title');
const findDueDate = () => wrapper.find('.js-due-date'); const findDueDate = () => wrapper.find('.js-due-date');
const findLabelContainer = () => wrapper.find('.js-labels'); const findLabelContainer = () => wrapper.find('.js-labels');
const findLabelLinks = () => findLabelContainer().findAll(GlLink); const findLabelLinks = () => findLabelContainer().findAll('a');
const findWeight = () => wrapper.find('.js-weight'); const findWeight = () => wrapper.find('.js-weight');
const findAssignees = () => wrapper.find(IssueAssignees); const findAssignees = () => wrapper.find(IssueAssignees);
const findMergeRequestsCount = () => wrapper.find('.js-merge-requests'); const findMergeRequestsCount = () => wrapper.find('.js-merge-requests');
...@@ -86,7 +90,7 @@ describe('Issuable component', () => { ...@@ -86,7 +90,7 @@ describe('Issuable component', () => {
factory(); factory();
expect(initUserPopovers).toHaveBeenCalledWith([findOpenedAgoContainer().find('a').element]); expect(initUserPopovers).toHaveBeenCalledWith([wrapper.vm.$refs.openedAgoByContainer.$el]);
}); });
}); });
...@@ -135,7 +139,7 @@ describe('Issuable component', () => { ...@@ -135,7 +139,7 @@ describe('Issuable component', () => {
}); });
it('renders fuzzy opened date and author', () => { 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}`, `opened 1 month ago by ${TEST_USER_NAME}`,
); );
}); });
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册