From ca51a808035e2b29c5a3168ec5c8afb875b53269 Mon Sep 17 00:00:00 2001 From: rugk Date: Wed, 1 Feb 2017 19:24:56 +0100 Subject: [PATCH 1/7] Update the history when a paste is created Fixes https://github.com/PrivateBin/PrivateBin/issues/167 --- js/privatebin.js | 23 +++++++++++++++++++++++ tpl/bootstrap.php | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/js/privatebin.js b/js/privatebin.js index 6ed88f03..5963592a 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1118,6 +1118,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var deleteUrl = controller.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); + history.pushState({type: 'newpaste'}, '', url); $('#pastelink').html( i18n._( @@ -1152,6 +1153,26 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }); }, + /** + * handle history (pop) state changes + * + * currently this does only handle redirects to the home page. + * + * @name controller.historyChange + * @function + * @param {Event} event + */ + historyChange: function(event) + { + if (event.originalEvent.state === null && // no state object passed + this.scriptLocation() === event.originalEvent.target.location.href && // target location is home page + this.scriptLocation() === window.location.href // and we are not already on the home page + ) { + // redirect to home page + window.location.href = this.scriptLocation(); + } + }, + /** * check if a URL shortener was defined and create HTML containing a link to it * @@ -1619,6 +1640,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.passwordModal.on('shown.bs.modal', $.proxy(this.passwordDecrypt.focus, this)); this.passwordModal.on('hidden.bs.modal', $.proxy(this.decryptPasswordModal, this)); this.passwordForm.submit($.proxy(this.submitPasswordModal, this)); + + $(window).on('popstate', $.proxy(this.historyChange, this)); }, /** diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 8938112c..aa61a02b 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + From 5442af6e20ba6ba2ad234a582625e52147c31a24 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 14:47:03 +0100 Subject: [PATCH 2/7] slight JS refactoring --- js/privatebin.js | 314 ++++++++++++++++++++++++---------------------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 168 insertions(+), 150 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 5963592a..e8d8acb2 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -115,8 +115,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { // For IE<10: Doesn't support white-space:pre-wrap; so we have to do this... if ($('#oldienotice').is(':visible')) { - var html = this.htmlEntities(text).replace(/\n/ig,'\r\n
'); - element.html('
'+html+'
'); + var html = this.htmlEntities(text).replace(/\n/ig, '\r\n
'); + element.html('
' + html + '
'); } // for other (sane) browsers: else @@ -230,11 +230,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @return {string} */ getCookie: function(cname) { - var name = cname + '='; - var ca = document.cookie.split(';'); + var name = cname + '=', + ca = document.cookie.split(';'); for (var i = 0; i < ca.length; ++i) { var c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1); + while (c.charAt(0) === ' ') + { + c = c.substring(1); + } if (c.indexOf(name) === 0) { return c.substring(name.length, c.length); @@ -243,6 +246,77 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { return ''; }, + /** + * get the current script location (without search or hash part of the URL), + * eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/ + * + * @name helper.scriptLocation + * @function + * @return {string} current script location + */ + scriptLocation: function() + { + var scriptLocation = window.location.href.substring( + 0, + window.location.href.length - window.location.search.length - window.location.hash.length + ), hashIndex = scriptLocation.indexOf('#'); + if (hashIndex !== -1) + { + scriptLocation = scriptLocation.substring(0, hashIndex); + } + return scriptLocation; + }, + + /** + * get the pastes unique identifier from the URL, + * eg. http://example.com/path/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 + * + * @name helper.pasteId + * @function + * @return {string} unique identifier + */ + pasteId: function() + { + return window.location.search.substring(1); + }, + + /** + * return the deciphering key stored in anchor part of the URL + * + * @name helper.pageKey + * @function + * @return {string} key + */ + pageKey: function() + { + // Some web 2.0 services and redirectors add data AFTER the anchor + // (such as &utm_source=...). We will strip any additional data. + + var key = window.location.hash.substring(1), // Get key + i = key.indexOf('='); + + // First, strip everything after the equal sign (=) which signals end of base64 string. + if (i > -1) + { + key = key.substring(0, i + 1); + } + + // If the equal sign was not present, some parameters may remain: + i = key.indexOf('&'); + if (i > -1) + { + key = key.substring(0, i); + } + + // Then add trailing equal sign if it's missing + if (key.charAt(key.length - 1) !== '=') + { + key += '='; + } + + return key; + }, + /** * convert all applicable characters to HTML entities * @@ -404,8 +478,11 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ loadTranslations: function() { - var selectedLang = helper.getCookie('lang'); - var language = selectedLang.length > 0 ? selectedLang : (navigator.language || navigator.userLanguage).substring(0, 2); + var language = helper.getCookie('lang'); + if (language.length === 0) + { + language = (navigator.language || navigator.userLanguage).substring(0, 2); + } // note that 'en' is built in, so no translation is necessary if (i18n.supportedLanguages.indexOf(language) === -1) { @@ -555,76 +632,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ createdPasteUrl: '', - /** - * get the current script location (without search or hash part of the URL), - * eg. http://example.com/zero/?aaaa#bbbb --> http://example.com/zero/ - * - * @name controller.scriptLocation - * @function - * @return {string} current script location - */ - scriptLocation: function() - { - var scriptLocation = window.location.href.substring(0,window.location.href.length - - window.location.search.length - window.location.hash.length), - hashIndex = scriptLocation.indexOf('#'); - if (hashIndex !== -1) - { - scriptLocation = scriptLocation.substring(0, hashIndex); - } - return scriptLocation; - }, - - /** - * get the pastes unique identifier from the URL, - * eg. http://example.com/zero/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 - * - * @name controller.pasteID - * @function - * @return {string} unique identifier - */ - pasteID: function() - { - return window.location.search.substring(1); - }, - - /** - * return the deciphering key stored in anchor part of the URL - * - * @name controller.pageKey - * @function - * @return {string} key - */ - pageKey: function() - { - // Some web 2.0 services and redirectors add data AFTER the anchor - // (such as &utm_source=...). We will strip any additional data. - - var key = window.location.hash.substring(1), // Get key - i = key.indexOf('='); - - // First, strip everything after the equal sign (=) which signals end of base64 string. - if (i > -1) - { - key = key.substring(0, i + 1); - } - - // If the equal sign was not present, some parameters may remain: - i = key.indexOf('&'); - if (i > -1) - { - key = key.substring(0, i); - } - - // Then add trailing equal sign if it's missing - if (key.charAt(key.length - 1) !== '=') - { - key += '='; - } - - return key; - }, - /** * ask the user for the password and set it * @@ -722,8 +729,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { displayMessages: function(paste) { paste = paste || $.parseJSON(this.cipherData.text()); - var key = this.pageKey(); - var password = this.passwordInput.val(); + var key = helper.pageKey(), + password = this.passwordInput.val(); if (!this.prettyPrint.hasClass('prettyprinted')) { // Try to decrypt the paste. try @@ -813,7 +820,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // unfortunately many web servers don't support DELETE (and PUT) out of the box $.ajax({ type: 'POST', - url: this.scriptLocation() + '?' + this.pasteID(), + url: helper.scriptLocation() + '?' + helper.pasteId(), data: {deletetoken: 'burnafterreading'}, dataType: 'json', headers: this.headers @@ -838,24 +845,27 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // iterate over comments for (var i = 0; i < paste.comments.length; ++i) { - var place = this.comments; - var comment = paste.comments[i]; - var commenttext = filter.decipher(key, password, comment.data); - // if parent comment exists, display below (CSS will automatically shift it to the right) - var cname = '#comment_' + comment.parentid; + var place = this.comments, + comment = paste.comments[i], + commenttext = filter.decipher(key, password, comment.data), + // if parent comment exists, display below (CSS will automatically shift it to the right) + cname = '#comment_' + comment.parentid, + divComment = $('
' + + '
' + + '
' + + '
'), + divCommentData = divComment.find('div.commentdata'); // if the element exists in page if ($(cname).length) { place = $(cname); } - var divComment = $('
' - + '
' - + '' - + '
'); divComment.find('button').click({commentid: comment.id}, $.proxy(this.openReply, this)); - helper.setElementText(divComment.find('div.commentdata'), commenttext); - helper.urls2links(divComment.find('div.commentdata')); + helper.setElementText(divCommentData, commenttext); + helper.urls2links(divCommentData); // try to get optional nickname var nick = filter.decipher(key, password, comment.meta.nickname); @@ -887,7 +897,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { '
' ); - divComment.find('button').click({commentid: this.pasteID()}, $.proxy(this.openReply, this)); + divComment.find('button').click({commentid: helper.pasteId()}, $.proxy(this.openReply, this)); this.comments.append(divComment); this.discussion.removeClass('hidden'); } @@ -903,20 +913,26 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { openReply: function(event) { event.preventDefault(); - var source = $(event.target), - commentid = event.data.commentid, - hint = i18n._('Optional nickname...'); // remove any other reply area $('div.reply').remove(); - var reply = $( - '
' + - '' + - '' + - '
' + + var source = $(event.target), + commentid = event.data.commentid, + hint = i18n._('Optional nickname...'), + reply = $( + '

