From cc1f3fce5b3b56246f874b55bc0cf38f2b850091 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 8 Apr 2019 13:43:24 +0100 Subject: [PATCH 1/2] check incoming hash when present --- .../app/coffee/ShareJsUpdateManager.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/services/document-updater/app/coffee/ShareJsUpdateManager.coffee b/services/document-updater/app/coffee/ShareJsUpdateManager.coffee index a5cc6070cb..6631c7537b 100644 --- a/services/document-updater/app/coffee/ShareJsUpdateManager.coffee +++ b/services/document-updater/app/coffee/ShareJsUpdateManager.coffee @@ -6,6 +6,7 @@ Keys = require "./UpdateKeys" {EventEmitter} = require "events" util = require "util" RealTimeRedisManager = require "./RealTimeRedisManager" +crypto = require "crypto" ShareJsModel:: = {} util.inherits ShareJsModel, EventEmitter @@ -42,6 +43,10 @@ module.exports = ShareJsUpdateManager = logger.log project_id: project_id, doc_id: doc_id, error: error, "applied update" model.getSnapshot doc_key, (error, data) => return callback(error) if error? + # only check hash when present and no other updates have been applied + if update.hash? and update.v == version + ourHash = ShareJsUpdateManager._computeHash(data.snapshot) + return callback(new Error("Invalid hash")) if ourHash != update.hash docLines = data.snapshot.split(/\r\n|\n|\r/) callback(null, docLines, data.v, model.db.appliedOps[doc_key] or []) @@ -53,3 +58,9 @@ module.exports = ShareJsUpdateManager = _sendOp: (project_id, doc_id, op) -> RealTimeRedisManager.sendData {project_id, doc_id, op} + _computeHash: (content) -> + return crypto.createHash('sha1') + .update("blob " + content.length + "\x00") + .update(content, 'utf8') + .digest('hex') + From 3c635c8d9886baf828dffb1f4f2c1c9cf3223fd4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 Apr 2019 09:20:48 +0100 Subject: [PATCH 2/2] check version before it is modified by applyOp --- .../document-updater/app/coffee/ShareJsUpdateManager.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/document-updater/app/coffee/ShareJsUpdateManager.coffee b/services/document-updater/app/coffee/ShareJsUpdateManager.coffee index 6631c7537b..f4f3674c75 100644 --- a/services/document-updater/app/coffee/ShareJsUpdateManager.coffee +++ b/services/document-updater/app/coffee/ShareJsUpdateManager.coffee @@ -23,7 +23,8 @@ module.exports = ShareJsUpdateManager = applyUpdate: (project_id, doc_id, update, lines, version, callback = (error, updatedDocLines) ->) -> logger.log project_id: project_id, doc_id: doc_id, update: update, "applying sharejs updates" jobs = [] - + # record the update version before it is modified + incomingUpdateVersion = update.version # We could use a global model for all docs, but we're hitting issues with the # internal state of ShareJS not being accessible for clearing caches, and # getting stuck due to queued callbacks (line 260 of sharejs/server/model.coffee) @@ -44,7 +45,7 @@ module.exports = ShareJsUpdateManager = model.getSnapshot doc_key, (error, data) => return callback(error) if error? # only check hash when present and no other updates have been applied - if update.hash? and update.v == version + if update.hash? and incomingUpdateVersion == version ourHash = ShareJsUpdateManager._computeHash(data.snapshot) return callback(new Error("Invalid hash")) if ourHash != update.hash docLines = data.snapshot.split(/\r\n|\n|\r/)