From 5e6f4c738bd807821bf474c20cb6188090ccb0ad Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 9 Jan 2019 15:19:22 +0100 Subject: [PATCH 01/10] update changelog for Pademelon release --- CHANGELOG.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20ec08a00..aed467811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,47 @@ +# Pademelon release (v2.15.0) + +## Goals + +For this release we planned to improve upon last release's introduction of the display of other users' cursors in our code and slide editors by adding the same functionality to our rich text editor. + +Beyond just producing software, the CryptPad team has also begun to produce peer-reviewed papers. +We have previously published [Private Document Editing with Some Trust](https://dl.acm.org/citation.cfm?doid=3209280.3209535) as a part of the 2018 proceedings of the ACM Symposium on Document Engineering. +We have recently been accepted for publication as a part of [HCI-CPT](http://2019.hci.international/hci-cpt): the first international conference on HCI (Human Computer Interaction) for cybersecurity, privacy and trust. +In preparation for this publication we've begun to collect additional usage data in order to inform the wider community of our findings regarding usability of cryptography-based collaboration systems. + +## Update notes + +* Updating to version 2.15.0 from 2.14.0 should only require that update to the latest clientside code via git, and update any cache-busting parameters you've set. +* Several of our third-party clientside dependencies have been updated, and you may optionally run `bower update` to receive their latest versions. +* As explained above, we have added a number of new keys to our existing feedback system. The new keys are detailed below + * HOME_SUPPORT_CRYPTPAD informs us when users discover our opencollective campaign from the CryptPad home page + * UPGRADE_ACCOUNT informs us when someone clicks the upgrade account button from their CryptDrive or settings page + * SUPPORT_CRYPTPAD is not active on our CryptPad instance, since this key is only sent when clicking the _donate button_ which is shown when upgraded accounts are disabled + * DELETE_ACCOUNT_AUTOMATIC informs us when somebody deletes their account automatically from the settings page. Automatic account deletion is only available for accounts created since version 1.29.0 + * DELETE_ACCOUNT_MANUAL informs us when a user generates the proof of their account ownership which is required for manual account deletion. This feature is available only for accounts predating version 1.29.0 + * OWNED_DRIVE_MIGRATION informs us when a user migrates their CryptDrive from our legacy format (which does not support automatic deletion) to our newer format (which does) via the settings page + * PASSWORD_CHANGED informs us when a user changes their password from the settings page + * NO_WEBRTC informs us when a users browser does not support WebRTC at all via a crude test which never actually runs any WebRTC-based code + * SUBSCRIPTION_BUTTON informs us when a user navigates to our paid account administration panel from their settings page + * LOGOUT_EVERYWHERE informs us when a user executes the command to log out of their account on all remote devices from the settings page +* We've implemented the ability to configure which applications are available on a particular CryptPad instance via `cryptpad/customize/application_config.js`. Two arrays (`config.availablePadTypes` and `config.registeredOnlyTypes`) define which applications are available to everyone, and which applications are available to registered users. Due to a bug which was discovered, this behaviour is incorrect for our encrypted file viewer, and as a result encrypted files cannot currently be disabled. This will be addressed in our next release. + +## Features + +* Our rich text editor now displays other users' cursors when editing with a group. Preferences for this behaviour can be defined via the settings page. +* Links in our rich text editor can now be clicked more easily, as a small tooltip with a clickable link will be displayed above the editable link in the document. +* Users who wish to be notified of spelling errors in their rich text pads can enable spellcheck via the settings page. +* As noted above, various pad types can be disabled by instance administrators via `customize/application_config.js`. +* We've enabled a feature in the settings page which will migrate users' CryptDrive from our legacy format to our latest format (which supports automatic deletion). Only users with accounts dating back to version 1.29.0 will notice any difference. +* We've worked to improve some usability issues presented by the interaction of _owned files_ and _shared folders_. Since only the owner of an owned document can delete it the owner must keep a record of that document in their CryptDrive even if they place it in a shared folder (where someone else could delete it while they are offline). As such, owned documents were always copied to shared folders instead of being moved, and this proliferation of copies made it more difficult for users to organize their CryptDrives. Duplicated owned documents which are kept in your CryptDrive can now be hidden via the settings page. If those files are removed from a shared folder by another user, the hidden duplicate will be revealed in the root of your CryptDrive's tree. +* Finally, we've implemented the ability to copy documents to multiple shared folders via an entry in the right-click menu for any such document. + +## Bugfixes + +* We've improved the styles for displaying other users' cursors in the code and slide editors to avoid moving your view of the text when someone else highlights it. +* We've also changed some of the logic for how often other users' cursors are updated and displayed, so as to maximize the accuracy of their position and not show incorrect placements while you are typing. +* We fixed a bug which caused errors while loading your CryptDrive after a shared folder had been deleted. + # Opossum release (v2.14.0) ## Goals From 0f644b04b2c0b674aefd17d0d9d46aa386daa25e Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 9 Jan 2019 16:16:32 +0100 Subject: [PATCH 02/10] send feedback related to full drive export --- www/settings/inner.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/settings/inner.js b/www/settings/inner.js index 4b1836e64..ddc8c0745 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -930,6 +930,7 @@ define([ $(cancel).click(function () { UI.confirm(Messages.settings_exportCancel, function (yes) { if (!yes) { return; } + Feedback.send('FULL_DRIVE_EXPORT_CANCEL'); _onCancel.forEach(function (h) { h(); }); }); }).appendTo(actions); @@ -1078,6 +1079,7 @@ define([ // Backup all the pads var exportDrive = function () { + Feedback.send('FULL_DRIVE_EXPORT_START'); var todo = function (data, filename) { var getPad = function (data, cb) { sframeChan.query("Q_CRYPTGET", data, function (err, obj) { @@ -1094,6 +1096,7 @@ define([ saveAs(blob, filename); sframeChan.event('EV_CRYPTGET_DISCONNECT'); ui.complete(function () { + Feedback.send('FULL_DRIVE_EXPORT_COMPLETE'); saveAs(blob, filename); }, errors); }, ui.update); From 060e5a12a2f3bcaf6dfb1ff1f9ed97aeff0b9faa Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 9 Jan 2019 16:16:44 +0100 Subject: [PATCH 03/10] lint compliance --- www/pad/inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/pad/inner.js b/www/pad/inner.js index 18cccfc6b..9b56e56bb 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -452,8 +452,8 @@ define([ var $iframe = $('html').find('iframe').contents(); var ifrWindow = $html.find('iframe')[0].contentWindow; - var customCss = '/customize/ckeditor-contents.css?' + CKEDITOR.CRYPTPAD_URLARGS; - $iframe.find('head').append('') + var customCss = '/customize/ckeditor-contents.css?' + window.CKEDITOR.CRYPTPAD_URLARGS; + $iframe.find('head').append(''); framework._.sfCommon.addShortcuts(ifrWindow); From 275a3c80fcf7577d89bc93089c7ec91dc4cc537e Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 10 Jan 2019 18:20:32 +0100 Subject: [PATCH 04/10] roll back to less 3.7.1 because newer versions break IE see https://github.com/less/less.js/issues/3321 --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 27b1ea1f4..94c6af3a7 100644 --- a/bower.json +++ b/bower.json @@ -36,7 +36,7 @@ "alertifyjs": "1.0.11", "scrypt-async": "1.2.0", "require-css": "0.1.10", - "less": "^3.7.1", + "less": "3.7.1", "bootstrap": "^v4.0.0", "diff-dom": "2.1.1", "nthen": "^0.1.5", From 20959f84ae870c451021ff898fadb8d79581fe74 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 10 Jan 2019 18:21:39 +0100 Subject: [PATCH 05/10] return Boolean from our test for WebRTC support --- www/common/cryptpad-common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 0b72dccbc..ab956ba74 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1112,11 +1112,11 @@ define([ }; common.isWebRTCSupported = function () { - return navigator.getUserMedia || + return Boolean(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || - window.RTCPeerConnection; + window.RTCPeerConnection); }; common.ready = (function () { From 77c0cf7359d4b33f53010001b5b5c399953613f4 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 11 Jan 2019 16:58:39 +0100 Subject: [PATCH 06/10] guard against calling an undefined function --- www/common/common-feedback.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/common-feedback.js b/www/common/common-feedback.js index f0b7d989b..d377bae5c 100644 --- a/www/common/common-feedback.js +++ b/www/common/common-feedback.js @@ -22,6 +22,7 @@ define([ http.send(); }; Feedback.send = function (action, force, cb) { + if (typeof(cb) !== 'function') { cb = function () {}; } if (AppConfig.disableFeedback) { return void cb(); } if (!action) { return void cb(); } if (force !== true) { From 8bab799ac981c02fd3f43ec6475615f948b1cded Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 11 Jan 2019 16:58:39 +0100 Subject: [PATCH 07/10] guard against calling an undefined function --- www/common/common-feedback.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/common-feedback.js b/www/common/common-feedback.js index f0b7d989b..d377bae5c 100644 --- a/www/common/common-feedback.js +++ b/www/common/common-feedback.js @@ -22,6 +22,7 @@ define([ http.send(); }; Feedback.send = function (action, force, cb) { + if (typeof(cb) !== 'function') { cb = function () {}; } if (AppConfig.disableFeedback) { return void cb(); } if (!action) { return void cb(); } if (force !== true) { From 6f89156415a9849da826ffb185f87bd328b25e6c Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 11 Jan 2019 16:58:39 +0100 Subject: [PATCH 08/10] guard against calling an undefined function --- www/common/common-feedback.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/common-feedback.js b/www/common/common-feedback.js index f0b7d989b..d377bae5c 100644 --- a/www/common/common-feedback.js +++ b/www/common/common-feedback.js @@ -22,6 +22,7 @@ define([ http.send(); }; Feedback.send = function (action, force, cb) { + if (typeof(cb) !== 'function') { cb = function () {}; } if (AppConfig.disableFeedback) { return void cb(); } if (!action) { return void cb(); } if (force !== true) { From 9105a89eab0216eb79d6477e0c321d3ee8dd0dd0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 14 Jan 2019 15:27:09 +0100 Subject: [PATCH 09/10] intantiate ephemeral channels with 34 hex characters --- www/common/common-hash.js | 7 ++++--- www/common/sframe-common.js | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/www/common/common-hash.js b/www/common/common-hash.js index e089852cc..0f5341d9b 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -76,9 +76,10 @@ define([ return s.replace(/\/+/g, '/'); }; - Hash.createChannelId = function () { - var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16)); - if (id.length !== 32 || /[^a-f0-9]/.test(id)) { + Hash.ephemeralChannelLength = 34; + Hash.createChannelId = function (ephemeral) { + var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(ephemeral? 17: 16)); + if ([32, 34].indexOf(id.length) === -1 || /[^a-f0-9]/.test(id)) { throw new Error('channel ids must consist of 32 hex characters'); } return id; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 468d724dd..bbbbe266b 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -197,7 +197,10 @@ define([ }; funcs.openCursorChannel = function (saveChanges) { var md = JSON.parse(JSON.stringify(ctx.metadataMgr.getMetadata())); - var channel = md.cursor || Hash.createChannelId(); + var channel = md.cursor; + if (typeof(channel) !== 'string' || channel.length !== Hash.ephemeralChannelLength) { + channel = Hash.createChannelId(true); // true indicates that it's an ephemeral channel + } if (!md.cursor) { md.cursor = channel; ctx.metadataMgr.updateMetadata(md); From 6cf8720458e462833c13e760f1f528f977869a49 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 14 Jan 2019 16:40:54 +0100 Subject: [PATCH 10/10] update how the async store chooses to prune history to avoid the diverged checkpoint bug --- www/common/outer/async-store.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index bbd2ba8f7..a329f5e33 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -958,12 +958,20 @@ define([ history: [], pushHistory: function (msg, isCp) { if (isCp) { + // the current message is a checkpoint. + // push it to your worker's history, prepending it with cp| + // cp| and anything else related to checkpoints has already + // been stripped by chainpad-netflux-worker or within async store + // when the message was outgoing. channel.history.push('cp|' + msg); + // since the latest message is a checkpoint, we are able to drop + // some of the older history, but we can't rely on checkpoints being + // correct, as they might be checkpoints from different forks var i; - for (i = channel.history.length - 2; i > 0; i--) { + for (i = channel.history.length - 101; i > 0; i--) { if (/^cp\|/.test(channel.history[i])) { break; } } - channel.history = channel.history.slice(i); + channel.history = channel.history.slice(Math.max(i, 0)); return; } channel.history.push(msg);