From 6b9ffe8dd18543479e99584bb36717e049f11bdc Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Tue, 7 Nov 2017 14:51:53 +0100
Subject: [PATCH 1/3] Make thumbnails more secure

---
 www/common/common-thumbnail.js     | 33 +++++++++++++++++-------------
 www/common/cryptpad-common.js      | 10 ++++++++-
 www/common/sframe-app-framework.js |  2 +-
 www/common/sframe-common-outer.js  | 14 +++++++++++++
 www/common/sframe-common.js        | 27 ++++++++++++++++++++----
 www/common/sframe-protocol.js      |  6 +++++-
 www/poll/inner.js                  |  2 +-
 www/whiteboard/inner.js            |  2 +-
 8 files changed, 73 insertions(+), 23 deletions(-)

diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js
index 5f5a8106a..6ec60d063 100644
--- a/www/common/common-thumbnail.js
+++ b/www/common/common-thumbnail.js
@@ -4,9 +4,8 @@ define([
     '/common/visible.js',
     '/common/common-hash.js',
     '/file/file-crypto.js',
-    '/bower_components/localforage/dist/localforage.min.js',
     '/bower_components/tweetnacl/nacl-fast.min.js',
-], function ($, Util, Visible, Hash, FileCrypto, localForage) {
+], function ($, Util, Visible, Hash, FileCrypto) {
     var Nacl = window.nacl;
     var Thumb = {
         dimension: 100,
@@ -196,7 +195,7 @@ define([
         require(['/bower_components/html2canvas/build/html2canvas.min.js'], todo);
     };
 
-    Thumb.initPadThumbnails = function (opts) {
+    Thumb.initPadThumbnails = function (common, opts) {
         if (!opts.href || !opts.getContent) {
             throw new Error("href and getContent are needed for thumbnails");
         }
@@ -206,7 +205,7 @@ define([
             if (content === oldThumbnailState) { return; }
             Thumb.fromDOM(opts, function (err, b64) {
                 oldThumbnailState = content;
-                Thumb.setPadThumbnail(opts.href, b64);
+                Thumb.setPadThumbnail(common, opts.href, b64);
             });
         };
         var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
@@ -238,15 +237,19 @@ define([
         $span.prepend(img);
         cb($(img));
     };
-    Thumb.setPadThumbnail = function (href, b64, cb) {
-        cb = cb || function () {};
-        var k  ='thumbnail-' + href;
-        localForage.setItem(k, b64, cb);
+    var getKey = function (href) {
+        var parsed = Hash.parsePadUrl(href);
+        return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
     };
-    Thumb.displayThumbnail = function (href, $container, cb) {
+    Thumb.setPadThumbnail = function (common, href, b64, cb) {
+        cb = cb || function () {};
+        var k = getKey(href);
+        common.setThumbnail(k, b64, cb);
+    };
+    Thumb.displayThumbnail = function (common, href, $container, cb) {
         cb = cb || function () {};
         var parsed = Hash.parsePadUrl(href);
-        var k  ='thumbnail-' + href;
+        var k = getKey(href);
         var whenNewThumb = function () {
             var secret = Hash.getSecrets('file', parsed.hash);
             var hexFileName = Util.base64ToHex(secret.channel);
@@ -254,15 +257,17 @@ define([
             var cryptKey = secret.keys && secret.keys.fileKeyStr;
             var key = Nacl.util.decodeBase64(cryptKey);
             FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
-                if (!metadata.thumbnail) {
-                    return void localForage.setItem(k, 'EMPTY');
+                var v = metadata.thumbnail;
+                if (!v) {
+                    v = 'EMPTY';
                 }
-                localForage.setItem(k, metadata.thumbnail, function (err) {
+                Thumb.setPadThumbnail(common, href, v, function (err) {
+                    if (!metadata.thumbnail) { return; }
                     addThumbnail(err, metadata.thumbnail, $container, cb);
                 });
             });
         };
-        localForage.getItem(k, function (err, v) {
+        common.getThumbnail(k, function (err, v) {
             if (!v && parsed.type === 'file') {
                 // We can only create thumbnails for files here since we can't easily decrypt pads
                 return void whenNewThumb();
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 9789a5ce8..9826427a3 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -21,9 +21,10 @@ define([
     '/customize/application_config.js',
     '/common/media-tag.js',
     '/bower_components/nthen/index.js',
+    '/bower_components/localforage/dist/localforage.min.js',
 ], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
             Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard,
-            Pinpad, AppConfig, MediaTag, Nthen) {
+            Pinpad, AppConfig, MediaTag, Nthen, localForage) {
 
     // Configure MediaTags to use our local viewer
     if (MediaTag && MediaTag.PdfPlugin) {
@@ -519,6 +520,13 @@ define([
         });
     };
 
+    common.setThumbnail = function (key, value, cb) {
+        localForage.setItem(key, value, cb);
+    };
+    common.getThumbnail = function (key, cb) {
+        localForage.getItem(key, cb);
+    };
+
     /*  this returns a reference to your proxy. changing it will change your drive.
     */
     var getFileEntry = common.getFileEntry = function (href, cb) {
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index d58710e1b..342108678 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -276,7 +276,7 @@ define([
                         if (!cpNfInner.chainpad) { return; }
                         return cpNfInner.chainpad.getUserDoc();
                     };
-                    Thumb.initPadThumbnails(options.thumbnail);
+                    Thumb.initPadThumbnails(common, options.thumbnail);
                 }
             }
 
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index 0f07ac8a3..abab4761b 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -309,6 +309,20 @@ define([
                 });
             });
 
+            sframeChan.on('Q_THUMBNAIL_GET', function (data, cb) {
+                Cryptpad.getThumbnail(data.key, function (e, data) {
+                    cb({
+                        error: e,
+                        data: data
+                    });
+                });
+            });
+            sframeChan.on('Q_THUMBNAIL_SET', function (data, cb) {
+                Cryptpad.setThumbnail(data.key, data.value, function (e) {
+                    cb({error:e});
+                });
+            });
+
             sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) {
                 sessionStorage[data.key] = data.value;
                 cb();
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index 2a539e88e..1fd8981fb 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -15,7 +15,8 @@ define([
     '/common/cryptpad-common.js',
     '/common/common-realtime.js',
     '/common/common-util.js',
-    '/common/common-thumbnail.js'
+    '/common/common-thumbnail.js',
+    '/bower_components/localforage/dist/localforage.min.js'
 ], function (
     $,
     nThen,
@@ -32,9 +33,9 @@ define([
     Cryptpad,
     CommonRealtime,
     Util,
-    Thumb
+    Thumb,
+    localForage
 ) {
-
     // Chainpad Netflux Inner
     var funcs = {};
     var ctx = {};
@@ -84,7 +85,7 @@ define([
     funcs.updateTags = callWithCommon(UI.updateTags);
 
     // Thumb
-    funcs.displayThumbnail = Thumb.displayThumbnail;
+    funcs.displayThumbnail = callWithCommon(Thumb.displayThumbnail);
 
     // History
     funcs.getHistory = callWithCommon(History.create);
@@ -218,6 +219,22 @@ define([
         }, cb);
     };
 
+    // Thumbnails
+    funcs.setThumbnail = function (key, value, cb) {
+        cb = cb || $.noop;
+        ctx.sframeChan.query('Q_THUMBNAIL_SET', {
+            key: key,
+            value: value
+        }, cb);
+    };
+    funcs.getThumbnail = function (key, cb) {
+        ctx.sframeChan.query('Q_THUMBNAIL_GET', {
+            key: key
+        }, function (err, res) {
+            cb (err || res.error, res.data);
+        });
+    };
+
     funcs.sessionStorage = {
         put: function (key, value, cb) {
             ctx.sframeChan.query('Q_SESSIONSTORAGE_PUT', {
@@ -310,6 +327,8 @@ define([
             SFrameChannel.create(window.parent, waitFor(function (sfc) { ctx.sframeChan = sfc; }), true);
             // CpNfInner.start() should be here....
         }).nThen(function () {
+            localForage.clear();
+
             ctx.metadataMgr = MetadataMgr.create(ctx.sframeChan);
 
             ctx.sframeChan.whenReg('EV_CACHE_PUT', function () {
diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js
index 846502120..e41bc129f 100644
--- a/www/common/sframe-protocol.js
+++ b/www/common/sframe-protocol.js
@@ -171,5 +171,9 @@ define({
     // Add or remove the avatar from the profile.
     // We have to pin/unpin the avatar and store/remove the value from the user object
     'Q_PROFILE_AVATAR_ADD': true,
-    'Q_PROFILE_AVATAR_REMOVE': true
+    'Q_PROFILE_AVATAR_REMOVE': true,
+
+    // Store outside and get thumbnails inside (stored with localForage (indexedDB) outside)
+    'Q_THUMBNAIL_SET': true,
+    'Q_THUMBNAIL_GET': true,
 });
diff --git a/www/poll/inner.js b/www/poll/inner.js
index 783ed9afc..ad7752a3d 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -837,7 +837,7 @@ define([
             href: href,
             getContent: function () { return JSON.stringify(APP.proxy.content); }
         };
-        Thumb.initPadThumbnails(options);
+        Thumb.initPadThumbnails(common, options);
     };
 
     var checkDeletedCells = function () {
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index 7550cb98d..e52cb6828 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -390,7 +390,7 @@ define([
                 var D = Thumb.getResizedDimensions($canvas[0], 'pad');
                 Thumb.fromCanvas($canvas[0], D, function (err, b64) {
                     oldThumbnailState = content;
-                    Thumb.setPadThumbnail(href, b64);
+                    Thumb.setPadThumbnail(common, href, b64);
                 });
             };
             window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL);

From 0956905ca8db252c6b1c77642af4ebef63097fde Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Tue, 7 Nov 2017 14:56:50 +0100
Subject: [PATCH 2/3] Wipe localForage on log out

---
 www/common/cryptpad-common.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 9826427a3..8a5b38c66 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -327,6 +327,7 @@ define([
             delete localStorage[k];
             delete sessionStorage[k];
         });
+        localForage.clear();
         // Make sure we have an FS_hash in localStorage before reloading all the tabs
         // so that we don't end up with tabs using different anon hashes
         if (!localStorage[fileHashKey]) {

From c1ba08cfc621b318e430e04ba2c349628bae8a31 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Tue, 7 Nov 2017 15:04:45 +0100
Subject: [PATCH 3/3] Make sure thumbnails are .png

---
 www/common/common-thumbnail.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js
index 6ec60d063..6c561e52a 100644
--- a/www/common/common-thumbnail.js
+++ b/www/common/common-thumbnail.js
@@ -232,7 +232,7 @@ define([
 
     var addThumbnail = function (err, thumb, $span, cb) {
         var img = new Image();
-        img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:;base64,'+thumb;
+        img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:image/png;base64,'+thumb;
         $span.find('.cp-icon').hide();
         $span.prepend(img);
         cb($(img));