From 998afd1b935bd632e1c3c22a8375048b9742cb77 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Thu, 17 Feb 2022 16:54:22 +0600 Subject: [PATCH] feat: zipbomb detection using static analysis --- package-lock.json | 9 ++++++++- package.json | 1 + src/common/helper.js | 25 +++++++++++++++++++++---- src/services/ProcessorService.js | 18 +++++++++++++++--- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 83d7845..e05cbcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -113,6 +113,11 @@ "to-fast-properties": "^2.0.0" } }, + "@ronomon/pure": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@ronomon/pure/-/pure-1.0.4.tgz", + "integrity": "sha512-imorghj7fJzdnzPD+tJs/d1GAYWPfV4AmbwqPmMI3bWa6IJHfQYRbmWh6zAakyIihv/8gMFqUn68uL+NaJz/dQ==" + }, "@types/bluebird": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.0.tgz", @@ -2558,6 +2563,7 @@ "version": "0.1.4", "bundled": true, "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -3500,7 +3506,8 @@ "longest": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "lru-cache": { "version": "4.1.3", diff --git a/package.json b/package.json index cfee1da..9069ba1 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "standard": "^11.0.1" }, "dependencies": { + "@ronomon/pure": "^1.0.4", "amazon-s3-uri": "0.0.3", "aws-sdk": "^2.300.0", "axios": "^0.18.0", diff --git a/src/common/helper.js b/src/common/helper.js index c934ff7..83f459b 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -13,6 +13,7 @@ const m2mAuth = require('tc-core-library-js').auth.m2m const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL'])) const AWS = require('aws-sdk') const AmazonS3URI = require('amazon-s3-uri') +const pure = require("@ronomon/pure"); AWS.config.region = config.get('aws.REGION') const s3 = new AWS.S3() @@ -39,11 +40,26 @@ function * downloadFile (fileURL) { } } -function * scanWithClamAV (fileURL) { - // Download file from URL - const downloadedFile = yield downloadFile(fileURL) +/** + * check if the file is a zipbomb + * + * @param {string} fileBuffer the file buffer + * @returns + */ + function isZipBomb(fileBuffer) { + const error = pure.zip(fileBuffer, 0); + + // we only care about zip bombs + if (error.code === "PURE_E_OK" || error.code.indexOf("ZIP_BOMB") === -1) { + return [false]; + } else { + return [true, error.code, error.message]; + } +} + +function * scanWithClamAV (file) { // Scan - const fileStream = streamifier.createReadStream(downloadedFile) + const fileStream = streamifier.createReadStream(file) return new Promise((resolve, reject) => { clamavScanner.scan(fileStream, (scanErr, object, malicious) => { if (scanErr) { @@ -85,6 +101,7 @@ function * postToBusAPI (reqBody) { } module.exports = { + isZipBomb, scanWithClamAV, postToBusAPI } diff --git a/src/services/ProcessorService.js b/src/services/ProcessorService.js index 9c85564..32a2d5c 100644 --- a/src/services/ProcessorService.js +++ b/src/services/ProcessorService.js @@ -11,11 +11,23 @@ const helper = require('../common/helper') * @param {Object} message the message */ function * processScan (message) { - // Scan the file using ClamAV - const isInfected = yield helper.scanWithClamAV(message.payload.url) - // Update Scanning results message.timestamp = (new Date()).toISOString() message.payload.status = 'scanned' + + const downloadedFile = yield helper.downloadFile(message.payload.url); + + // Scan the file using ClamAV + const [isZipBomb, errorCode, errorMessage] = helper.isZipBomb(downloadedFile); + if (isZipBomb) { + message.isInfected = true; + logger.warn(`File at ${message.payload.url} is a ZipBomb. ${errorCode}: ${errorMessage}`); + yield helper.postToBusAPI(message) + return message; + } + + const isInfected = yield helper.scanWithClamAV(downloadedFile) + + // Update Scanning results message.payload.isInfected = isInfected yield helper.postToBusAPI(message)