more general solution addressing #554, kudos @rugk for the suggestions

This commit is contained in:
El RIDO 2020-01-04 11:34:16 +01:00
parent 6a3a8a395a
commit 2caddf985f
No known key found for this signature in database
GPG Key ID: 0F5C940A6BD81F92
14 changed files with 241 additions and 151 deletions

View File

@ -36,21 +36,6 @@ var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh'], supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh'],
mimeTypes = ['image/png', 'application/octet-stream'], mimeTypes = ['image/png', 'application/octet-stream'],
formats = ['plaintext', 'markdown', 'syntaxhighlighting'], formats = ['plaintext', 'markdown', 'syntaxhighlighting'],
/**
* character to HTML entity lookup table
*
* @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60}
*/
entityMap = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
},
logFile = fs.createWriteStream('test.log'), logFile = fs.createWriteStream('test.log'),
mimeFile = fs.createReadStream('/etc/mime.types'), mimeFile = fs.createReadStream('/etc/mime.types'),
mimeLine = ''; mimeLine = '';
@ -97,22 +82,6 @@ function parseMime(line) {
// common testing helper functions // common testing helper functions
/**
* convert all applicable characters to HTML entities
*
* @see {@link https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content}
* @name htmlEntities
* @function
* @param {string} str
* @return {string} escaped HTML
*/
exports.htmlEntities = function(str) {
return String(str).replace(
/[&<>"'`=\/]/g, function(s) {
return entityMap[s];
});
};
// provides random lowercase characters from a to z // provides random lowercase characters from a to z
exports.jscA2zString = function() { exports.jscA2zString = function() {
return jsc.elements(a2zString); return jsc.elements(a2zString);

View File

@ -267,6 +267,32 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
return false; return false;
} }
/**
* encode all applicable characters to HTML entities
*
* @see {@link https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html}
*
* @name Helper.htmlEntities
* @function
* @param string str
* @return string escaped HTML
*/
me.htmlEntities = function(str) {
// using textarea, since other tags may allow and execute scripts, even when detached from DOM
let holder = document.createElement('textarea');
holder.textContent = str;
// as per OWASP recommendation, also encoding quotes and slash
return holder.innerHTML.replace(
/["'\/]/g,
function(s) {
return {
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;'
}[s];
});
};
return me; return me;
})(); })();
@ -419,17 +445,31 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
args[0] = translations[messageId]; args[0] = translations[messageId];
} }
// messageID may contain links, but should be from a trusted source (code or translation JSON files)
let containsNoLinks = args[0].indexOf('<a') === -1;
for (let i = 0; i < args.length; ++i) {
// parameters (i > 0) may never contain HTML as they may come from untrusted parties
if (i > 0 || containsNoLinks) {
args[i] = Helper.htmlEntities(args[i]);
}
}
// format string // format string
var output = Helper.sprintf.apply(this, args); var output = Helper.sprintf.apply(this, args);
// if $element is given, apply text to element // if $element is given, apply text to element
if ($element !== null) { if ($element !== null) {
// get last text node of element if (containsNoLinks) {
var content = $element.contents(); // avoid HTML entity encoding if translation contains links
if (content.length > 1) {
content[content.length - 1].nodeValue = ' ' + output;
} else {
$element.text(output); $element.text(output);
} else {
// only allow tags/attributes we actually use in our translations
$element.html(
DOMPurify.sanitize(output, {
ALLOWED_TAGS: ['a', 'br', 'i', 'span'],
ALLOWED_ATTR: ['href', 'id']
})
);
} }
} }
@ -1052,28 +1092,35 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
icon = null; // icons not supported in this case icon = null; // icons not supported in this case
} }
} }
var $translationTarget = $element;
// handle icon // handle icon, if template uses one
if (icon !== null && // icon was passed var $glyphIcon = $element.find(':first');
icon !== currentIcon[id] // and it differs from current icon if ($glyphIcon.length) {
) { // if there is an icon, we need to provide an inner element
var $glyphIcon = $element.find(':first'); // to translate the message into, instead of the parent
$translationTarget = $('<span>');
$element.html(' ').prepend($glyphIcon).append($translationTarget);
// remove (previous) icon if (icon !== null && // icon was passed
$glyphIcon.removeClass(currentIcon[id]); icon !== currentIcon[id] // and it differs from current icon
) {
// remove (previous) icon
$glyphIcon.removeClass(currentIcon[id]);
// any other thing as a string (e.g. 'null') (only) removes the icon // any other thing as a string (e.g. 'null') (only) removes the icon
if (typeof icon === 'string') { if (typeof icon === 'string') {
// set new icon // set new icon
currentIcon[id] = 'glyphicon-' + icon; currentIcon[id] = 'glyphicon-' + icon;
$glyphIcon.addClass(currentIcon[id]); $glyphIcon.addClass(currentIcon[id]);
}
} }
} }
// show text // show text
if (args !== null) { if (args !== null) {
// add jQuery object to it as first parameter // add jQuery object to it as first parameter
args.unshift($element); args.unshift($translationTarget);
// pass it to I18n // pass it to I18n
I18n._.apply(this, args); I18n._.apply(this, args);
} }
@ -1764,9 +1811,9 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
// escape HTML entities, link URLs, sanitize // escape HTML entities, link URLs, sanitize
var escapedLinkedText = Helper.urls2links( var escapedLinkedText = Helper.urls2links(
$('<div />').text(text).html() Helper.htmlEntities(text)
), ),
sanitizedLinkedText = DOMPurify.sanitize(escapedLinkedText); sanitizedLinkedText = DOMPurify.sanitize(escapedLinkedText);
$plainText.html(sanitizedLinkedText); $plainText.html(sanitizedLinkedText);
$prettyPrint.html(sanitizedLinkedText); $prettyPrint.html(sanitizedLinkedText);
@ -2894,7 +2941,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
for (var i = 0; i < $head.length; i++) { for (var i = 0; i < $head.length; i++) {
newDoc.write($head[i].outerHTML); newDoc.write($head[i].outerHTML);
} }
newDoc.write('</head><body><pre>' + DOMPurify.sanitize(paste) + '</pre></body></html>'); newDoc.write('</head><body><pre>' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '</pre></body></html>');
newDoc.close(); newDoc.close();
} }

