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
const clam = require('clamscan')();
const crypto = require('crypto');
const freshclam = require('./lib/freshclam.js');
const debug = require('debug')('scanner:do');
const fs = require('fs');
const path = require('path');
const S3 = require('./lib/S3.js');
const SQS = require('./lib/SQS.js');
const scanner = require('./lib/scanner.js');
// Check for required environment variables
for (let env of [
'AWS_ACCESSKEY',
'AWS_REGION',
'AWS_SECRETKEY',
'AWS_SQSURL'
]) {
......@@ -19,6 +18,11 @@ for (let env of [
}
}
// Create _temp folder
if (!fs.existsSync('_temp')) {
fs.mkdirSync('_temp');
}
// TODO: loop freshclam
/**
......@@ -27,6 +31,7 @@ for (let env of [
*/
function handleError (error) {
// TODO: error handling
console.error(error);
}
/**
......@@ -38,13 +43,15 @@ function pollSQS () {
QueueUrl: process.env['AWS_SQSURL'],
WaitTimeSeconds: 20
}, function (err, data) {
if (err) return handleError(error);
if (err) return handleError(err);
debug(`received data from SQS, ${(data.Messages || []).length} records`);
let promises = [];
(data.Records || []).forEach(msg => {
(data.Messages || []).forEach(msg => {
promises.push(
Promise.resolve(msg)
.then(msg => JSON.parse(msg.Body))
.then(body => { msg: msg, msgBody: body })
.then(body => { return { msg: msg, msgBody: body }; })
.then(validateMessageBody)
.then(getObject)
.then(writeTempFile)
.then(clamScan)
......@@ -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.
* @param {Object} params
* @return {Promise<Object, Error>}
*/
function getObject (data) {
return new Promise((resolve, reject) => {
S3.getObject({
Bucket: body.s3.bucket.name,
Key: body.s3.object.Key
Bucket: data.msgBody.Records[0].s3.bucket.name,
Key: data.msgBody.Records[0].s3.object.key
}, (err, res) => {
if (err) return reject(err);
data.Body = new Buffer(res.Body);
......@@ -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.
*/
function writeTempFile (data) {
return new Promise((resolve, reject) => {
// 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
fs.writeFile(filepath, data.Body, err => {
......@@ -125,7 +156,7 @@ function clamScan (data) {
*/
function unlinkTempFile (data) {
return new Promise((resolve, reject) => {
fs.unlink(filepath, (err) => {
fs.unlink(data.filepath, (err) => {
if (err) return reject(err);
resolve(data);
});
......
......@@ -5,5 +5,6 @@ const AWS = require('aws-sdk');
module.exports = new AWS.SQS({
apiVersion: '2012-11-05',
accessKeyId: process.env['AWS_ACCESSKEY'],
region: process.env['AWS_REGION'],
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