diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index 325df9369f..74f6108ba0 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -4,38 +4,57 @@ metrics = require "../../infrastructure/Metrics" fs = require "fs" Path = require "path" +ONE_MEG = 1024 * 1024 + module.exports = ArchiveManager = extractZipArchive: (source, destination, _callback = (err) ->) -> callback = (args...) -> _callback(args...) _callback = () -> - timer = new metrics.Timer("unzipDirectory") - logger.log source: source, destination: destination, "unzipping file" + child.exec "unzip -l #{source} | tail -n 1", (err, result)-> + if err? + logger.err err:err, "error checking size of zip file" + return callback(err) - unzip = child.spawn("unzip", [source, "-d", destination]) + totalSizeInBytes = result.trim()?.split(" ")?[0] + + if !totalSizeInBytes? + logger.err source:source, "error getting bytes of zip" + return callback(new Error("something went wrong")) - # don't remove this line, some zips need - # us to listen on this for some unknow reason - unzip.stdout.on "data", (d)-> + if totalSizeInBytes > ONE_MEG * 300 + logger.log source:source, totalSizeInBytes:totalSizeInBytes, "zip file too large" + return callback(new Error("zip_too_large")) + - 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) + timer = new metrics.Timer("unzipDirectory") + logger.log source: source, destination: destination, "unzipping file" - unzip.on "exit", () -> - timer.done() - if error? - error = new Error(error) - logger.error err:error, source: source, destination: destination, "error unzipping file" - callback(error) + unzip = child.spawn("unzip", [source, "-d", destination]) + + # don't remove this line, some zips need + # us to listen on this for some unknow reason + unzip.stdout.on "data", (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", () -> + timer.done() + if error? + error = new Error(error) + logger.error err:error, source: source, destination: destination, "error unzipping file" + callback(error) findTopLevelDirectory: (directory, callback = (error, topLevelDir) ->) -> fs.readdir directory, (error, files) -> diff --git a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee index 079ab6d099..f1547d0413 100644 --- a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee @@ -15,6 +15,7 @@ describe "ArchiveManager", -> @process.stderr = new events.EventEmitter @child = spawn: sinon.stub().returns(@process) + exec: sinon.stub().callsArgWith(1, null, " 109042 2 files") @metrics = Timer: class Timer done: sinon.stub() @@ -58,6 +59,19 @@ describe "ArchiveManager", -> it "should log out the error", -> @logger.error.called.should.equal true + describe "with a zip that is too large", -> + beforeEach (done) -> + @child.exec = sinon.stub().callsArgWith(1, null, " 10000000000009042 2 files") + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + + it "should return the callback with an error", -> + @callback.calledWithExactly(new Error("zip_too_large")).should.equal true + + it "should not call spawn", -> + @child.spawn.called.should.equal false + describe "with an error on the process", -> beforeEach (done) -> @ArchiveManager.extractZipArchive @source, @destination, (error) =>