Merge branch 'master' into sanitizeMarkdown, changing test to use new library

This commit is contained in:
El RIDO 2017-11-22 06:30:38 +01:00
commit 233bd65b00
No known key found for this signature in database
GPG key ID: 0F5C940A6BD81F92
10 changed files with 856 additions and 49 deletions

2
.gitattributes vendored
View file

@ -2,8 +2,6 @@ doc/ export-ignore
tst/ export-ignore
js/.istanbul.yml export-ignore
js/test.js export-ignore
js/mocha-3.2.0.js export-ignore
css/mocha-3.2.0.css export-ignore
.codeclimate.yml export-ignore
.csslintrc export-ignore
.dockerignore export-ignore

View file

@ -3,10 +3,11 @@ preset: recommended
risky: false
enabled:
- no_empty_comment
- align_equals
- long_array_syntax
- concat_with_spaces
- long_array_syntax
- no_empty_comment
- pre_increment
disabled:
- blank_line_after_opening_tag
@ -23,6 +24,7 @@ disabled:
- phpdoc_separation
- phpdoc_single_line_var_spacing
- phpdoc_summary
- post_increment
- short_array_syntax
- single_line_after_imports
- unalign_equals

View file

@ -302,8 +302,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
* internationalization module
*
* @name I18n
* @param {object} window
* @param {object} document
* @class
*/
var I18n = (function () {
@ -414,7 +412,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
var orgArguments = arguments;
$(document).on(languageLoadedEvent, function () {
// log to show that the previous error could be mitigated
console.log('Fix missing translation of \'' + messageId + '\' with now loaded language ' + language);
console.warn('Fix missing translation of \'' + messageId + '\' with now loaded language ' + language);
// re-execute this function
me.translate.apply(this, orgArguments);
});
@ -845,11 +843,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
* everything directly UI-related, which fits nowhere else
*
* @name UiHelper
* @param {object} window
* @param {object} document
* @class
*/
var UiHelper = (function (window, document) {
var UiHelper = (function () {
var me = {};
/**
@ -866,7 +862,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
{
var currentLocation = Helper.baseUri();
if (event.originalEvent.state === null && // no state object passed
event.originalEvent.target.location.href === currentLocation && // target location is home page
event.target.location.href === currentLocation && // target location is home page
window.location.href === currentLocation // and we are not already on the home page
) {
// redirect to home page
@ -958,6 +954,23 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
});
}
/**
* trigger a history (pop) state change
*
* used to test the UiHelper.historyChange private function
*
* @name UiHelper.mockHistoryChange
* @function
* @param {string} state (optional) state to mock
*/
me.mockHistoryChange = function(state)
{
if (typeof state === 'undefined') {
state = null;
}
historyChange($.Event('popstate', {originalEvent: new PopStateEvent('popstate', {state: state}), target: window}));
}
/**
* initialize
*
@ -973,7 +986,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
}
return me;
})(window, document);
})();
/**
* Alert/error manager
@ -989,12 +1002,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
$statusMessage,
$remainingTime;
var currentIcon = [
'glyphicon-time', // loading icon
'glyphicon-info-sign', // status icon
'', // resevered for warning, not used yet
'glyphicon-alert' // error icon
];
var currentIcon;
var alertType = [
'loading', // not in bootstrap, but using a good value here
@ -1090,7 +1098,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
*/
me.showStatus = function(message, icon, dismissable, autoclose)
{
console.log('status shown: ', message);
console.info('status shown: ', message);
// @TODO: implement dismissable
// @TODO: implement autoclose
@ -1133,7 +1141,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
*/
me.showRemaining = function(message)
{
console.log('remaining message shown: ', message);
console.info('remaining message shown: ', message);
handleNotification(1, $remainingTime, message);
}
@ -1151,7 +1159,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
me.showLoading = function(message, percentage, icon)
{
if (typeof message !== 'undefined' && message !== null) {
console.log('status changed: ', message);
console.info('status changed: ', message);
}
// default message text
@ -1238,6 +1246,13 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
$loadingIndicator = $('#loadingindicator');
$statusMessage = $('#status');
$remainingTime = $('#remainingtime');
currentIcon = [
'glyphicon-time', // loading icon
'glyphicon-info-sign', // status icon
'', // reserved for warning, not used yet
'glyphicon-alert' // error icon
];
}
return me;
@ -1247,10 +1262,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
* handles paste status/result
*
* @name PasteStatus
* @param {object} window
* @class
*/
var PasteStatus = (function (window) {
var PasteStatus = (function () {
var me = {};
var $pasteSuccess,
@ -1351,7 +1365,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
];
Alert.showRemaining([expirationLabel, expiration[0]]);
$remainingTime.removeClass('foryoureyesonly')
$remainingTime.removeClass('foryoureyesonly');
} else {
// never expires
return;
@ -1383,7 +1397,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
*/
me.init = function()
{
$pasteSuccess = $('#pasteSuccess');
$pasteSuccess = $('#pastesuccess');
// $pasteUrl is saved in me.createPasteNotification() after creation
$remainingTime = $('#remainingtime');
$shortenButton = $('#shortenbutton');
@ -1393,7 +1407,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
}
return me;
})(window);
})();
/**
* password prompt
@ -1454,7 +1468,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
throw 'password prompt canceled';
}
if (password.length === 0) {
// recursive…
// recurse…
return me.requestPassword();
}
@ -1827,7 +1841,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
*
* @name PasteViewer.setFormat
* @function
* @param {string} newFormat the the new format
* @param {string} newFormat the new format
*/
me.setFormat = function(newFormat)
{
@ -1836,7 +1850,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
return;
}
// needs to update display too, if from or to Markdown is switched
// needs to update display too, if we switch from or to Markdown
if (format === 'markdown' || newFormat === 'markdown') {
isDisplayed = false;
}
@ -1965,6 +1979,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
// get default option from template/HTML or fall back to set value
format = Model.getFormatDefault() || format;
text = '';
isDisplayed = false;
isChanged = true;
}
return me;
@ -2641,7 +2658,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
me.showViewButtons = function()
{
if (viewButtonsDisplayed) {
console.log('showViewButtons: view buttons are already displayed');
console.warn('showViewButtons: view buttons are already displayed');
return;
}
@ -2661,7 +2678,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
me.hideViewButtons = function()
{
if (!viewButtonsDisplayed) {
console.log('hideViewButtons: view buttons are already hidden');
console.warn('hideViewButtons: view buttons are already hidden');
return;
}
@ -2693,7 +2710,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
me.showCreateButtons = function()
{
if (createButtonsDisplayed) {
console.log('showCreateButtons: create buttons are already displayed');
console.warn('showCreateButtons: create buttons are already displayed');
return;
}
@ -2718,7 +2735,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
me.hideCreateButtons = function()
{
if (!createButtonsDisplayed) {
console.log('hideCreateButtons: create buttons are already hidden');
console.warn('hideCreateButtons: create buttons are already hidden');
return;
}
@ -3970,7 +3987,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
Uploader.setUnencryptedData('deletetoken', deleteToken);
Uploader.setFailure(function () {
Controller.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
Alert.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
})
Uploader.run();
}

View file

@ -22,11 +22,17 @@ global.sjcl = require('./sjcl-1.0.6');
global.Base64 = require('./base64-2.1.9').Base64;
global.RawDeflate = require('./rawdeflate-0.5').RawDeflate;
global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate;
require('./prettify');
global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR.prettyPrintOne;
global.showdown = require('./showdown-1.6.1');
global.DOMPurify = require('./purify.min');
require('./bootstrap-3.3.7');
require('./privatebin');
// redirect console messages to log file
console.warn = console.error = function (msg) {
logFile.write(msg + '\n');
console.info = console.warn = console.error = function () {
logFile.write(Array.prototype.slice.call(arguments).join('') + '\n');
}
describe('Helper', function () {
@ -182,7 +188,7 @@ describe('Helper', function () {
jsc.array(jsc.elements(queryString)),
'string',
function (prefix, query, postfix) {
var url = 'magnet:?' + query.join(''),
var url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm,''),
prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = $.PrivateBin.Helper.htmlEntities(postfix),
element = $('<div>' + prefix + url + ' ' + postfix + '</div>');
@ -599,6 +605,82 @@ describe('Model', function () {
);
});
describe('getFormatDefault', function () {
before(function () {
$.PrivateBin.Model.reset();
cleanup();
});
jsc.property(
'returns the contents of the element with id "pasteFormatter"',
'array asciinestring',
'string',
'small nat',
function (keys, value, key) {
keys = keys.map($.PrivateBin.Helper.htmlEntities);
value = $.PrivateBin.Helper.htmlEntities(value);
var content = keys.length > key ? keys[key] : (keys.length > 0 ? keys[0] : 'null'),
contents = '<select id="pasteFormatter" name="pasteFormatter">';
keys.forEach(function(item) {
contents += '<option value="' + item + '"';
if (item === content) {
contents += ' selected="selected"';
}
contents += '>' + value + '</option>';
});
contents += '</select>';
$('body').html(contents);
var result = $.PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getFormatDefault()
);
$.PrivateBin.Model.reset();
return content === result;
}
);
});
describe('hasCipherData', function () {
before(function () {
$.PrivateBin.Model.reset();
cleanup();
});
jsc.property(
'checks if the element with id "cipherdata" contains any data',
'asciistring',
function (value) {
value = $.PrivateBin.Helper.htmlEntities(value).trim();
$('body').html('<div id="cipherdata">' + value + '</div>');
$.PrivateBin.Model.init();
var result = $.PrivateBin.Model.hasCipherData();
$.PrivateBin.Model.reset();
return (value.length > 0) === result;
}
);
});
describe('getCipherData', function () {
before(function () {
$.PrivateBin.Model.reset();
cleanup();
});
jsc.property(
'returns the contents of the element with id "cipherdata"',
'asciistring',
function (value) {
value = $.PrivateBin.Helper.htmlEntities(value).trim();
$('body').html('<div id="cipherdata">' + value + '</div>');
$.PrivateBin.Model.init();
var result = $.PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getCipherData()
);
$.PrivateBin.Model.reset();
return value === result;
}
);
});
describe('getPasteId', function () {
this.timeout(30000);
before(function () {
@ -710,4 +792,701 @@ describe('Model', function () {
}
);
});
describe('getTemplate', function () {
before(function () {
$.PrivateBin.Model.reset();
cleanup();
});
jsc.property(
'returns the contents of the element with id "[name]template"',
jsc.nearray(jsc.elements(alnumString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(alnumString)),
function (id, element, value) {
id = id.join('');
element = element.join('');
value = value.join('').trim();
// <br>, <hr>, <img> and <wbr> tags can't contain strings,
// table tags can't be alone, so test with a <p> instead
if (['br', 'col', 'hr', 'img', 'tr', 'td', 'th', 'wbr'].indexOf(element) >= 0) {
element = 'p';
}
$('body').html(
'<div id="templates"><' + element + ' id="' + id +
'template">' + value + '</' + element + '></div>'
);
$.PrivateBin.Model.init();
var template = '<' + element + ' id="' + id + '">' + value +
'</' + element + '>',
result = $.PrivateBin.Model.getTemplate(id).wrap('<p/>').parent().html();
$.PrivateBin.Model.reset();
return template === result;
}
);
});
});
describe('UiHelper', function () {
// TODO: As per https://github.com/tmpvar/jsdom/issues/1565 there is no navigation support in jsdom, yet.
// for now we use a mock function to trigger the event
describe('historyChange', function () {
this.timeout(30000);
before(function () {
$.PrivateBin.Helper.reset();
});
jsc.property(
'redirects to home, when the state is null',
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
function (schema, address) {
var expected = schema + '://' + address.join('') + '/',
clean = jsdom('', {url: expected});
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.mockHistoryChange();
$.PrivateBin.Helper.reset();
var result = window.location.href;
clean();
return expected === result;
}
);
jsc.property(
'does not redirect to home, when a new paste is created',
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var expected = schema + '://' + address.join('') + '/' + '?' +
query.join('') + '#' + fragment.join(''),
clean = jsdom('', {url: expected});
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.mockHistoryChange([
{type: 'newpaste'}, '', expected
]);
$.PrivateBin.Helper.reset();
var result = window.location.href;
clean();
return expected === result;
}
);
});
describe('reloadHome', function () {
this.timeout(30000);
before(function () {
$.PrivateBin.Helper.reset();
});
jsc.property(
'redirects to home',
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var expected = schema + '://' + address.join('') + '/',
clean = jsdom('', {
url: expected + '?' + query.join('') + '#' + fragment.join('')
});
// make window.location.href writable
Object.defineProperty(window.location, 'href', {
writable: true,
value: window.location.href
});
$.PrivateBin.UiHelper.reloadHome();
$.PrivateBin.Helper.reset();
var result = window.location.href;
clean();
return expected === result;
}
);
});
describe('isVisible', function () {
// TODO As per https://github.com/tmpvar/jsdom/issues/1048 there is no layout support in jsdom, yet.
// once it is supported or a workaround is found, uncomment the section below
/*
before(function () {
$.PrivateBin.Helper.reset();
});
jsc.property(
'detect visible elements',
jsc.nearray(jsc.elements(alnumString)),
jsc.nearray(jsc.elements(a2zString)),
function (id, element) {
id = id.join('');
element = element.join('');
var clean = jsdom(
'<' + element + ' id="' + id + '"></' + element + '>'
);
var result = $.PrivateBin.UiHelper.isVisible($('#' + id));
clean();
return result;
}
);
*/
});
describe('scrollTo', function () {
// TODO Did not find a way to test that, see isVisible test above
});
});
describe('Alert', function () {
describe('showStatus', function () {
before(function () {
cleanup();
});
jsc.property(
'shows a status message',
jsc.array(jsc.elements(alnumString)),
jsc.array(jsc.elements(alnumString)),
function (icon, message) {
icon = icon.join('');
message = message.join('');
var expected = '<div id="status" role="alert" ' +
'class="statusmessage alert alert-info"><span ' +
'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</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, icon);
var result = $('body').html();
return expected === result;
}
);
});
describe('showError', function () {
before(function () {
cleanup();
});
jsc.property(
'shows an error message',
jsc.array(jsc.elements(alnumString)),
jsc.array(jsc.elements(alnumString)),
function (icon, message) {
icon = icon.join('');
message = message.join('');
var expected = '<div id="errormessage" role="alert" ' +
'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</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, icon);
var result = $('body').html();
return expected === result;
}
);
});
describe('showRemaining', function () {
before(function () {
cleanup();
});
jsc.property(
'shows remaining time',
jsc.array(jsc.elements(alnumString)),
jsc.array(jsc.elements(alnumString)),
'integer',
function (message, string, number) {
message = message.join('');
string = string.join('');
var expected = '<div id="remainingtime" role="alert" ' +
'class="alert alert-info"><span ' +
'class="glyphicon glyphicon-fire" aria-hidden="true">' +
'</span> ' + string + message + number + '</div>';
$('body').html(
'<div id="remainingtime" role="alert" class="hidden ' +
'alert alert-info"><span class="glyphicon ' +
'glyphicon-fire" aria-hidden="true"></span> </div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
var result = $('body').html();
return expected === result;
}
);
});
describe('showLoading', function () {
before(function () {
cleanup();
});
jsc.property(
'shows a loading message',
jsc.array(jsc.elements(alnumString)),
jsc.array(jsc.elements(alnumString)),
'integer',
function (icon, message, number) {
icon = icon.join('');
message = message.join('');
var default_message = 'Loading…';
if (message.length == 0) {
message = default_message;
}
var expected = '<ul class="nav navbar-nav"><li ' +
'id="loadingindicator" class="navbar-text"><span ' +
'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> ' + message + '</li></ul>';
$('body').html(
'<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text hidden"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' +
default_message + '</li></ul>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.showLoading(message, number, icon);
var result = $('body').html();
return expected === result;
}
);
});
describe('hideLoading', function () {
before(function () {
cleanup();
});
it(
'hides the loading message',
function() {
$('body').html(
'<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' +
'Loading…</li></ul>'
);
$('body').addClass('loading');
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.hideLoading();
return !$('body').hasClass('loading') &&
$('#loadingindicator').hasClass('hidden');
}
);
});
describe('hideMessages', function () {
before(function () {
cleanup();
});
it(
'hides all messages',
function() {
$('body').html(
'<div id="status" role="alert" class="statusmessage ' +
'alert alert-info"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
'<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.hideMessages();
return $('#statusmessage').hasClass('hidden') &&
$('#errormessage').hasClass('hidden');
}
);
});
describe('setCustomHandler', function () {
before(function () {
cleanup();
});
jsc.property(
'calls a given handler function',
'nat 3',
jsc.array(jsc.elements(alnumString)),
function (trigger, message) {
message = message.join('');
var handlerCalled = false,
default_message = 'Loading…',
functions = [
$.PrivateBin.Alert.showStatus,
$.PrivateBin.Alert.showError,
$.PrivateBin.Alert.showRemaining,
$.PrivateBin.Alert.showLoading
];
if (message.length == 0) {
message = default_message;
}
$('body').html(
'<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text hidden"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' +
default_message + '</li></ul>' +
'<div id="remainingtime" role="alert" class="hidden ' +
'alert alert-info"><span class="glyphicon ' +
'glyphicon-fire" aria-hidden="true"></span> </div>' +
'<div id="status" role="alert" class="statusmessage ' +
'alert alert-info"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
'<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>'
);
$.PrivateBin.Alert.init();
$.PrivateBin.Alert.setCustomHandler(function(id, $element) {
handlerCalled = true;
return jsc.random(0, 1) ? true : $element;
});
functions[trigger](message);
return handlerCalled;
}
);
});
});
describe('PasteStatus', function () {
describe('createPasteNotification', function () {
this.timeout(30000);
before(function () {
cleanup();
});
jsc.property(
'creates a notification after a successfull paste upload',
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
'string',
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
function (
schema1, address1, query1, fragment1,
schema2, address2, query2
) {
var expected1 = schema1 + '://' + address1.join('') + '/?' +
encodeURI(query1.join('').replace(/^&+|&+$/gm,'') + '#' + fragment1),
expected2 = schema2 + '://' + address2.join('') + '/?' +
encodeURI(query2.join('')),
clean = jsdom();
$('body').html('<div><div id="deletelink"></div><div id="pastelink"></div></div>');
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
var result1 = $('#pasteurl')[0].href,
result2 = $('#deletelink a')[0].href;
clean();
return result1 == expected1 && result2 == expected2;
}
);
});
describe('showRemainingTime', function () {
this.timeout(30000);
before(function () {
cleanup();
});
jsc.property(
'shows burn after reading message or remaining time',
'bool',
'nat',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
'string',
function (
burnafterreading, remaining_time,
schema, address, query, fragment
) {
var clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + queryString + '#' + fragment
});
$('body').html('<div id="remainingtime" class="hidden"></div>');
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.showRemainingTime({
'burnafterreading': burnafterreading,
'remaining_time': remaining_time,
'expire_date': remaining_time ? ((new Date()).getTime() / 1000) + remaining_time : 0
});
if (burnafterreading) {
var result = $('#remainingtime').hasClass('foryoureyesonly') &&
!$('#remainingtime').hasClass('hidden');
} else if (remaining_time) {
var result =!$('#remainingtime').hasClass('foryoureyesonly') &&
!$('#remainingtime').hasClass('hidden');
} else {
var result = $('#remainingtime').hasClass('hidden') &&
!$('#remainingtime').hasClass('foryoureyesonly');
}
clean();
return result;
}
);
});
describe('hideMessages', function () {
before(function () {
cleanup();
});
it(
'hides all messages',
function() {
$('body').html(
'<div id="remainingtime"></div><div id="pastesuccess"></div>'
);
$.PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.hideMessages();
return $('#remainingtime').hasClass('hidden') &&
$('#pastesuccess').hasClass('hidden');
}
);
});
});
describe('Prompt', function () {
// TODO: this does not test the prompt() fallback, since that isn't available
// in nodejs -> replace the prompt in the "page" template with a modal
describe('requestPassword & getPassword', function () {
this.timeout(30000);
before(function () {
cleanup();
});
jsc.property(
'returns the password fed into the dialog',
'string',
function (password) {
password = password.replace(/\r+/g, '');
var clean = jsdom('', {url: 'ftp://example.com/#0'});
$('body').html(
'<div id="passwordmodal" class="modal fade" role="dialog">' +
'<div class="modal-dialog"><div class="modal-content">' +
'<div class="modal-body"><form id="passwordform" role="form">' +
'<div class="form-group"><input id="passworddecrypt" ' +
'type="password" class="form-control" placeholder="Enter ' +
'password"></div><button type="submit">Decrypt</button>' +
'</form></div></div></div></div><div id="cipherdata">{}</div>'
);
$.PrivateBin.Model.init();
$.PrivateBin.Prompt.init();
$.PrivateBin.Prompt.requestPassword();
$('#passworddecrypt').val(password);
$('#passwordform').submit();
var result = $.PrivateBin.Prompt.getPassword();
clean();
return result == password;
}
);
});
});
describe('Editor', function () {
describe('show, hide, getText, setText & isPreview', function () {
this.timeout(30000);
before(function () {
cleanup();
});
jsc.property(
'returns text fed into the textarea, handles editor tabs',
'string',
function (text) {
var clean = jsdom(),
results = [];
$('body').html(
'<ul id="editorTabs" class="nav nav-tabs hidden"><li ' +
'role="presentation" class="active"><a id="messageedit" ' +
'href="#">Editor</a></li><li role="presentation"><a ' +
'id="messagepreview" href="#">Preview</a></li></ul><div ' +
'id="placeholder" class="hidden">+++ no paste text +++</div>' +
'<div id="prettymessage" class="hidden"><pre id="prettyprint" ' +
'class="prettyprint linenums:1"></pre></div><div ' +
'id="plaintext" class="hidden"></div><p><textarea ' +
'id="message" name="message" cols="80" rows="25" ' +
'class="form-control hidden"></textarea></p>'
);
$.PrivateBin.Editor.init();
results.push(
$('#editorTabs').hasClass('hidden') &&
$('#message').hasClass('hidden')
);
$.PrivateBin.Editor.show();
results.push(
!$('#editorTabs').hasClass('hidden') &&
!$('#message').hasClass('hidden')
);
$.PrivateBin.Editor.hide();
results.push(
$('#editorTabs').hasClass('hidden') &&
$('#message').hasClass('hidden')
);
$.PrivateBin.Editor.show();
$.PrivateBin.Editor.focusInput();
results.push(
$.PrivateBin.Editor.getText().length == 0
);
$.PrivateBin.Editor.setText(text);
results.push(
$.PrivateBin.Editor.getText() == $('#message').val()
);
$.PrivateBin.Editor.setText();
results.push(
!$.PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden')
);
$('#messagepreview').click();
results.push(
$.PrivateBin.Editor.isPreview() &&
$('#message').hasClass('hidden')
);
$('#messageedit').click();
results.push(
!$.PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden')
);
clean();
return results.every(element => element);
}
);
});
});
describe('PasteViewer', function () {
describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () {
this.timeout(30000);
before(function () {
cleanup();
});
jsc.property(
'displays text according to format',
jsc.elements(['plaintext', 'markdown', 'syntaxhighlighting']),
'nestring',
function (format, text) {
var clean = jsdom(),
results = [];
$('body').html(
'<div id="placeholder" class="hidden">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden"><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText('');
results.push(
$('#placeholder').hasClass('hidden') &&
$('#prettymessage').hasClass('hidden') &&
$('#plaintext').hasClass('hidden') &&
$.PrivateBin.PasteViewer.getFormat() == format &&
$.PrivateBin.PasteViewer.getText() == ''
);
$.PrivateBin.PasteViewer.run();
results.push(
!$('#placeholder').hasClass('hidden') &&
$('#prettymessage').hasClass('hidden') &&
$('#plaintext').hasClass('hidden')
);
$.PrivateBin.PasteViewer.hide();
results.push(
$('#placeholder').hasClass('hidden') &&
$('#prettymessage').hasClass('hidden') &&
$('#plaintext').hasClass('hidden')
);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
results.push(
$('#placeholder').hasClass('hidden') &&
!$.PrivateBin.PasteViewer.isPrettyPrinted() &&
$.PrivateBin.PasteViewer.getText() == text
);
if (format == 'markdown') {
results.push(
$('#prettymessage').hasClass('hidden') &&
!$('#plaintext').hasClass('hidden')
);
} else {
results.push(
!$('#prettymessage').hasClass('hidden') &&
$('#plaintext').hasClass('hidden')
);
}
clean();
return results.every(element => element);
}
);
jsc.property(
'sanitizes XSS',
jsc.elements(['plaintext', 'markdown', 'syntaxhighlighting']),
'string',
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
jsc.elements([
'<PLAINTEXT>',
'\';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";',
'alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--',
'></SCRIPT>">\'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>',
'\'\';!--"<XSS>=&{()}',
'<SCRIPT SRC=http://example.com/xss.js></SCRIPT>',
'\'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\\></|\\><plaintext/onmouseover=prompt(1)><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>\'-->"></script><script>alert(document.cookie)</script>"><img/id="confirm&lpar;1)"/alt="/"src="/"onerror=eval(id)>\'">',
'<IMG SRC="javascript:alert(\'XSS\');">',
'<IMG SRC=javascript:alert(\'XSS\')>',
'<IMG SRC=JaVaScRiPt:alert(\'XSS\')>',
'<IMG SRC=javascript:alert(&quot;XSS&quot;)>',
'<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>',
'<a onmouseover="alert(document.cookie)">xxs link</a>',
'<a onmouseover=alert(document.cookie)>xxs link</a>',
'<IMG """><SCRIPT>alert("XSS")</SCRIPT>">',
'<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>'
// the list goes on…
]),
'string',
function (format, prefix, xss, suffix) {
var clean = jsdom(),
text = prefix + xss + suffix;
$('body').html(
'<div id="placeholder" class="hidden">+++ no paste text ' +
'+++</div><div id="prettymessage" class="hidden"><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>'
);
$.PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run();
var result = $('body').html().indexOf(xss) !== -1;
clean();
return result;
}
);
});
});

View file

@ -64,7 +64,7 @@ class Filter
$i = 0;
while (($size / 1024) >= 1) {
$size = $size / 1024;
$i++;
++$i;
}
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . I18n::_($iec[$i]);
}
@ -82,7 +82,7 @@ class Filter
public static function slowEquals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
for ($i = 0; $i < strlen($a) && $i < strlen($b); ++$i) {
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;

View file

@ -70,7 +70,7 @@ if ($MARKDOWN):
<?php
endif;
?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-CbxrV468ako77cl5jNqoqohA9EphJI54ha7/3Zv0K7lXW/0fC7l1L+SXpTq94FpQP4vSIZFmQnOkrmPxkgNbag==" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-zbiQgBDsSGOdieFoYhNqu7fnj+GpeuQRXQcUSg1U9lP3HlHUY6Lp1w/Hl/LK2y/RGjkoV3MRcjU/BQVGoZxKlw==" crossorigin="anonymous"></script>
<!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]-->
@ -231,6 +231,17 @@ if ($isCpct):
?>
</ul>
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
<?php
foreach ($FORMATTER as $key => $value):
?>
<option value="<?php echo $key; ?>"<?php
if ($key == $FORMATTERDEFAULT):
?> selected="selected"<?php
endif;
?>><?php echo $value; ?></option>
<?php
endforeach;
?>
</select>
</li>
<?php
@ -424,7 +435,7 @@ endif;
<a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>
</div>
<div id="pasteSuccess" role="alert" class="hidden alert alert-success">
<div id="pastesuccess" role="alert" class="hidden alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<div id="deletelink"></div>
<div id="pastelink">

View file

@ -48,7 +48,7 @@ if ($MARKDOWN):
<?php
endif;
?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-CbxrV468ako77cl5jNqoqohA9EphJI54ha7/3Zv0K7lXW/0fC7l1L+SXpTq94FpQP4vSIZFmQnOkrmPxkgNbag==" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-zbiQgBDsSGOdieFoYhNqu7fnj+GpeuQRXQcUSg1U9lP3HlHUY6Lp1w/Hl/LK2y/RGjkoV3MRcjU/BQVGoZxKlw==" crossorigin="anonymous"></script>
<!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]-->
@ -185,7 +185,7 @@ if (strlen($LANGUAGESELECTION)):
endif;
?>
</div>
<div id="pasteresult" class="hidden">
<div id="pastesuccess" class="hidden">
<div id="deletelink"></div>
<div id="pastelink">
<?php