change zip size check to spawn

This commit is contained in:
Henry Oswald 2016-03-12 15:43:16 +00:00
parent f11ba97389
commit 8812ff445e
2 changed files with 95 additions and 13 deletions

View file

@ -3,32 +3,70 @@ logger = require "logger-sharelatex"
metrics = require "../../infrastructure/Metrics" metrics = require "../../infrastructure/Metrics"
fs = require "fs" fs = require "fs"
Path = require "path" Path = require "path"
_ = require("underscore")
ONE_MEG = 1024 * 1024 ONE_MEG = 1024 * 1024
module.exports = ArchiveManager = module.exports = ArchiveManager =
_isZipTooLarge: (source, callback = (err, isTooLarge)->)->
callback = _.once callback
unzip = child.spawn("unzip", ["-l", source])
output = ""
unzip.stdout.on "data", (d)->
output += d
error = null
unzip.stderr.on "data", (chunk) ->
error ||= ""
error += chunk
unzip.on "error", (err) ->
logger.error {err, source, destination}, "unzip failed"
if err.code == "ENOENT"
logger.error "unzip command not found. Please check the unzip command is installed"
callback(err)
unzip.on "exit", () ->
if error?
error = new Error(error)
logger.error err:error, source: source, destination: destination, "error checking zip size"
lines = output.split("\n")
lastLine = lines[lines.length - 2]?.trim()
totalSizeInBytes = lastLine?.split(" ")?[0]
totalSizeInBytes = parseInt(totalSizeInBytes)
if !totalSizeInBytes? or isNaN(totalSizeInBytes)
logger.err source:source, "error getting bytes of zip"
return callback(new Error("something went wrong"))
isTooLarge = totalSizeInBytes > (ONE_MEG * 300)
callback(error, isTooLarge)
extractZipArchive: (source, destination, _callback = (err) ->) -> extractZipArchive: (source, destination, _callback = (err) ->) ->
callback = (args...) -> callback = (args...) ->
_callback(args...) _callback(args...)
_callback = () -> _callback = () ->
child.exec "unzip -l #{source} | tail -n 1", (err, result)-> ArchiveManager._isZipTooLarge source, (err, isTooLarge)->
if err? if err?
logger.err err:err, "error checking size of zip file" logger.err err:err, "error checking size of zip file"
return callback(err) return callback(err)
totalSizeInBytes = result.trim()?.split(" ")?[0] if isTooLarge
if !totalSizeInBytes?
logger.err source:source, "error getting bytes of zip"
return callback(new Error("something went wrong"))
if totalSizeInBytes > ONE_MEG * 300
logger.log source:source, totalSizeInBytes:totalSizeInBytes, "zip file too large"
return callback(new Error("zip_too_large")) return callback(new Error("zip_too_large"))
timer = new metrics.Timer("unzipDirectory") timer = new metrics.Timer("unzipDirectory")
logger.log source: source, destination: destination, "unzipping file" logger.log source: source, destination: destination, "unzipping file"

View file

@ -1,4 +1,5 @@
sinon = require('sinon') sinon = require('sinon')
expect = require("chai").expect
chai = require('chai') chai = require('chai')
should = chai.should() should = chai.should()
modulePath = "../../../../app/js/Features/Uploads/ArchiveManager.js" modulePath = "../../../../app/js/Features/Uploads/ArchiveManager.js"
@ -9,13 +10,16 @@ describe "ArchiveManager", ->
beforeEach -> beforeEach ->
@logger = @logger =
error: sinon.stub() error: sinon.stub()
err:->
log: sinon.stub() log: sinon.stub()
@process = new events.EventEmitter @process = new events.EventEmitter
@process.stdout = new events.EventEmitter @process.stdout = new events.EventEmitter
@process.stderr = new events.EventEmitter @process.stderr = new events.EventEmitter
@child = @child =
spawn: sinon.stub().returns(@process) spawn: sinon.stub().returns(@process)
exec: sinon.stub().callsArgWith(1, null, " 109042 2 files")
@metrics = @metrics =
Timer: class Timer Timer: class Timer
done: sinon.stub() done: sinon.stub()
@ -30,6 +34,7 @@ describe "ArchiveManager", ->
@source = "/path/to/zip/source.zip" @source = "/path/to/zip/source.zip"
@destination = "/path/to/zip/destination" @destination = "/path/to/zip/destination"
@callback = sinon.stub() @callback = sinon.stub()
@ArchiveManager._isZipTooLarge = sinon.stub().callsArgWith(1, null, false)
describe "successfully", -> describe "successfully", ->
beforeEach (done) -> beforeEach (done) ->
@ -61,7 +66,7 @@ describe "ArchiveManager", ->
describe "with a zip that is too large", -> describe "with a zip that is too large", ->
beforeEach (done) -> beforeEach (done) ->
@child.exec = sinon.stub().callsArgWith(1, null, " 10000000000009042 2 files") @ArchiveManager._isZipTooLarge = sinon.stub().callsArgWith(1, null, true)
@ArchiveManager.extractZipArchive @source, @destination, (error) => @ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error) @callback(error)
done() done()
@ -85,6 +90,45 @@ describe "ArchiveManager", ->
it "should log out the error", -> it "should log out the error", ->
@logger.error.called.should.equal true @logger.error.called.should.equal true
describe "_isZipTooLarge", ->
beforeEach ->
@output = (totalSize)->" Length Date Time Name \n-------- ---- ---- ---- \n241 03-12-16 12:20 main.tex \n108801 03-12-16 12:20 ddd/x1J5kHh.jpg \n-------- ------- \n#{totalSize} 2 files\n"
it "should return false with small output", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
isTooLarge.should.equal false
done()
@process.stdout.emit "data", @output("109042")
@process.emit "exit"
it "should return true with large bytes", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
isTooLarge.should.equal true
done()
@process.stdout.emit "data", @output("1090000000000000042")
@process.emit "exit"
it "should return error on no data", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", ""
@process.emit "exit"
it "should return error if it didn't get a number", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", @output("total_size_string")
@process.emit "exit"
it "should return error if the is only a bit of data", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", " Length Date Time Name \n--------"
@process.emit "exit"
describe "findTopLevelDirectory", -> describe "findTopLevelDirectory", ->
beforeEach -> beforeEach ->
@fs.readdir = sinon.stub() @fs.readdir = sinon.stub()