View File

@ -3,21 +3,56 @@ var common = require('../common');
describe('Alert', function () { describe('Alert', function () {
describe('showStatus', function () { describe('showStatus', function () {
before(function () {
cleanup();
});
jsc.property( jsc.property(
'shows a status message', 'shows a status message',
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
function (icon, message) {
icon = icon.join('');
message = message.join('');
var expected = '<div id="status">' + message + '</div>';
$('body').html(
'<div id="status"></div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showStatus(message, icon);
var result = $('body').html();
return expected === result;
}
);
jsc.property(
'shows a status message (bootstrap)',
jsc.array(common.jscAlnumString()),
function (message) {
message = message.join('');
var expected = '<div id="status" role="alert" ' +
'class="statusmessage alert alert-info"><span ' +
'class="glyphicon glyphicon-info-sign" ' +
'aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html(
'<div id="status" role="alert" class="statusmessage ' +
'alert alert-info hidden"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showStatus(message);
var result = $('body').html();
return expected === result;
}
);
jsc.property(
'shows a status message (bootstrap, custom icon)',
jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()),
function (icon, message) { function (icon, message) {
icon = icon.join(''); icon = icon.join('');
message = message.join(''); message = message.join('');
var expected = '<div id="status" role="alert" ' + var expected = '<div id="status" role="alert" ' +
'class="statusmessage alert alert-info"><span ' + 'class="statusmessage alert alert-info"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</div>'; '" aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( $('body').html(
'<div id="status" role="alert" class="statusmessage ' + '<div id="status" role="alert" class="statusmessage ' +
'alert alert-info hidden"><span class="glyphicon ' + 'alert alert-info hidden"><span class="glyphicon ' +
@ -32,12 +67,48 @@ describe('Alert', function () {
}); });
describe('showError', function () { describe('showError', function () {
before(function () { jsc.property(
cleanup(); 'shows an error message (basic)',
}); jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()),
function (icon, message) {
icon = icon.join('');
message = message.join('');
var expected = '<div id="errormessage">' + message + '</div>';
$('body').html(
'<div id="errormessage"></div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showError(message, icon);
var result = $('body').html();
return expected === result;
}
);
jsc.property( jsc.property(
'shows an error message', 'shows an error message (bootstrap)',
jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()),
function (icon, message) {
message = message.join('');
var expected = '<div id="errormessage" role="alert" ' +
'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-alert" ' +
'aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html(
'<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showError(message);
var result = $('body').html();
return expected === result;
}
);
jsc.property(
'shows an error message (bootstrap, custom icon)',
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
function (icon, message) { function (icon, message) {
@ -46,7 +117,7 @@ describe('Alert', function () {
var expected = '<div id="errormessage" role="alert" ' + var expected = '<div id="errormessage" role="alert" ' +
'class="statusmessage alert alert-danger"><span ' + 'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</div>'; '" aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( $('body').html(
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' + 'alert alert-danger hidden"><span class="glyphicon ' +
@ -61,12 +132,27 @@ describe('Alert', function () {
}); });
describe('showRemaining', function () { describe('showRemaining', function () {
before(function () { jsc.property(
cleanup(); 'shows remaining time (basic)',
}); jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()),
'integer',
function (message, string, number) {
message = message.join('');
string = string.join('');
var expected = '<div id="remainingtime" class="">' + string + message + number + '</div>';
$('body').html(
'<div id="remainingtime" class="hidden"></div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
var result = $('body').html();
return expected === result;
}
);
jsc.property( jsc.property(
'shows remaining time', 'shows remaining time (bootstrap)',
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
'integer', 'integer',
@ -76,7 +162,7 @@ describe('Alert', function () {
var expected = '<div id="remainingtime" role="alert" ' + var expected = '<div id="remainingtime" role="alert" ' +
'class="alert alert-info"><span ' + 'class="alert alert-info"><span ' +
'class="glyphicon glyphicon-fire" aria-hidden="true">' + 'class="glyphicon glyphicon-fire" aria-hidden="true">' +
'</span> ' + string + message + number + '</div>'; '</span> <span>' + string + message + number + '</span></div>';
$('body').html( $('body').html(
'<div id="remainingtime" role="alert" class="hidden ' + '<div id="remainingtime" role="alert" class="hidden ' +
'alert alert-info"><span class="glyphicon ' + 'alert alert-info"><span class="glyphicon ' +
@ -91,12 +177,30 @@ describe('Alert', function () {
}); });
describe('showLoading', function () { describe('showLoading', function () {
before(function () { jsc.property(
cleanup(); 'shows a loading message (basic)',
}); jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()),
function (message, icon) {
message = message.join('');
icon = icon.join('');
var defaultMessage = 'Loading…';
if (message.length === 0) {
message = defaultMessage;
}
var expected = '<div id="loadingindicator" class="">' + message + '</div>';
$('body').html(
'<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showLoading(message, icon);
var result = $('body').html();
return expected === result;
}
);
jsc.property( jsc.property(
'shows a loading message', 'shows a loading message (bootstrap)',
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
jsc.array(common.jscAlnumString()), jsc.array(common.jscAlnumString()),
function (message, icon) { function (message, icon) {
@ -109,7 +213,7 @@ describe('Alert', function () {
var expected = '<ul class="nav navbar-nav"><li ' + var expected = '<ul class="nav navbar-nav"><li ' +
'id="loadingindicator" class="navbar-text"><span ' + 'id="loadingindicator" class="navbar-text"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</li></ul>'; '" aria-hidden="true"></span> <span>' + message + '</span></li></ul>';
$('body').html( $('body').html(
'<ul class="nav navbar-nav"><li id="loadingindicator" ' + '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text hidden"><span class="glyphicon ' + 'class="navbar-text hidden"><span class="glyphicon ' +
@ -125,10 +229,6 @@ describe('Alert', function () {
}); });
describe('hideLoading', function () { describe('hideLoading', function () {
before(function () {
cleanup();
});
it( it(
'hides the loading message', 'hides the loading message',
function() { function() {
@ -150,10 +250,6 @@ describe('Alert', function () {
}); });
describe('hideMessages', function () { describe('hideMessages', function () {
before(function () {
cleanup();
});
it( it(
'hides all messages', 'hides all messages',
function() { function() {
@ -176,10 +272,6 @@ describe('Alert', function () {
}); });
describe('setCustomHandler', function () { describe('setCustomHandler', function () {
before(function () {
cleanup();
});
jsc.property( jsc.property(
'calls a given handler function', 'calls a given handler function',
'nat 3', 'nat 3',

View File

@ -4,9 +4,6 @@ var common = require('../common');
describe('AttachmentViewer', function () { describe('AttachmentViewer', function () {
describe('setAttachment, showAttachment, removeAttachment, hideAttachment, hideAttachmentPreview, hasAttachment, getAttachment & moveAttachmentTo', function () { describe('setAttachment, showAttachment, removeAttachment, hideAttachment, hideAttachmentPreview, hasAttachment, getAttachment & moveAttachmentTo', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'displays & hides data as requested', 'displays & hides data as requested',
@ -24,7 +21,8 @@ describe('AttachmentViewer', function () {
mimeType.substring(0, 6) === 'video/' || mimeType.substring(0, 6) === 'video/' ||
mimeType.match(/\/pdf/i) mimeType.match(/\/pdf/i)
), ),
results = []; results = [],
result = '';
prefix = prefix.replace(/%(s|d)/g, '%%'); prefix = prefix.replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
$('body').html( $('body').html(
@ -72,13 +70,19 @@ describe('AttachmentViewer', function () {
!$('#attachment').hasClass('hidden') && !$('#attachment').hasClass('hidden') &&
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
var element = $('<div></div>'); let element = $('<div>');
$.PrivateBin.AttachmentViewer.moveAttachmentTo(element, prefix + '%s' + postfix); $.PrivateBin.AttachmentViewer.moveAttachmentTo(element, prefix + '%s' + postfix);
// messageIDs with links get a relaxed treatment
if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) {
result = $.PrivateBin.Helper.htmlEntities(prefix + filename + postfix);
} else {
result = $('<div>').html(prefix + $.PrivateBin.Helper.htmlEntities(filename) + postfix).html();
}
if (filename.length) { if (filename.length) {
results.push( results.push(
element.children()[0].href === data && element.children()[0].href === data &&
element.children()[0].getAttribute('download') === filename && element.children()[0].getAttribute('download') === filename &&
element.children()[0].text === prefix + filename + postfix element.children()[0].text === result
); );
} else { } else {
results.push(element.children()[0].href === data); results.push(element.children()[0].href === data);

View File

@ -4,9 +4,6 @@ var common = require('../common');
describe('DiscussionViewer', function () { describe('DiscussionViewer', function () {
describe('handleNotification, prepareNewDiscussion, addComment, finishDiscussion, getReplyMessage, getReplyNickname, getReplyCommentId & highlightComment', function () { describe('handleNotification, prepareNewDiscussion, addComment, finishDiscussion, getReplyMessage, getReplyNickname, getReplyCommentId & highlightComment', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'displays & hides comments as requested', 'displays & hides comments as requested',

View File

@ -4,9 +4,6 @@ require('../common');
describe('Editor', function () { describe('Editor', function () {
describe('show, hide, getText, setText & isPreview', function () { describe('show, hide, getText, setText & isPreview', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'returns text fed into the textarea, handles editor tabs', 'returns text fed into the textarea, handles editor tabs',

View File

@ -3,10 +3,6 @@ var common = require('../common');
describe('Helper', function () { describe('Helper', function () {
describe('secondsToHuman', function () { describe('secondsToHuman', function () {
after(function () {
cleanup();
});
jsc.property('returns an array with a number and a word', 'integer', function (number) { jsc.property('returns an array with a number and a word', 'integer', function (number) {
var result = $.PrivateBin.Helper.secondsToHuman(number); var result = $.PrivateBin.Helper.secondsToHuman(number);
return Array.isArray(result) && return Array.isArray(result) &&
@ -57,11 +53,11 @@ describe('Helper', function () {
'nearray string', 'nearray string',
function (ids, contents) { function (ids, contents) {
var html = '', var html = '',
result = true; result = true,
clean = jsdom(html);
ids.forEach(function(item, i) { ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + common.htmlEntities(contents[i] || contents[0]) + '</div>'; html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
}); });
var clean = jsdom(html);
// TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet. // TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet.
// Once there is one, uncomment the block below to actually check the result. // Once there is one, uncomment the block below to actually check the result.
/* /*
@ -77,8 +73,8 @@ describe('Helper', function () {
}); });
describe('urls2links', function () { describe('urls2links', function () {
after(function () { before(function () {
cleanup(); cleanup = jsdom();
}); });
jsc.property( jsc.property(
@ -97,11 +93,11 @@ describe('Helper', function () {
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
'string', 'string',
function (prefix, schema, address, query, fragment, postfix) { function (prefix, schema, address, query, fragment, postfix) {
var query = query.join(''), var query = query.join(''),
fragment = fragment.join(''), fragment = fragment.join(''),
url = schema + '://' + address.join('') + '/?' + query + '#' + fragment, url = schema + '://' + address.join('') + '/?' + query + '#' + fragment,
prefix = common.htmlEntities(prefix), prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = ' ' + common.htmlEntities(postfix); postfix = ' ' + $.PrivateBin.Helper.htmlEntities(postfix);
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x // special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x
if ( if (
@ -122,19 +118,15 @@ describe('Helper', function () {
jsc.array(common.jscQueryString()), jsc.array(common.jscQueryString()),
'string', 'string',
function (prefix, query, postfix) { function (prefix, query, postfix) {
var url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm,''), var url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm,''),
prefix = common.htmlEntities(prefix), prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = common.htmlEntities(postfix); postfix = $.PrivateBin.Helper.htmlEntities(postfix);
return prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a> ' + postfix === $.PrivateBin.Helper.urls2links(prefix + url + ' ' + postfix); return prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a> ' + postfix === $.PrivateBin.Helper.urls2links(prefix + url + ' ' + postfix);
} }
); );
}); });
describe('sprintf', function () { describe('sprintf', function () {
after(function () {
cleanup();
});
jsc.property( jsc.property(
'replaces %s in strings with first given parameter', 'replaces %s in strings with first given parameter',
'string', 'string',
@ -211,6 +203,10 @@ describe('Helper', function () {
describe('getCookie', function () { describe('getCookie', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'returns the requested cookie', 'returns the requested cookie',
'nearray asciinestring', 'nearray asciinestring',
@ -261,16 +257,16 @@ describe('Helper', function () {
}); });
describe('htmlEntities', function () { describe('htmlEntities', function () {
after(function () { before(function () {
cleanup(); cleanup = jsdom();
}); });
jsc.property( jsc.property(
'removes all HTML entities from any given string', 'removes all HTML entities from any given string',
'string', 'string',
function (string) { function (string) {
var result = common.htmlEntities(string); var result = $.PrivateBin.Helper.htmlEntities(string);
return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result))); return !(/[<>]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result)));
} }
); );
}); });

View File

@ -32,6 +32,7 @@ describe('I18n', function () {
var fakeAlias = $.PrivateBin.I18n._(fake); var fakeAlias = $.PrivateBin.I18n._(fake);
$.PrivateBin.I18n.reset(); $.PrivateBin.I18n.reset();
messageId = $.PrivateBin.Helper.htmlEntities(messageId);
return messageId === result && messageId === alias && return messageId === result && messageId === alias &&
messageId === pluralResult && messageId === pluralAlias && messageId === pluralResult && messageId === pluralAlias &&
messageId === fakeResult && messageId === fakeAlias; messageId === fakeResult && messageId === fakeAlias;
@ -46,7 +47,7 @@ describe('I18n', function () {
prefix = prefix.replace(/%(s|d)/g, '%%'); prefix = prefix.replace(/%(s|d)/g, '%%');
params[0] = params[0].replace(/%(s|d)/g, '%%'); params[0] = params[0].replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
var translation = prefix + params[0] + postfix; var translation = $.PrivateBin.Helper.htmlEntities(prefix + params[0] + postfix);
params.unshift(prefix + '%s' + postfix); params.unshift(prefix + '%s' + postfix);
var result = $.PrivateBin.I18n.translate.apply(this, params); var result = $.PrivateBin.I18n.translate.apply(this, params);
$.PrivateBin.I18n.reset(); $.PrivateBin.I18n.reset();

View File

@ -5,18 +5,18 @@ describe('Model', function () {
describe('getExpirationDefault', function () { describe('getExpirationDefault', function () {
before(function () { before(function () {
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
cleanup(); cleanup = jsdom();
}); });
jsc.property( jsc.property(
'returns the contents of the element with id "pasteExpiration"', 'returns the contents of the element with id "pasteExpiration"',
'array asciinestring', 'nearray asciinestring',
'string', 'string',
'small nat', 'small nat',
function (keys, value, key) { function (keys, value, key) {
keys = keys.map(common.htmlEntities); keys = keys.map($.PrivateBin.Helper.htmlEntities);
value = common.htmlEntities(value); value = $.PrivateBin.Helper.htmlEntities(value);
var content = keys.length > key ? keys[key] : (keys.length > 0 ? keys[0] : 'null'), var content = keys.length > key ? keys[key] : keys[0],
contents = '<select id="pasteExpiration" name="pasteExpiration">'; contents = '<select id="pasteExpiration" name="pasteExpiration">';
keys.forEach(function(item) { keys.forEach(function(item) {
contents += '<option value="' + item + '"'; contents += '<option value="' + item + '"';
@ -27,7 +27,7 @@ describe('Model', function () {
}); });
contents += '</select>'; contents += '</select>';
$('body').html(contents); $('body').html(contents);
var result = common.htmlEntities( var result = $.PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getExpirationDefault() $.PrivateBin.Model.getExpirationDefault()
); );
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
@ -39,18 +39,20 @@ describe('Model', function () {
describe('getFormatDefault', function () { describe('getFormatDefault', function () {
before(function () { before(function () {
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
});
after(function () {
cleanup(); cleanup();
}); });
jsc.property( jsc.property(
'returns the contents of the element with id "pasteFormatter"', 'returns the contents of the element with id "pasteFormatter"',
'array asciinestring', 'nearray asciinestring',
'string', 'string',
'small nat', 'small nat',
function (keys, value, key) { function (keys, value, key) {
keys = keys.map(common.htmlEntities); keys = keys.map($.PrivateBin.Helper.htmlEntities);
value = common.htmlEntities(value); value = $.PrivateBin.Helper.htmlEntities(value);
var content = keys.length > key ? keys[key] : (keys.length > 0 ? keys[0] : 'null'), var content = keys.length > key ? keys[key] : keys[0],
contents = '<select id="pasteFormatter" name="pasteFormatter">'; contents = '<select id="pasteFormatter" name="pasteFormatter">';
keys.forEach(function(item) { keys.forEach(function(item) {
contents += '<option value="' + item + '"'; contents += '<option value="' + item + '"';
@ -61,7 +63,7 @@ describe('Model', function () {
}); });
contents += '</select>'; contents += '</select>';
$('body').html(contents); $('body').html(contents);
var result = common.htmlEntities( var result = $.PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getFormatDefault() $.PrivateBin.Model.getFormatDefault()
); );
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
@ -74,7 +76,6 @@ describe('Model', function () {
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
cleanup();
}); });
jsc.property( jsc.property(
@ -185,7 +186,6 @@ describe('Model', function () {
describe('getTemplate', function () { describe('getTemplate', function () {
before(function () { before(function () {
$.PrivateBin.Model.reset(); $.PrivateBin.Model.reset();
cleanup();
}); });
jsc.property( jsc.property(

View File

@ -39,9 +39,6 @@ describe('PasteStatus', function () {
describe('showRemainingTime', function () { describe('showRemainingTime', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'shows burn after reading message or remaining time', 'shows burn after reading message or remaining time',
@ -84,10 +81,6 @@ describe('PasteStatus', function () {
}); });
describe('hideMessages', function () { describe('hideMessages', function () {
before(function () {
cleanup();
});
it( it(
'hides all messages', 'hides all messages',
function() { function() {

View File

@ -4,9 +4,6 @@ var common = require('../common');
describe('PasteViewer', function () { describe('PasteViewer', function () {
describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () { describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
cleanup();
});
jsc.property( jsc.property(
'displays text according to format', 'displays text according to format',

View File

@ -6,10 +6,6 @@ describe('Prompt', function () {
// in nodejs -> replace the prompt in the "page" template with a modal // in nodejs -> replace the prompt in the "page" template with a modal
describe('requestPassword & getPassword', function () { describe('requestPassword & getPassword', function () {
this.timeout(30000); this.timeout(30000);
before(function () {
$.PrivateBin.Model.reset();
cleanup();
});
jsc.property( jsc.property(
'returns the password fed into the dialog', 'returns the password fed into the dialog',
@ -26,6 +22,7 @@ describe('Prompt', function () {
'password"></div><button type="submit">Decrypt</button>' + 'password"></div><button type="submit">Decrypt</button>' +
'</form></div></div></div></div>' '</form></div></div></div></div>'
); );
$.PrivateBin.Model.reset();
$.PrivateBin.Model.init(); $.PrivateBin.Model.init();
$.PrivateBin.Prompt.init(); $.PrivateBin.Prompt.init();
$.PrivateBin.Prompt.requestPassword(); $.PrivateBin.Prompt.requestPassword();

View File

@ -75,7 +75,7 @@ if ($MARKDOWN):
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.7.js" integrity="sha512-XjNEK1xwh7SJ/7FouwV4VZcGW9cMySL3SwNpXgrURLBcXXQYtZdqhGoNdEwx9vwLvFjUGDQVNgpOrTsXlSTiQg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-2.0.7.js" integrity="sha512-XjNEK1xwh7SJ/7FouwV4VZcGW9cMySL3SwNpXgrURLBcXXQYtZdqhGoNdEwx9vwLvFjUGDQVNgpOrTsXlSTiQg==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5xT6WHyurvC0LFESbkycBMAjhsi0KL/Xhx2oU+d0bqiJPkWK6ZAZgZ7I02oQiXaLtFSLf7UqpBo5XEePlfhH7Q==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-XauVNDkk7v8lwDf2WmHWzloKuejJKJpfEvjBBf4bJV45VRP2SX6tg7ngNhmywIay8vNgoFhVBZZoe3y9tEio8Q==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->

View File

@ -53,7 +53,7 @@ if ($MARKDOWN):
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.7.js" integrity="sha512-XjNEK1xwh7SJ/7FouwV4VZcGW9cMySL3SwNpXgrURLBcXXQYtZdqhGoNdEwx9vwLvFjUGDQVNgpOrTsXlSTiQg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-2.0.7.js" integrity="sha512-XjNEK1xwh7SJ/7FouwV4VZcGW9cMySL3SwNpXgrURLBcXXQYtZdqhGoNdEwx9vwLvFjUGDQVNgpOrTsXlSTiQg==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5xT6WHyurvC0LFESbkycBMAjhsi0KL/Xhx2oU+d0bqiJPkWK6ZAZgZ7I02oQiXaLtFSLf7UqpBo5XEePlfhH7Q==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-XauVNDkk7v8lwDf2WmHWzloKuejJKJpfEvjBBf4bJV45VRP2SX6tg7ngNhmywIay8vNgoFhVBZZoe3y9tEio8Q==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->