' + + '
' + ); + reply.find('button').click( + {parentid: commentid}, + $.proxy(this.sendComment, this) ); - reply.find('button').click({parentid: commentid}, $.proxy(this.sendComment, this)); source.after(reply); this.replyStatus = $('#replystatus'); $('#replymessage').focus(); @@ -941,24 +957,25 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } this.showStatus(i18n._('Sending comment...'), true); - var parentid = event.data.parentid; - var cipherdata = filter.cipher(this.pageKey(), this.passwordInput.val(), replyMessage.val()); - var ciphernickname = ''; - var nick = $('#nickname').val(); - if (nick !== '') + var parentid = event.data.parentid, + key = helper.pageKey(), + cipherdata = filter.cipher(key, this.passwordInput.val(), replyMessage.val()), + ciphernickname = '', + nick = $('#nickname').val(); + if (nick.length > 0) { - ciphernickname = filter.cipher(this.pageKey(), this.passwordInput.val(), nick); + ciphernickname = filter.cipher(key, this.passwordInput.val(), nick); } var data_to_send = { data: cipherdata, parentid: parentid, - pasteid: this.pasteID(), + pasteid: helper.pasteId(), nickname: ciphernickname }; $.ajax({ type: 'POST', - url: this.scriptLocation(), + url: helper.scriptLocation(), data: data_to_send, dataType: 'json', headers: this.headers, @@ -969,7 +986,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { controller.showStatus(i18n._('Comment posted.')); $.ajax({ type: 'GET', - url: controller.scriptLocation() + '?' + controller.pasteID(), + url: helper.scriptLocation() + '?' + helper.pasteId(), dataType: 'json', headers: controller.headers, success: function(data) @@ -1040,8 +1057,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.password.addClass('hidden'); this.showStatus(i18n._('Sending paste...'), true); - var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); - var password = this.passwordInput.val(); + var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0), + password = this.passwordInput.val(); if(files && files[0]) { if(typeof FileReader === undefined) @@ -1088,14 +1105,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ sendDataContinue: function(randomkey, cipherdata_attachment, cipherdata_attachment_name) { - var cipherdata = filter.cipher(randomkey, this.passwordInput.val(), this.message.val()); - var data_to_send = { - data: cipherdata, - expire: $('#pasteExpiration').val(), - formatter: $('#pasteFormatter').val(), - burnafterreading: this.burnAfterReading.is(':checked') ? 1 : 0, - opendiscussion: this.openDiscussion.is(':checked') ? 1 : 0 - }; + var cipherdata = filter.cipher(randomkey, this.passwordInput.val(), this.message.val()), + data_to_send = { + data: cipherdata, + expire: $('#pasteExpiration').val(), + formatter: $('#pasteFormatter').val(), + burnafterreading: this.burnAfterReading.is(':checked') ? 1 : 0, + opendiscussion: this.openDiscussion.is(':checked') ? 1 : 0 + }; if (cipherdata_attachment.length > 0) { data_to_send.attachment = cipherdata_attachment; @@ -1106,7 +1123,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } $.ajax({ type: 'POST', - url: this.scriptLocation(), + url: helper.scriptLocation(), data: data_to_send, dataType: 'json', headers: this.headers, @@ -1114,8 +1131,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { if (data.status === 0) { controller.stateExistingPaste(); - var url = controller.scriptLocation() + '?' + data.id + '#' + randomkey; - var deleteUrl = controller.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; + var url = helper.scriptLocation() + '?' + data.id + '#' + randomkey, + deleteUrl = helper.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); history.pushState({type: 'newpaste'}, '', url); @@ -1153,26 +1170,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }); }, - /** - * handle history (pop) state changes - * - * currently this does only handle redirects to the home page. - * - * @name controller.historyChange - * @function - * @param {Event} event - */ - historyChange: function(event) - { - if (event.originalEvent.state === null && // no state object passed - this.scriptLocation() === event.originalEvent.target.location.href && // target location is home page - this.scriptLocation() === window.location.href // and we are not already on the home page - ) { - // redirect to home page - window.location.href = this.scriptLocation(); - } - }, - /** * check if a URL shortener was defined and create HTML containing a link to it * @@ -1325,7 +1322,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { reloadPage: function(event) { event.preventDefault(); - window.location.href = this.scriptLocation(); + window.location.href = helper.scriptLocation(); }, /** @@ -1341,8 +1338,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var paste = $('#pasteFormatter').val() === 'markdown' ? this.prettyPrint.text() : this.clearText.text(); history.pushState( - null, document.title, this.scriptLocation() + '?' + - this.pasteID() + '#' + this.pageKey() + null, document.title, helper.scriptLocation() + '?' + + helper.pasteId() + '#' + helper.pageKey() ); // we use text/html instead of text/plain to avoid a bug when // reloading the raw text view (it reverts to type text/html) @@ -1364,7 +1361,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.stateNewPaste(); // erase the id and the key in url - history.replaceState(null, document.title, this.scriptLocation()); + history.replaceState(null, document.title, helper.scriptLocation()); this.showStatus(''); if (this.attachmentLink.attr('href')) @@ -1485,6 +1482,27 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.formatPaste($('#pasteFormatter').val(), this.message.val()); }, + /** + * handle history (pop) state changes + * + * currently this does only handle redirects to the home page. + * + * @name controller.historyChange + * @function + * @param {Event} event + */ + historyChange: function(event) + { + var currentLocation = helper.scriptLocation(); + if (event.originalEvent.state === null && // no state object passed + event.originalEvent.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 + window.location.href = currentLocation; + } + }, + /** * create a new paste * diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index aa61a02b..041b5698 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 66a814eb..3aab3d69 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 80f7baa6048384a33b7b4e3d586d3a1e95c39914 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 16:45:11 +0100 Subject: [PATCH 3/7] writing test for scriptLocation function, fixing non-removed query separator bug --- js/privatebin.js | 3 ++- js/test.js | 52 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index e8d8acb2..708b1855 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -259,7 +259,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var scriptLocation = window.location.href.substring( 0, window.location.href.length - window.location.search.length - window.location.hash.length - ), hashIndex = scriptLocation.indexOf('#'); + ), + hashIndex = scriptLocation.indexOf('?'); if (hashIndex !== -1) { scriptLocation = scriptLocation.substring(0, hashIndex); diff --git a/js/test.js b/js/test.js index b979534f..58a7fdbb 100644 --- a/js/test.js +++ b/js/test.js @@ -1,22 +1,20 @@ 'use strict'; -var jsc = require('jsverify'); - -before(function () { - this.jsdom = require('jsdom-global')(); - global.$ = global.jQuery = require('./jquery-3.1.1'); - global.sjcl = require('./sjcl-1.0.6'); - global.Base64 = require('./base64-2.1.9'); - global.RawDeflate = require('./rawdeflate-0.5'); - require('./rawinflate-0.3'); - require('./privatebin'); -}) - -after(function () { - this.jsdom(); -}) +var jsc = require('jsverify'), + jsdom = require('jsdom-global'), + cleanup = jsdom(); +global.$ = global.jQuery = require('./jquery-3.1.1'); +global.sjcl = require('./sjcl-1.0.6'); +global.Base64 = require('./base64-2.1.9'); +global.RawDeflate = require('./rawdeflate-0.5'); +require('./rawinflate-0.3'); +require('./privatebin'); describe('helper', function () { describe('secondsToHuman', function () { + after(function () { + cleanup(); + }); + jsc.property('returns an array with a number and a word', 'integer', function (number) { var result = $.PrivateBin.helper.secondsToHuman(number); return Array.isArray(result) && @@ -56,5 +54,29 @@ describe('helper', function () { return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month'; }); }); + + describe('scriptLocation', function () { + after(function () { + cleanup(); + }); + + var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'], + alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), + queryString = alnumString.concat(['+','%','&','.','*','-','_']); + jsc.property( + 'returns the URL without query & fragment', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + 'string', + function (schema, address, query, fragment) { + var expected = schema.join('') + '://' + address.join('') + '/', + clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}), + result = $.PrivateBin.helper.scriptLocation(); + clean(); + return expected === result; + } + ); + }); }); From a97b94640e6f653ff358e1e4ba975187c825d2a4 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 16:58:58 +0100 Subject: [PATCH 4/7] writing test for pasteId function --- js/test.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/js/test.js b/js/test.js index 58a7fdbb..000362c5 100644 --- a/js/test.js +++ b/js/test.js @@ -1,7 +1,13 @@ 'use strict'; var jsc = require('jsverify'), jsdom = require('jsdom-global'), - cleanup = jsdom(); + cleanup = jsdom(), + + a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z'], + alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), + queryString = alnumString.concat(['+','%','&','.','*','-','_']); + global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); global.Base64 = require('./base64-2.1.9'); @@ -56,13 +62,6 @@ describe('helper', function () { }); describe('scriptLocation', function () { - after(function () { - cleanup(); - }); - - var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'], - alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), - queryString = alnumString.concat(['+','%','&','.','*','-','_']); jsc.property( 'returns the URL without query & fragment', jsc.nearray(jsc.elements(a2zString)), @@ -78,5 +77,25 @@ describe('helper', function () { } ); }); + + describe('pasteId', function () { + jsc.property( + 'returns the query string without separator, if any', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + 'string', + function (schema, address, query, fragment) { + var query = query.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/?' + query + '#' + fragment + }), + result = $.PrivateBin.helper.pasteId(); + clean(); + return query === result; + } + ); + }); }); From 67f71f4dd60ce27c93fa0a73473ac289649b9e36 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:03:42 +0100 Subject: [PATCH 5/7] writing tests for pageKey function, fixing always added padding bug --- js/privatebin.js | 21 +++------------------ js/test.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 708b1855..31fa44f9 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -290,31 +290,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ pageKey: function() { + var key = window.location.hash.substring(1), + i = key.indexOf('&'); + // Some web 2.0 services and redirectors add data AFTER the anchor // (such as &utm_source=...). We will strip any additional data. - - var key = window.location.hash.substring(1), // Get key - i = key.indexOf('='); - - // First, strip everything after the equal sign (=) which signals end of base64 string. - if (i > -1) - { - key = key.substring(0, i + 1); - } - - // If the equal sign was not present, some parameters may remain: - i = key.indexOf('&'); if (i > -1) { key = key.substring(0, i); } - // Then add trailing equal sign if it's missing - if (key.charAt(key.length - 1) !== '=') - { - key += '='; - } - return key; }, diff --git a/js/test.js b/js/test.js index 000362c5..a283275f 100644 --- a/js/test.js +++ b/js/test.js @@ -6,7 +6,12 @@ var jsc = require('jsverify'), a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z'], alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), - queryString = alnumString.concat(['+','%','&','.','*','-','_']); + queryString = alnumString.concat(['+','%','&','.','*','-','_']), + base64String = alnumString.concat(['+','/','=']).concat( + a2zString.map(function(c) { + return c.toUpperCase(); + }) + ); global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); @@ -97,5 +102,43 @@ describe('helper', function () { } ); }); + + describe('pageKey', function () { + jsc.property( + 'returns the fragment of the URL', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + jsc.array(jsc.elements(base64String)), + function (schema, address, query, fragment) { + var fragment = fragment.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/?' + query.join('') + '#' + fragment + }), + result = $.PrivateBin.helper.pageKey(); + clean(); + return fragment === result; + } + ); + jsc.property( + 'returns the fragment stripped of trailing query parts', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + jsc.array(jsc.elements(base64String)), + jsc.array(jsc.elements(queryString)), + function (schema, address, query, fragment, trail) { + var fragment = fragment.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + '/?' + + query.join('') + '#' + fragment + '&' + trail.join('') + }), + result = $.PrivateBin.helper.pageKey(); + clean(); + return fragment === result; + } + ); + }); }); From f699ca6cd42a788f84be87321d2065e4f6a23fc1 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:46:24 +0100 Subject: [PATCH 6/7] writing tests for htmlEntities function --- js/test.js | 15 +++++++++++++++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/js/test.js b/js/test.js index a283275f..5e4d3c39 100644 --- a/js/test.js +++ b/js/test.js @@ -140,5 +140,20 @@ describe('helper', function () { } ); }); + + describe('htmlEntities', function () { + after(function () { + cleanup(); + }); + + jsc.property( + 'removes all HTML entities from any given string', + 'string', + function (string) { + var result = $.PrivateBin.helper.htmlEntities(string); + return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&/.test(result))); + } + ); + }); }); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 041b5698..668b2e91 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 3aab3d69..362e8878 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 366b61c32d43d8652bf2ae085deab72f717bcd32 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:53:57 +0100 Subject: [PATCH 7/7] adding document title in new history state --- js/privatebin.js | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 31fa44f9..850dfc93 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1121,7 +1121,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { deleteUrl = helper.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); - history.pushState({type: 'newpaste'}, '', url); + history.pushState({type: 'newpaste'}, document.title, url); $('#pastelink').html( i18n._( diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 668b2e91..b4572219 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 362e8878..491be5da 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - +