From ab75b183fbcda30ffbd4255a619ad0abcc4584df Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Sat, 17 Aug 2019 22:17:35 -0400 Subject: [PATCH] Fix click on new paste on clone paste editing view not removing custom attachment Fix cloning paste with attachment Update CSP in sample and default configuration Ensure clone paste also clone format Fix clone button hiding logic when paste is burn after read Remove attachment name when new paste clicked on Enable file operation only when editing --- cfg/conf.sample.php | 2 +- js/privatebin.js | 119 +++++++++++++++++++++++++++++++++++++++--- lib/Configuration.php | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 1c1d8d9..3decc5c 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -70,7 +70,7 @@ languageselection = false ; Check the documentation at https://content-security-policy.com/ ; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions. ; By default this disallows to load images from third-party servers, e.g. when they are embedded in pastes. If you wish to allow that, you can adjust the policy here. See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-it-load-embedded-images for details. -; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals" +; cspheader = "default-src 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals" ; stay compatible with PrivateBin Alpha 0.19, less secure ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of diff --git a/js/privatebin.js b/js/privatebin.js index 29071d6..6c69191 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1852,10 +1852,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { Alert.showRemaining('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.'); $remainingTime.addClass('foryoureyesonly'); - - // discourage cloning (it cannot really be prevented) - TopNav.hideCloneButton(); - } else if (paste.getTimeToLive() > 0) { // display paste expiration let expiration = Helper.secondsToHuman(paste.getTimeToLive()), @@ -2225,6 +2221,18 @@ jQuery.PrivateBin = (function($, RawDeflate) { return $message.val(); }; + /** + * returns if status is editing + * + * @name Editor.isEditing + * @function + * @return {bool} + */ + me.isEditing = function() + { + return !$message.hasClass('hidden'); + }; + /** * init status manager * @@ -2598,6 +2606,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { $attachmentLink.removeAttr('download'); $attachmentLink.off('click'); $attachmentPreview.html(''); + $dragAndDropFileName.text(''); AttachmentViewer.removeAttachmentData(); }; @@ -2838,6 +2847,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { const handleDragEnterOrOver = function(event) { event.stopPropagation(); event.preventDefault(); + return false; }; const handleDrop = function(event) { @@ -2845,6 +2855,10 @@ jQuery.PrivateBin = (function($, RawDeflate) { evt.stopPropagation(); evt.preventDefault(); + if (!Editor.isEditing()) { + return false; + } + if ($fileInput) { const file = evt.dataTransfer.files[0]; //Clear the file input: @@ -2858,7 +2872,12 @@ jQuery.PrivateBin = (function($, RawDeflate) { }; $(document).draghover().on({ - 'draghoverstart': function() { + 'draghoverstart': function(e) { + if (!Editor.isEditing()) { + e.stopPropagation(); + e.preventDefault(); + return false; + } // show dropzone to indicate drop support $dropzone.removeClass('hidden'); }, @@ -2884,6 +2903,11 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ function addClipboardEventHandler() { $(document).on('paste', function (event) { + if (!Editor.isEditing()) { + event.stopPropagation(); + event.preventDefault(); + return false; + } const items = (event.clipboardData || event.originalEvent.clipboardData).items; for (let i = 0; i < items.length; ++i) { if (items[i].kind === 'file') { @@ -3306,7 +3330,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { } /** - * set the format on bootstrap templates in dropdown + * set the format on bootstrap templates in dropdown from user interaction * * @name TopNav.updateFormat * @private @@ -3668,6 +3692,18 @@ jQuery.PrivateBin = (function($, RawDeflate) { $customAttachment.removeClass('hidden'); }; + /** + * hides the custom attachment + * + * @name TopNav.hideCustomAttachment + * @function + */ + me.hideCustomAttachment = function() + { + $customAttachment.addClass('hidden'); + $fileWrap.removeClass('hidden'); + }; + /** * collapses the navigation bar, only if expanded * @@ -3798,6 +3834,17 @@ jQuery.PrivateBin = (function($, RawDeflate) { }, 300); } + /** + * set the format on bootstrap templates in dropdown programmatically + * + * @name TopNav.setFormat + * @function + */ + me.setFormat = function(format) + { + $formatter.parent().find(`a[data-format="${format}"]`).click(); + } + /** * init navigation manager * @@ -4347,6 +4394,53 @@ jQuery.PrivateBin = (function($, RawDeflate) { let attachment = AttachmentViewer.getAttachment(); cipherMessage['attachment'] = attachment[0]; cipherMessage['attachment_name'] = attachment[1]; + + // we need to retrieve data from blob if browser already parsed it in memory + if (typeof attachment[0] === 'string' && attachment[0].startsWith('blob:')) { + Alert.showStatus( + [ + 'Retrieving cloned file \'%s\' from memory...', + attachment[1] + ], + 'copy' + ); + try { + const blobData = await $.ajax({ + type: 'GET', + url: `${attachment[0]}`, + processData: false, + timeout: 10000, + xhrFields: { + withCredentials: false, + responseType: 'blob' + } + }); + if (blobData instanceof window.Blob) { + const fileReading = new Promise(function(resolve, reject) { + const fileReader = new FileReader(); + fileReader.onload = function (event) { + resolve(event.target.result); + }; + fileReader.onerror = function (error) { + reject(error); + } + fileReader.readAsDataURL(blobData); + }); + cipherMessage['attachment'] = await fileReading; + } else { + Alert.showError( + I18n._('Cannot process attachment data.') + ); + throw new TypeError('Cannot process attachment data.'); + } + } catch (error) { + console.error(error); + Alert.showError( + I18n._('Cannot retrieve attachment.') + ); + throw error; + } + } } // encrypt message @@ -4587,6 +4681,11 @@ jQuery.PrivateBin = (function($, RawDeflate) { .then(() => { Alert.hideLoading(); TopNav.showViewButtons(); + + // discourage cloning (it cannot really be prevented) + if (paste.isBurnAfterReadingEnabled()) { + TopNav.hideCloneButton(); + } }) .catch((err) => { // wait for the user to type in the password, @@ -4799,6 +4898,12 @@ jQuery.PrivateBin = (function($, RawDeflate) { AttachmentViewer.removeAttachment(); TopNav.showCreateButtons(); + + // newPaste could be called when user is on paste clone editing view + TopNav.hideCustomAttachment(); + AttachmentViewer.clearDragAndDrop(); + AttachmentViewer.removeAttachmentData(); + Alert.hideLoading(); history.pushState({type: 'create'}, document.title, Helper.baseUri()); @@ -4914,6 +5019,8 @@ jQuery.PrivateBin = (function($, RawDeflate) { } Editor.setText(PasteViewer.getText()); + // also clone the format + TopNav.setFormat(PasteViewer.getFormat()); PasteViewer.hide(); Editor.show(); diff --git a/lib/Configuration.php b/lib/Configuration.php index 6e0c2af..67ecc23 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -53,7 +53,7 @@ class Configuration 'urlshortener' => '', 'qrcode' => true, 'icon' => 'identicon', - 'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals', + 'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals', 'zerobincompatibility' => false, 'httpwarning' => true, 'compression' => 'zlib', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index e775b7b..55f1d68 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -71,7 +71,7 @@ if ($MARKDOWN): endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 89973f2..6b48cee 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -49,7 +49,7 @@ if ($MARKDOWN): endif; ?> - +