Properly sanitize JSON data to fix XSS on Issue details page.
This commit is contained in:
parent
c56f2b9615
commit
6d360c210d
|
@ -1,10 +1,11 @@
|
|||
import Vue from 'vue';
|
||||
import sanitize from 'sanitize-html';
|
||||
import issuableApp from './components/app.vue';
|
||||
import '../vue_shared/vue_resource_interceptor';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
export default function initIssueableApp() {
|
||||
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
|
||||
const props = JSON.parse(initialDataEl.innerHTML.replace(/"/g, '"'));
|
||||
const props = JSON.parse(sanitize(initialDataEl.textContent).replace(/"/g, '"'));
|
||||
|
||||
return new Vue({
|
||||
el: document.getElementById('js-issuable-app'),
|
||||
|
@ -17,4 +18,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@ import Issue from '~/issue';
|
|||
import ShortcutsIssuable from '~/shortcuts_issuable';
|
||||
import ZenMode from '~/zen_mode';
|
||||
import '~/notes/index';
|
||||
import '~/issue_show/index';
|
||||
import initIssueableApp from '~/issue_show';
|
||||
|
||||
export default function () {
|
||||
initIssueableApp();
|
||||
new Issue(); // eslint-disable-line no-new
|
||||
new ShortcutsIssuable(); // eslint-disable-line no-new
|
||||
new ZenMode(); // eslint-disable-line no-new
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Sanitize JSON data properly to fix XSS on Issue details page
|
||||
merge_request:
|
||||
author:
|
||||
type: security
|
|
@ -18,6 +18,23 @@ describe 'Issue Detail', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when issue description has xss snippet' do
|
||||
before do
|
||||
issue.update!(description: '![xss" onload=alert(1);//](a)')
|
||||
sign_in(user)
|
||||
visit project_issue_path(project, issue)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'should encode the description to prevent xss issues' do
|
||||
page.within('.issuable-details .detail-page-description') do
|
||||
expect(page).to have_selector('img', count: 1)
|
||||
expect(find('img')['onerror']).to be_nil
|
||||
expect(find('img')['src']).to end_with('/a')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when edited by a user who is later deleted' do
|
||||
before do
|
||||
sign_in(user)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import initIssueableApp from '~/issue_show';
|
||||
|
||||
describe('Issue show index', () => {
|
||||
describe('initIssueableApp', () => {
|
||||
it('should initialize app with no potential XSS attack', () => {
|
||||
const d = document.createElement('div');
|
||||
d.id = 'js-issuable-app-initial-data';
|
||||
d.innerHTML = JSON.stringify({
|
||||
initialDescriptionHtml: '<img src=x onerror=alert(1)>',
|
||||
});
|
||||
document.body.appendChild(d);
|
||||
|
||||
const alertSpy = spyOn(window, 'alert');
|
||||
initIssueableApp();
|
||||
|
||||
expect(alertSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue