more general solution addressing #554, kudos @rugk for the suggestions
This commit is contained in:
parent
8d0ac336d2
commit
4bf7f863dc
31
js/common.js
31
js/common.js
@ -40,21 +40,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 = {
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'"': '"',
|
|
||||||
"'": ''',
|
|
||||||
'/': '/',
|
|
||||||
'`': '`',
|
|
||||||
'=': '='
|
|
||||||
},
|
|
||||||
mimeFile = fs.createReadStream('/etc/mime.types'),
|
mimeFile = fs.createReadStream('/etc/mime.types'),
|
||||||
mimeLine = '';
|
mimeLine = '';
|
||||||
|
|
||||||
@ -97,22 +82,6 @@ function parseMime(line) {
|
|||||||
exports.atob = atob;
|
exports.atob = atob;
|
||||||
exports.btoa = btoa;
|
exports.btoa = btoa;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
||||||
|
@ -440,7 +440,33 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
|
|
||||||
expirationDate = expirationDate.setUTCSeconds(expirationDate.getUTCSeconds() + secondsToExpiration);
|
expirationDate = expirationDate.setUTCSeconds(expirationDate.getUTCSeconds() + secondsToExpiration);
|
||||||
return expirationDate;
|
return expirationDate;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
'"': '"',
|
||||||
|
"'": ''',
|
||||||
|
'/': '/'
|
||||||
|
}[s];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
})();
|
})();
|
||||||
@ -592,16 +618,31 @@ jQuery.PrivateBin = (function($, 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
|
||||||
let output = Helper.sprintf.apply(this, args);
|
let 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) {
|
||||||
// avoid HTML entity encoding if translation contains link
|
if (containsNoLinks) {
|
||||||
if (output.indexOf('<a') === -1) {
|
// avoid HTML entity encoding if translation contains links
|
||||||
$element.text(output);
|
$element.text(output);
|
||||||
} else {
|
} else {
|
||||||
$element.html(output);
|
// 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']
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2362,7 +2403,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
|
|
||||||
// escape HTML entities, link URLs, sanitize
|
// escape HTML entities, link URLs, sanitize
|
||||||
const escapedLinkedText = Helper.urls2links(
|
const escapedLinkedText = Helper.urls2links(
|
||||||
$('<div />').text(text).html()
|
Helper.htmlEntities(text)
|
||||||
),
|
),
|
||||||
sanitizedLinkedText = DOMPurify.sanitize(escapedLinkedText);
|
sanitizedLinkedText = DOMPurify.sanitize(escapedLinkedText);
|
||||||
$plainText.html(sanitizedLinkedText);
|
$plainText.html(sanitizedLinkedText);
|
||||||
@ -2796,11 +2837,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
$attachmentLink.appendTo($element);
|
$attachmentLink.appendTo($element);
|
||||||
|
|
||||||
// update text - ensuring no HTML is inserted into the text node
|
// update text - ensuring no HTML is inserted into the text node
|
||||||
I18n._(
|
I18n._($attachmentLink, label, $attachmentLink.attr('download'));
|
||||||
$attachmentLink,
|
|
||||||
$('<div />').text(label).html(),
|
|
||||||
$('<div />').text($attachmentLink.attr('download')).html()
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3498,7 +3535,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
for (let i = 0; i < $head.length; ++i) {
|
for (let i = 0; i < $head.length; ++i) {
|
||||||
newDoc.write($head[i].outerHTML);
|
newDoc.write($head[i].outerHTML);
|
||||||
}
|
}
|
||||||
newDoc.write('</head><body><pre>' + DOMPurify.sanitize($('<div />').text(paste).html()) + '</pre></body></html>');
|
newDoc.write('</head><body><pre>' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '</pre></body></html>');
|
||||||
newDoc.close();
|
newDoc.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,10 +67,6 @@ describe('Alert', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('showWarning', function () {
|
describe('showWarning', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'shows a warning message (basic)',
|
'shows a warning message (basic)',
|
||||||
jsc.array(common.jscAlnumString()),
|
jsc.array(common.jscAlnumString()),
|
||||||
@ -136,10 +132,6 @@ describe('Alert', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('showError', function () {
|
describe('showError', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'shows an error message (basic)',
|
'shows an error message (basic)',
|
||||||
jsc.array(common.jscAlnumString()),
|
jsc.array(common.jscAlnumString()),
|
||||||
@ -205,10 +197,6 @@ describe('Alert', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('showRemaining', function () {
|
describe('showRemaining', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'shows remaining time (basic)',
|
'shows remaining time (basic)',
|
||||||
jsc.array(common.jscAlnumString()),
|
jsc.array(common.jscAlnumString()),
|
||||||
@ -254,10 +242,6 @@ describe('Alert', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('showLoading', function () {
|
describe('showLoading', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'shows a loading message (basic)',
|
'shows a loading message (basic)',
|
||||||
jsc.array(common.jscAlnumString()),
|
jsc.array(common.jscAlnumString()),
|
||||||
@ -310,10 +294,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() {
|
||||||
@ -335,10 +315,6 @@ describe('Alert', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('hideMessages', function () {
|
describe('hideMessages', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'hides all messages',
|
'hides all messages',
|
||||||
function() {
|
function() {
|
||||||
@ -361,10 +337,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',
|
||||||
|
@ -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',
|
||||||
@ -16,7 +13,7 @@ describe('AttachmentViewer', function () {
|
|||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
function (mimeType, rawdata, filename, prefix, postfix) {
|
function (mimeType, rawdata, filename, prefix, postfix) {
|
||||||
var clean = jsdom(),
|
let clean = jsdom(),
|
||||||
data = 'data:' + mimeType + ';base64,' + btoa(rawdata),
|
data = 'data:' + mimeType + ';base64,' + btoa(rawdata),
|
||||||
previewSupported = (
|
previewSupported = (
|
||||||
mimeType.substring(0, 6) === 'image/' ||
|
mimeType.substring(0, 6) === 'image/' ||
|
||||||
@ -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(
|
||||||
@ -57,7 +55,7 @@ describe('AttachmentViewer', function () {
|
|||||||
}
|
}
|
||||||
// beyond this point we will get the blob URL instead of the data
|
// beyond this point we will get the blob URL instead of the data
|
||||||
data = window.URL.createObjectURL(data);
|
data = window.URL.createObjectURL(data);
|
||||||
var attachment = $.PrivateBin.AttachmentViewer.getAttachment();
|
const attachment = $.PrivateBin.AttachmentViewer.getAttachment();
|
||||||
results.push(
|
results.push(
|
||||||
$.PrivateBin.AttachmentViewer.hasAttachment() &&
|
$.PrivateBin.AttachmentViewer.hasAttachment() &&
|
||||||
$('#attachment').hasClass('hidden') &&
|
$('#attachment').hasClass('hidden') &&
|
||||||
@ -84,13 +82,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 === $('<div />').text(prefix + filename + postfix).html()
|
element.children()[0].text === result
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
results.push(element.children()[0].href === data);
|
results.push(element.children()[0].href === data);
|
||||||
|
@ -5,9 +5,6 @@ var common = require('../common');
|
|||||||
describe('Check', function () {
|
describe('Check', function () {
|
||||||
describe('init', function () {
|
describe('init', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false and shows error, if a bot UA is detected', function () {
|
it('returns false and shows error, if a bot UA is detected', function () {
|
||||||
jsc.assert(jsc.forall(
|
jsc.assert(jsc.forall(
|
||||||
|
@ -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',
|
||||||
|
@ -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',
|
||||||
|
@ -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. � or &#x
|
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. � 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,7 +203,7 @@ describe('Helper', function () {
|
|||||||
|
|
||||||
describe('getCookie', function () {
|
describe('getCookie', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
after(function () {
|
before(function () {
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -263,16 +255,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 && !(/&/.test(result)));
|
return !(/[<>]/.test(result)) && !(string.indexOf('&') > -1 && !(/&/.test(result)));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
@ -130,7 +131,6 @@ describe('Model', function () {
|
|||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
@ -238,7 +238,6 @@ describe('Model', function () {
|
|||||||
describe('getTemplate', function () {
|
describe('getTemplate', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
|
@ -36,9 +36,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 v1',
|
'shows burn after reading message or remaining time v1',
|
||||||
@ -121,10 +118,6 @@ describe('PasteStatus', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('hideMessages', function () {
|
describe('hideMessages', function () {
|
||||||
before(function () {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'hides all messages',
|
'hides all messages',
|
||||||
function() {
|
function() {
|
||||||
|
@ -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',
|
||||||
|
@ -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();
|
||||||
|
@ -72,7 +72,7 @@ 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/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-HxU8g6MFviBCv6fEKEL+bn0fICxg9++ZUxxqySWXkX4iGgLodH/OVZDfr3dgKK5vZAv+0niWLuf+CWZ+/gUwWw==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vV7gNbSUVUstR7GC+oZ050bVkHkjezDQW40KrIKWlhvZoJyuLMpH/jF9eN1Wd8/YJd4eovY4LTPJDHvwI3pwjg==" crossorigin="anonymous"></script>
|
||||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
||||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
||||||
<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
|
<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
|
||||||
|
@ -50,7 +50,7 @@ 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/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-HxU8g6MFviBCv6fEKEL+bn0fICxg9++ZUxxqySWXkX4iGgLodH/OVZDfr3dgKK5vZAv+0niWLuf+CWZ+/gUwWw==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vV7gNbSUVUstR7GC+oZ050bVkHkjezDQW40KrIKWlhvZoJyuLMpH/jF9eN1Wd8/YJd4eovY4LTPJDHvwI3pwjg==" crossorigin="anonymous"></script>
|
||||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
||||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
||||||
<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
|
<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
|
||||||
|
Loading…
Reference in New Issue
Block a user