Fix front-end XSS prevention

This commit is contained in:
printempw 2018-08-15 14:59:09 +08:00
parent 5e9da1e492
commit 3adf04ff6d
9 changed files with 44 additions and 23 deletions

View File

@ -2,6 +2,11 @@
const $ = require('jquery');
window.$ = window.jQuery = $;
$.fn.dataTable = {
defaults: {},
ext: { errMode: '' },
render: { text: () => ({ filter: text => text }) }
};
jest.useFakeTimers();
@ -1318,7 +1323,6 @@ describe('tests for "common" module', () => {
const fetch = jest.fn()
.mockReturnValue(Promise.resolve({ errno: 0, msg: 'Recorded.' }));
$.fn.dataTable = { defaults: {}, ext: { errMode: '' } };
window.document.cookie = '';
window.fetch = fetch;
window.blessing = {
@ -1347,7 +1351,6 @@ describe('tests for "common" module', () => {
const showModal = jest.fn();
window.trans = jest.fn(t => t);
window.showModal = showModal;
$.fn.dataTable = { defaults: {}, ext: { errMode: '' } };
handleDataTablesAjaxError(undefined, undefined, '{}');
expect(showModal).not.toBeCalled();

View File

@ -2,6 +2,7 @@
const $ = require('jquery');
window.$ = window.jQuery = $;
$.fn.dataTable = { render: { text: () => ({ filter: text => text }) } };
window.getQueryString = jest.fn((key, defaultValue) => defaultValue);

View File

@ -2,6 +2,7 @@
const $ = require('jquery');
window.$ = window.jQuery = $;
$.fn.dataTable = { render: { text: () => ({ filter: text => text }) } };
describe('tests for "verification" module', () => {
const modulePath = '../user/verification';

View File

@ -19,30 +19,38 @@ const marketTableColumnDefs = [
targets: 0,
title: trans('admin.pluginTitle'),
data: 'title',
render: (title, type, row) => `<strong>${ title }</strong><div class="plugin-name">${ row.name }</div>`
render: (title, type, row) => {
return $.fn.dataTable.render.text().filter(`
<strong>${ title }</strong>
<div class="plugin-name">${ row.name }</div>
`);
}
},
{
targets: 1,
title: trans('admin.pluginDescription'),
data: 'description',
render: $.fn.dataTable.render.text(),
orderable: false
},
{
targets: 2,
title: trans('admin.pluginAuthor'),
data: 'author',
render: $.fn.dataTable.render.text(),
orderable: false
},
{
targets: 3,
title: trans('admin.pluginVersion'),
data: 'version',
render: $.fn.dataTable.render.text(),
orderable: false
},
{
targets: 4,
data: 'dependencies',
title: trans('admin.pluginDependencies'),
data: 'dependencies',
searchable: false,
orderable: false,
render: data => {

View File

@ -31,7 +31,8 @@ const playersTableColumnDefs = [
},
{
targets: 2,
data: 'player_name'
data: 'player_name',
render: $.fn.dataTable.render.text()
},
{
targets: 3,

View File

@ -36,7 +36,7 @@ const pluginsTableColumnDefs = [
}
return `
<strong>${ title }</strong>
<strong>${ $.fn.dataTable.render.text().filter(title) }</strong>
<div class="actions">${ actions.join(' | ') }</div>
`;
}
@ -48,7 +48,7 @@ const pluginsTableColumnDefs = [
orderable: false,
render: (description, type, row) => {
return `
<div class="plugin-description"><p>${ description }</p></div>
<div class="plugin-description"><p>${ $.fn.dataTable.render.text().filter(description) }</p></div>
<div class="plugin-version-author">
${ trans('admin.pluginVersion') } <span class="text-primary">${ row.version }</span> |
${ trans('admin.pluginAuthor') } <a href="${ row.url }">${ row.author }</a> |

View File

@ -38,7 +38,8 @@ const usersTableColumnDefs = [
},
{
targets: 2,
data: 'nickname'
data: 'nickname',
render: $.fn.dataTable.render.text()
},
{
targets: 3,

View File

@ -105,6 +105,8 @@ function renderSkinlibItemComponent(item) {
title = item.liked ? trans('skinlib.removeFromCloset') : trans('skinlib.addToCloset');
}
item.name = $.fn.dataTable.render.text().filter(item.name);
return `<a href="${ url('skinlib/show/' + item.tid) }">
<div class="item" tid="${ item.tid }">
<div class="item-body">

View File

@ -115,26 +115,30 @@ async function initCloset() {
* @param {{ name: string, tid: number, type: 'steve' | 'alex' | 'cape' }} item
*/
function renderClosetItemComponent(item) {
// Prevent XSS
item.name = $.fn.dataTable.render.text().filter(item.name);
return `
<div class="item" tid="${item.tid}" data-texture-type="${item.type}">
<div class="item-body">
<img src="${url('/')}preview/${item.tid}.png">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${item.name}">${item.name} <small>(${item.type})</small></span>
</p>
<div class="item-body">
<img src="${url('/')}preview/${item.tid}.png">
</div>
<div class="item-footer">
<p class="texture-name">
<span title="${item.name}">${item.name} <small>(${item.type})</small></span>
</p>
<a href="${url('/')}skinlib/show/${item.tid}" title="${trans('user.viewInSkinlib')}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
<span title="${trans('general.more')}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
<a href="${url('/')}skinlib/show/${item.tid}" title="${trans('user.viewInSkinlib')}" class="more" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-share"></i></a>
<span title="${trans('general.more')}" class="more" data-toggle="dropdown" aria-haspopup="true" id="more-button"><i class="fa fa-cog"></i></span>
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
<li><a onclick="renameClosetItem(${item.tid}, '${item.name}');">${trans('user.renameItem')}</a></li>
<li><a onclick="removeFromCloset(${item.tid});">${trans('user.removeItem')}</a></li>
<li><a onclick="setAsAvatar(${item.tid});">${trans('user.setAsAvatar')}</a></li>
</ul>
<ul class="dropup dropdown-menu" aria-labelledby="more-button">
<li><a onclick="renameClosetItem(${item.tid}, '${item.name}');">${trans('user.renameItem')}</a></li>
<li><a onclick="removeFromCloset(${item.tid});">${trans('user.removeItem')}</a></li>
<li><a onclick="setAsAvatar(${item.tid});">${trans('user.setAsAvatar')}</a></li>
</ul>
</div>
</div>
</div>`;
`;
}
/**