Commit 88280301 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'security-approvers-security-fix-master' into 'master'

[master] Escape username in appovers dropdown

See merge request gitlab/gitlab-ee!618
parents 9255697c ac04db31
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore';
import Api from '~/api'; import Api from '~/api';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Flash from '~/flash'; import Flash from '~/flash';
...@@ -29,10 +30,9 @@ export default class ApproversSelect { ...@@ -29,10 +30,9 @@ export default class ApproversSelect {
static getApprovers(fieldName, approverList) { static getApprovers(fieldName, approverList) {
const input = $(`[name="${fieldName}"]`); const input = $(`[name="${fieldName}"]`);
const existingApprovers = $(approverList).map((i, el) => const existingApprovers = $(approverList).map((i, el) => parseInt($(el).data('id'), 10));
parseInt($(el).data('id'), 10), const selectedApprovers = input
); .val()
const selectedApprovers = input.val()
.split(',') .split(',')
.filter(val => val !== ''); .filter(val => val !== '');
return [...existingApprovers, ...selectedApprovers]; return [...existingApprovers, ...selectedApprovers];
...@@ -76,11 +76,12 @@ export default class ApproversSelect { ...@@ -76,11 +76,12 @@ export default class ApproversSelect {
} }
initSelect2() { initSelect2() {
this.$approverSelect.select2({ this.$approverSelect
.select2({
placeholder: 'Search for users or groups', placeholder: 'Search for users or groups',
multiple: true, multiple: true,
minimumInputLength: 0, minimumInputLength: 0,
query: (query) => { query: query => {
const fetchGroups = this.fetchGroups(query.term); const fetchGroups = this.fetchGroups(query.term);
const fetchUsers = this.fetchUsers(query.term); const fetchUsers = this.fetchUsers(query.term);
return Promise.all([fetchGroups, fetchUsers]).then(([groups, users]) => { return Promise.all([fetchGroups, fetchUsers]).then(([groups, users]) => {
...@@ -131,8 +132,8 @@ export default class ApproversSelect { ...@@ -131,8 +132,8 @@ export default class ApproversSelect {
<img class="avatar s40" src="${avatar}"> <img class="avatar s40" src="${avatar}">
</div> </div>
<div class="user-info"> <div class="user-info">
<div class="user-name">${name}</div> <div class="user-name">${_.escape(name)}</div>
<div class="user-username">@${username}</div> <div class="user-username">@${_.escape(username)}</div>
</div> </div>
</div> </div>
`; `;
...@@ -140,8 +141,8 @@ export default class ApproversSelect { ...@@ -140,8 +141,8 @@ export default class ApproversSelect {
return ` return `
<div class="group-result"> <div class="group-result">
<div class="group-name">${fullName}</div> <div class="group-name">${_.escape(fullName)}</div>
<div class="group-path">${fullPath}</div> <div class="group-path">${_.escape(fullPath)}</div>
</div> </div>
`; `;
} }
...@@ -163,14 +164,17 @@ export default class ApproversSelect { ...@@ -163,14 +164,17 @@ export default class ApproversSelect {
const $form = $('.js-add-approvers').closest('form'); const $form = $('.js-add-approvers').closest('form');
$loadWrapper.removeClass('hidden'); $loadWrapper.removeClass('hidden');
axios.post($form.attr('action'), `_method=PATCH&${[encodeURIComponent(fieldName)]}=${newValue}`, { axios
.post($form.attr('action'), `_method=PATCH&${[encodeURIComponent(fieldName)]}=${newValue}`, {
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
}, },
}).then(({ data }) => { })
.then(({ data }) => {
ApproversSelect.updateApproverList(data); ApproversSelect.updateApproverList(data);
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper); ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
}).catch(() => { })
.catch(() => {
Flash(__('An error occurred while adding approver')); Flash(__('An error occurred while adding approver'));
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper); ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
}); });
...@@ -188,20 +192,27 @@ export default class ApproversSelect { ...@@ -188,20 +192,27 @@ export default class ApproversSelect {
const $loadWrapper = $('.load-wrapper'); const $loadWrapper = $('.load-wrapper');
$loadWrapper.removeClass('hidden'); $loadWrapper.removeClass('hidden');
axios.post(target.getAttribute('href'), '_method=DELETE', { axios
.post(target.getAttribute('href'), '_method=DELETE', {
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
}, },
}).then(({ data }) => { })
.then(({ data }) => {
ApproversSelect.updateApproverList(data); ApproversSelect.updateApproverList(data);
$loadWrapper.addClass('hidden'); $loadWrapper.addClass('hidden');
}).catch(() => { })
.catch(() => {
Flash(__('An error occurred while removing approver')); Flash(__('An error occurred while removing approver'));
$loadWrapper.addClass('hidden'); $loadWrapper.addClass('hidden');
}); });
} }
static updateApproverList(html) { static updateApproverList(html) {
$('.js-current-approvers').html($(html).find('.js-current-approvers').html()); $('.js-current-approvers').html(
$(html)
.find('.js-current-approvers')
.html(),
);
} }
} }
---
title: Escape name in merge request approvers dropdown
merge_request:
author:
type: security
...@@ -34,4 +34,29 @@ describe('ApproversSelect', () => { ...@@ -34,4 +34,29 @@ describe('ApproversSelect', () => {
expect($loadWrapper.addClass).toHaveBeenCalledWith('hidden'); expect($loadWrapper.addClass).toHaveBeenCalledWith('hidden');
}); });
}); });
describe('formatResult', () => {
it('escapes name', () => {
const output = ApproversSelect.formatResult({
name: '<script>alert("testing")</script>',
username: 'testing',
avatar_url: gl.TEST_HOST,
full_name: '<script>alert("testing")</script>',
full_path: 'testing',
});
expect(output).not.toContain('<script>alert("testing")</script>');
});
it('escapes full name', () => {
const output = ApproversSelect.formatResult({
username: 'testing',
avatar_url: gl.TEST_HOST,
full_name: '<script>alert("testing")</script>',
full_path: 'testing',
});
expect(output).not.toContain('<script>alert("testing")</script>');
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment