Skip to content
Snippets Groups Projects
Commit 04650b9b authored by Dean's avatar Dean
Browse files

small fixes + Dockerfile

parent 504a0e80
No related branches found
No related tags found
No related merge requests found
FROM buildpack-deps:jessie
MAINTAINER Dean Sheather <dean@deansheather.com>
# add Node.js repository to apt
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
# install Node.js and ClamAV
RUN apt-get update \
&& apt-get install -y -qq --force-yes nodejs \
clamav \
clamav-freshclam \
ca-certificates
# make the container smaller
RUN apt-get clean
RUN rm -rf /var/lib/apt
# update virus database using freshclam
RUN freshclam
# copy source files into container
COPY index.js src/
COPY package.json src/
COPY lib/ src/lib
WORKDIR src/
# update NPM dependencies
RUN npm install
# start the consumer
CMD ["node", "index.js"]
// Required modules // Required modules
const clam = require('clamscan')(); const clam = require('clamscan')();
const crypto = require('crypto'); const debug = require('debug')('scanner:do');
const freshclam = require('./lib/freshclam.js');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const S3 = require('./lib/S3.js'); const S3 = require('./lib/S3.js');
const SQS = require('./lib/SQS.js'); const SQS = require('./lib/SQS.js');
const scanner = require('./lib/scanner.js');
// Check for required environment variables // Check for required environment variables
for (let env of [ for (let env of [
'AWS_ACCESSKEY', 'AWS_ACCESSKEY',
'AWS_REGION',
'AWS_SECRETKEY', 'AWS_SECRETKEY',
'AWS_SQSURL' 'AWS_SQSURL'
]) { ]) {
...@@ -19,6 +18,11 @@ for (let env of [ ...@@ -19,6 +18,11 @@ for (let env of [
} }
} }
// Create _temp folder
if (!fs.existsSync('_temp')) {
fs.mkdirSync('_temp');
}
// TODO: loop freshclam // TODO: loop freshclam
/** /**
...@@ -27,6 +31,7 @@ for (let env of [ ...@@ -27,6 +31,7 @@ for (let env of [
*/ */
function handleError (error) { function handleError (error) {
// TODO: error handling // TODO: error handling
console.error(error);
} }
/** /**
...@@ -38,13 +43,15 @@ function pollSQS () { ...@@ -38,13 +43,15 @@ function pollSQS () {
QueueUrl: process.env['AWS_SQSURL'], QueueUrl: process.env['AWS_SQSURL'],
WaitTimeSeconds: 20 WaitTimeSeconds: 20
}, function (err, data) { }, function (err, data) {
if (err) return handleError(error); if (err) return handleError(err);
debug(`received data from SQS, ${(data.Messages || []).length} records`);
let promises = []; let promises = [];
(data.Records || []).forEach(msg => { (data.Messages || []).forEach(msg => {
promises.push( promises.push(
Promise.resolve(msg) Promise.resolve(msg)
.then(msg => JSON.parse(msg.Body)) .then(msg => JSON.parse(msg.Body))
.then(body => { msg: msg, msgBody: body }) .then(body => { return { msg: msg, msgBody: body }; })
.then(validateMessageBody)
.then(getObject) .then(getObject)
.then(writeTempFile) .then(writeTempFile)
.then(clamScan) .then(clamScan)
...@@ -63,16 +70,39 @@ function pollSQS () { ...@@ -63,16 +70,39 @@ function pollSQS () {
}); });
} }
// Start the loop
pollSQS();
/**
* Validate incoming SQS message body.
*/
function validateMessageBody (data) {
// eslint-disable-next-line promise/param-names
return new Promise((resolve, _reject) => {
function reject (err) {
deleteSQSMessage(data)
.then(() => _reject(err))
.catch(e => _reject([err, e]));
}
if (data.msgBody.Event === 's3:TestEvent') return reject(new Error('test event'));
if (!Array.isArray(data.msgBody.Records)) return reject(new Error('invalid S3 message structure'));
if (data.msgBody.Records.length !== 1) return reject(new Error('records count !== 1'));
for (const item of data.msgBody.Records) {
if (typeof item !== 'object' || item === null) return reject('invalid item in records');
if (typeof item.eventName !== 'string' || item.eventName.indexOf('ObjectCreated') === -1) return reject('invalid event type on record');
}
resolve(data);
});
}
/** /**
* Get object from S3, promisified. * Get object from S3, promisified.
* @param {Object} params
* @return {Promise<Object, Error>}
*/ */
function getObject (data) { function getObject (data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
S3.getObject({ S3.getObject({
Bucket: body.s3.bucket.name, Bucket: data.msgBody.Records[0].s3.bucket.name,
Key: body.s3.object.Key Key: data.msgBody.Records[0].s3.object.key
}, (err, res) => { }, (err, res) => {
if (err) return reject(err); if (err) return reject(err);
data.Body = new Buffer(res.Body); data.Body = new Buffer(res.Body);
...@@ -81,22 +111,23 @@ function getObject (data) { ...@@ -81,22 +111,23 @@ function getObject (data) {
}); });
} }
/**
* Generate random key.
* @return {string} 6 character key.
*/
function generateRandomKey () {
const seed = String(Math.floor(Math.random() * 10) + Date.now());
return crypto.createHash('md5').update(seed).digest('hex').substr(2, 6);
}
/** /**
* Create a temporary file on disk for scanning. * Create a temporary file on disk for scanning.
*/ */
function writeTempFile (data) { function writeTempFile (data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Construct the filepath (including random key) // Construct the filepath (including random key)
const filepath = path.join('.', '_temp', data.Bucket, generateRandomKey() + data.Key.replace(/[^a-z0-9_.-]/gi, '_')); const filepath = path.join(
'.',
'_temp',
data.msgBody.Records[0].s3.bucket.name,
data.msg.MessageId + data.msgBody.Records[0].s3.object.key.replace(/[^a-z0-9_.-]/gi, '_')
);
// Create bucket folder
if (!fs.existsSync('_temp/' + data.msgBody.Records[0].s3.bucket.name)) {
fs.mkdirSync('_temp/' + data.msgBody.Records[0].s3.bucket.name);
}
// Write the file // Write the file
fs.writeFile(filepath, data.Body, err => { fs.writeFile(filepath, data.Body, err => {
...@@ -125,7 +156,7 @@ function clamScan (data) { ...@@ -125,7 +156,7 @@ function clamScan (data) {
*/ */
function unlinkTempFile (data) { function unlinkTempFile (data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.unlink(filepath, (err) => { fs.unlink(data.filepath, (err) => {
if (err) return reject(err); if (err) return reject(err);
resolve(data); resolve(data);
}); });
......
...@@ -5,5 +5,6 @@ const AWS = require('aws-sdk'); ...@@ -5,5 +5,6 @@ const AWS = require('aws-sdk');
module.exports = new AWS.SQS({ module.exports = new AWS.SQS({
apiVersion: '2012-11-05', apiVersion: '2012-11-05',
accessKeyId: process.env['AWS_ACCESSKEY'], accessKeyId: process.env['AWS_ACCESSKEY'],
region: process.env['AWS_REGION'],
secretAccessKey: process.env['AWS_SECRETKEY'] secretAccessKey: process.env['AWS_SECRETKEY']
}); });
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment