diff --git a/services/real-time/app/coffee/DocumentUpdaterManager.coffee b/services/real-time/app/coffee/DocumentUpdaterManager.coffee index ab893aa47f..e183d11cec 100644 --- a/services/real-time/app/coffee/DocumentUpdaterManager.coffee +++ b/services/real-time/app/coffee/DocumentUpdaterManager.coffee @@ -54,6 +54,10 @@ module.exports = DocumentUpdaterManager = queueChange: (project_id, doc_id, change, callback = ()->)-> jsonChange = JSON.stringify change + if jsonChange.indexOf("\u0000") != -1 + error = new Error("null bytes found in op") + logger.error err: error, project_id: project_id, doc_id: doc_id, jsonChange: jsonChange, error.message + return callback(error) doc_key = "#{project_id}:#{doc_id}" # Push onto pendingUpdates for doc_id first, because once the doc updater # gets an entry on pending-updates-list, it starts processing. diff --git a/services/real-time/test/unit/coffee/DocumentUpdaterManagerTests.coffee b/services/real-time/test/unit/coffee/DocumentUpdaterManagerTests.coffee index e6f9b2098b..2d3e0ad3af 100644 --- a/services/real-time/test/unit/coffee/DocumentUpdaterManagerTests.coffee +++ b/services/real-time/test/unit/coffee/DocumentUpdaterManagerTests.coffee @@ -17,14 +17,17 @@ describe 'DocumentUpdaterManager', -> pendingUpdates: ({doc_id}) -> "PendingUpdates:#{doc_id}" @rclient = {auth:->} - @DocumentUpdaterManager = SandboxedModule.require modulePath, requires: - 'settings-sharelatex':@settings - 'logger-sharelatex': @logger = {log: sinon.stub(), error: sinon.stub(), warn: sinon.stub()} - 'request': @request = {} - 'redis-sharelatex' : createClient: () => @rclient - 'metrics-sharelatex': @Metrics = - Timer: class Timer - done: () -> + @DocumentUpdaterManager = SandboxedModule.require modulePath, + requires: + 'settings-sharelatex':@settings + 'logger-sharelatex': @logger = {log: sinon.stub(), error: sinon.stub(), warn: sinon.stub()} + 'request': @request = {} + 'redis-sharelatex' : createClient: () => @rclient + 'metrics-sharelatex': @Metrics = + Timer: class Timer + done: () -> + globals: + JSON: @JSON = Object.create(JSON) # avoid modifying JSON object directly describe "getDocument", -> beforeEach -> @@ -147,3 +150,14 @@ describe 'DocumentUpdaterManager', -> it "should return an error", -> @callback.calledWithExactly(sinon.match(Error)).should.equal true + + describe "with null byte corruption", -> + beforeEach -> + @JSON.stringify = () -> return '["bad bytes! \u0000 <- here"]' + @DocumentUpdaterManager.queueChange(@project_id, @doc_id, @change, @callback) + + it "should return an error", -> + @callback.calledWithExactly(sinon.match(Error)).should.equal true + + it "should not push the change onto the pending-updates-list queue", -> + @rclient.rpush.called.should.equal false