diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-29 19:10:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-29 19:10:42 +0000 |
commit | 96f7525ebb3d7b15e5d7d2b123564581672814ed (patch) | |
tree | d30635c979401c5002f476677c23a534aa73748f | |
parent | bcfbac449a751edb6174233b97b8ce1b73e6f7a2 (diff) | |
download | gitlab-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.vue | 61 | ||||
-rw-r--r-- | changelogs/unreleased/security-xss-issuables-list.yml | 5 | ||||
-rw-r--r-- | spec/frontend/issuables_list/components/issuable_spec.js | 14 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/gitignore/C++.gitignore | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | vendor/gitignore/Java.gitignore | 0 |
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"> · - <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 |