name0000-00-00
c
diff --git a/.editorconfig b/.editorconfig index 2c9ddc3c..86252fa0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,6 @@ insert_final_newline = true [*.css] indent_style = tab -indent_size = 4 [*.js] indent_style = space @@ -23,7 +22,6 @@ indent_size = 4 [*.jsonld] indent_style = tab -indent_size = 4 [*.php] indent_style = space @@ -31,7 +29,6 @@ indent_size = 4 [*.{htm,html}] indent_style = tab -indent_size = 4 [*.{md,markdown}] indent_style = space diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e77d866..ff497129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * CHANGED: Minimum required PHP version is 5.4 (#186) * CHANGED: Shipped .htaccess files were updated for Apache 2.4 (#192) * CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory + * CHANGED: Removed option to hide clone button on expiring pastes, since this requires reading the paste for rendering the template, which leaks information on the pastes state * **1.1.1 (2017-10-06)** * CHANGED: Switched to `.php` file extension for configuration file, to avoid leaking configuration data in unprotected installation. * **1.1 (2016-12-26)** diff --git a/INSTALL.md b/INSTALL.md index 6eebfe93..29dc7f1e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -39,7 +39,7 @@ process (see also > #### PATH Example > Your PrivateBin installation lives in a subfolder called "paste" inside of > your document root. The URL looks like this: -> http://example.com/paste/ +> https://example.com/paste/ > > The full path of PrivateBin on your webserver is: > /home/example.com/htdocs/paste diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index ed494b75..0f71c87d 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -22,10 +22,6 @@ fileupload = false ; preselect the burn-after-reading feature, defaults to false burnafterreadingselected = false -; delete a burn after reading paste immediatly after it is first accessed from -; the server and do not wait for a successful decryption -instantburnafterreading = false - ; which display mode to preselect by default, defaults to "plaintext" ; make sure the value exists in [formatter_options] defaultformatter = "plaintext" @@ -85,10 +81,6 @@ zerobincompatibility = false ; make sure the value exists in [expire_options] default = "1week" -; optionally the "clone" button can be disabled on expiring pastes -; note that this only hides the button, copy & paste is still possible -; clone = false - [expire_options] ; Set each one of these to the number of seconds in the expiration period, ; or 0 if it should never expire diff --git a/js/privatebin.js b/js/privatebin.js index 3c8b02dc..90131568 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -45,6 +45,18 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) { var Helper = (function () { var me = {}; + /** + * blacklist of UserAgents (parts) known to belong to a bot + * + * @private + * @enum {Object} + * @readonly + */ + var BadBotUA = [ + 'Bot', + 'bot' + ]; + /** * cache for script location * @@ -121,7 +133,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) { * URLs to handle: *
* magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
- * http://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+ * https://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
* http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
*
*
@@ -204,7 +216,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
/**
* get the current location (without search or hash part of the URL),
- * eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/
+ * eg. https://example.com/path/?aaaa#bbbb --> https://example.com/path/
*
* @name Helper.baseUri
* @function
@@ -232,6 +244,26 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
baseUri = null;
};
+ /**
+ * checks whether this is a bot we dislike
+ *
+ * @name Helper.isBadBot
+ * @function
+ * @return {bool}
+ */
+ me.isBadBot = function() {
+ // check whether a bot user agent part can be found in the current
+ // user agent
+ var arrayLength = BadBotUA.length;
+ for (var i = 0; i < arrayLength; i++) {
+ if (navigator.userAgent.indexOf(BadBotUA) >= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
return me;
})();
@@ -358,7 +390,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
// file is loaded
}
- // for all other langauges than English for which this behaviour
+ // for all other languages than English for which this behaviour
// is expected as it is built-in, log error
if (language !== null && language !== 'en') {
console.error('Missing translation for: \'' + messageId + '\' in language ' + language);
@@ -623,7 +655,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
var Model = (function () {
var me = {};
- var $cipherData,
+ var pasteData = null,
$templates;
var id = null, symmetricKey = null;
@@ -653,32 +685,53 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
};
/**
- * check if cipher data was supplied
+ * returns the paste data (including the cipher data)
*
- * @name Model.getCipherData
- * @function
- * @return boolean
- */
- me.hasCipherData = function()
- {
- return me.getCipherData().length > 0;
- };
-
- /**
- * returns the cipher data
- *
- * @name Model.getCipherData
+ * @name Model.getPasteData
* @function
+ * @param {function} callback (optional) Called when data is available
+ * @param {function} useCache (optional) Whether to use the cache or
+ * force a data reload. Default: true
* @return string
*/
- me.getCipherData = function()
+ me.getPasteData = function(callback, useCache)
{
- return $cipherData.text();
+ // use cache if possible/allowed
+ if (useCache !== false && pasteData !== null) {
+ //execute callback
+ if (typeof callback === 'function') {
+ return callback(pasteData);
+ }
+
+ // alternatively just using inline
+ return pasteData;
+ }
+
+ // reload data
+ Uploader.prepare();
+ Uploader.setUrl(Helper.baseUri() + '?' + me.getPasteId());
+
+ Uploader.setFailure(function (status, data) {
+ // revert loading status…
+ Alert.hideLoading();
+ TopNav.showViewButtons();
+
+ // show error message
+ Alert.showError(Uploader.parseUploadError(status, data, 'getting paste data'));
+ });
+ Uploader.setSuccess(function (status, data) {
+ pasteData = data;
+
+ if (typeof callback === 'function') {
+ return callback(data);
+ }
+ });
+ Uploader.run();
};
/**
* get the pastes unique identifier from the URL,
- * eg. http://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487
+ * eg. https://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487
*
* @name Model.getPasteId
* @function
@@ -688,6 +741,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
me.getPasteId = function()
{
if (id === null) {
+ // Attention: This also returns the delete token inside of the ID, if it is specified
id = window.location.search.substring(1);
if (id === '') {
@@ -696,7 +750,19 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
}
return id;
- };
+ }
+
+ /**
+ * Returns true, when the URL has a delete token and the current call was used for deleting a paste.
+ *
+ * @name Model.hasDeleteToken
+ * @function
+ * @return {bool}
+ */
+ me.hasDeleteToken = function()
+ {
+ return window.location.search.indexOf('deletetoken') !== -1;
+ }
/**
* return the deciphering key stored in anchor part of the URL
@@ -751,7 +817,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
*/
me.reset = function()
{
- $cipherData = $templates = id = symmetricKey = null;
+ pasteData = $templates = id = symmetricKey = null;
};
/**
@@ -764,7 +830,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
*/
me.init = function()
{
- $cipherData = $('#cipherdata');
$templates = $('#templates');
};
@@ -1259,8 +1324,8 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
if (pasteMetaData.burnafterreading) {
// display paste "for your eyes only" if it is deleted
- // actually remove paste, before we claim it is deleted
- Controller.removePaste(Model.getPasteId(), 'burnafterreading');
+ // the paste has been deleted when the JSON with the ciphertext
+ // has been downloaded
Alert.showRemaining("FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.");
$remainingTime.addClass('foryoureyesonly');
@@ -1402,6 +1467,21 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
return password;
};
+ /**
+ * resets the password to an empty string
+ *
+ * @name Prompt.reset
+ * @function
+ */
+ me.reset = function()
+ {
+ // reset internal
+ password = '';
+
+ // and also reset UI
+ $passwordDecrypt.val('');
+ }
+
/**
* init status manager
*
@@ -2149,7 +2229,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
loadedFile = $fileInput[0].files[0];
$dragAndDropFileName.text('');
} else {
- // TODO: cannot set original $fileWrap here for security reasons…
$dragAndDropFileName.text(loadedFile.name);
}
@@ -2295,6 +2374,10 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
if (items.hasOwnProperty(i)) {
var item = items[i];
if (item.kind === 'file') {
+ //Clear the file input:
+ $fileInput.wrap(''
);
$.PrivateBin.Model.init();
$.PrivateBin.Prompt.init();
@@ -31,6 +32,7 @@ describe('Prompt', function () {
$('#passworddecrypt').val(password);
$('#passwordform').submit();
var result = $.PrivateBin.Prompt.getPassword();
+ $.PrivateBin.Model.reset();
clean();
return result === password;
}
diff --git a/lib/Configuration.php b/lib/Configuration.php
index f505b90f..48105692 100644
--- a/lib/Configuration.php
+++ b/lib/Configuration.php
@@ -43,7 +43,6 @@ class Configuration
'password' => true,
'fileupload' => false,
'burnafterreadingselected' => false,
- 'instantburnafterreading' => false,
'defaultformatter' => 'plaintext',
'syntaxhighlightingtheme' => null,
'sizelimit' => 2097152,
@@ -59,7 +58,6 @@ class Configuration
),
'expire' => array(
'default' => '1week',
- 'clone' => true,
),
'expire_options' => array(
'5min' => 300,
diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php
index 1bac7c8b..d8749a99 100644
--- a/lib/Model/Paste.php
+++ b/lib/Model/Paste.php
@@ -49,7 +49,7 @@ class Paste extends AbstractModel
}
// check if non-expired burn after reading paste needs to be deleted
- if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading && $this->_conf->getKey('instantburnafterreading')) {
+ if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading) {
$this->delete();
}
@@ -72,6 +72,12 @@ class Paste extends AbstractModel
$data->comment_offset = 0;
$data->{'@context'} = 'js/paste.jsonld';
$this->_data = $data;
+
+ // If the paste was meant to be read only once, delete it.
+ if ($this->isBurnafterreading()) {
+ $this->delete();
+ }
+
return $this->_data;
}
@@ -163,7 +169,7 @@ class Paste extends AbstractModel
*
* The token is the hmac of the pastes ID signed with the server salt.
* The paste can be deleted by calling:
- * http://example.com/privatebin/?pasteid=