Skip to content

Commit 3f95f26

Browse files
Merge pull request #753 from topcoder-platform/feature/plat-3292
use the new file scanner service to av scan attachments
2 parents fe1fe2b + c3b4b8f commit 3f95f26

File tree

5 files changed

+108
-2
lines changed

5 files changed

+108
-2
lines changed

config/default.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"directProjectServiceEndpoint": "",
1515
"directProjectServiceTimeout": 5000,
1616
"attachmentsS3Bucket": "topcoder-prod-media",
17+
"attachmentsDMZS3Bucket": "topcoder-prod-media-dmz",
18+
"attachmentsQuarantineS3Bucket": "topcoder-prod-media-quarantine",
1719
"projectAttachmentPathPrefix": "projects",
1820
"projectAttachmentPathSuffix": "attachments",
1921
"elasticsearchConfig": {
@@ -87,4 +89,4 @@
8789
},
8890
"STRIPE_SECRET_KEY": "",
8991
"sfdcBillingAccountNameField": "Billing_Account_Name__c"
90-
}
92+
}

config/development.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
"pubsubQueueName": "dev.project.service",
33
"pubsubExchangeName": "dev.projects",
44
"attachmentsS3Bucket": "topcoder-dev-media",
5+
"attachmentsDMZS3Bucket": "topcoder-dev-media-dmz",
6+
"attachmentsQuarantineS3Bucket": "topcoder-dev-media-quarantine",
57
"connectProjectsUrl": "https://connect.topcoder-dev.com/projects/",
68
"fileServiceEndpoint": "https://api.topcoder-dev.com/v3/files/",
79
"connectProjectsUrl": "https://connect.topcoder-dev.com/projects/",

src/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ export const EVENT = {
139139
};
140140

141141
export const BUS_API_EVENT = {
142+
AV_SCAN_REQUEST: 'avscan.action.scan',
143+
142144
PROJECT_CREATED: 'project.action.create',
143145
PROJECT_UPDATED: 'project.action.update',
144146
PROJECT_DELETED: 'project.action.delete',
@@ -151,6 +153,7 @@ export const BUS_API_EVENT = {
151153
PROJECT_ATTACHMENT_ADDED: 'project.action.create',
152154
PROJECT_ATTACHMENT_REMOVED: 'project.action.delete',
153155
PROJECT_ATTACHMENT_UPDATED: 'project.action.update',
156+
PROJECT_ATTACHMENT_SCAN_RESULT: 'avscan.projects.assets.result',
154157

155158
// When phase is added/updated/deleted from the project,
156159
// When product is added/deleted from a phase

src/events/attachments/index.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Event handlers for attachment create, and av scan result
3+
*/
4+
import _ from 'lodash';
5+
import Joi from 'joi';
6+
import Promise from 'bluebird';
7+
import config from 'config';
8+
import util from '../../util';
9+
import { BUS_API_EVENT } from '../../constants';
10+
import { createEvent } from '../../services/busApi';
11+
12+
/**
13+
* Payload for new unified BUS events like `avscan.projects.assets.result` with `resource=attachment`
14+
*/
15+
const attachmentScanResultPayloadSchema = Joi.object().keys({
16+
url: Joi.string().required(),
17+
fileName: Joi.string().required(),
18+
isInfected: Joi.boolean().required(),
19+
}).unknown(true).required();
20+
21+
/**
22+
* Updates project activity fields. throws exceptions in case of error
23+
* @param {Object} app Application object used to interact with RMQ service
24+
* @param {String} topic Kafka topic
25+
* @param {Object} payload Message payload
26+
* @return {Promise} Promise
27+
*/
28+
async function attachmentScanResultKafkaHandler(app, topic, payload) {
29+
// Validate payload
30+
const result = Joi.validate(payload, attachmentScanResultPayloadSchema);
31+
if (result.error) {
32+
throw new Error(result.error);
33+
}
34+
const sourceBucket = config.get('attachmentsDMZS3Bucket');
35+
// if the attachment is infected, move it to the quarantine s3 bucket, if not move it to the clean s3 bucket
36+
if (payload.isInfected) {
37+
// move to quarantine
38+
const destBucket = config.get('attachmentsQuarantineS3Bucket');
39+
util.s3FileTransfer({ log: app.logger }, sourceBucket, payload.path, destBucket, payload.path)
40+
app.logger.debug(`Attachment ${payload.fileName} is infected, moving to quarantine`);
41+
} else {
42+
// move to clean
43+
const destBucket = config.get('attachmentsS3Bucket');
44+
util.s3FileTransfer({ log: app.logger }, sourceBucket, payload.path, destBucket, payload.path)
45+
}
46+
}
47+
48+
/**
49+
* Payload for new unified BUS events like `avscan.action.scan` with `resource=attachment`
50+
*/
51+
const attachmentPayloadSchema = Joi.object().keys({
52+
path: Joi.string().required(),
53+
}).unknown(true).required();
54+
55+
/**
56+
* Attachment Created BUS API event handler.
57+
* - requests av scan by posting a kafka message to `avscan.action.scan` topic
58+
* - throws exceptions in case of error
59+
*
60+
* @param {Object} app Application object
61+
* @param {String} topic Kafka topic
62+
* @param {Object} payload Message payload
63+
* @return {Promise} Promise
64+
*/
65+
async function attachmentCreatedKafkaHandler(app, topic, payload) {
66+
// Validate payload
67+
const result = Joi.validate(payload, attachmentPayloadSchema);
68+
if (result.error) {
69+
throw new Error(result.error);
70+
}
71+
const avScanPayload = {
72+
url: payload.path,
73+
fileName: payload.path.split('/').pop(),
74+
moveFile: false,
75+
callbackOption: 'kafka',
76+
callbackKafkaTopic: BUS_API_EVENT.PROJECT_ATTACHMENT_SCAN_RESULT,
77+
};
78+
await createEvent(
79+
BUS_API_EVENT.AV_SCAN_REQUEST,
80+
avScanPayload,
81+
app.logger,
82+
)
83+
}
84+
85+
module.exports = {
86+
attachmentScanResultKafkaHandler,
87+
attachmentCreatedKafkaHandler,
88+
};

src/events/kafkaHandlers.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import {
1717
} from './projectPhases';
1818
import { timelineAdjustedKafkaHandler } from './timelines';
1919
import { milestoneUpdatedKafkaHandler } from './milestones';
20+
import {
21+
attachmentScanResultKafkaHandler,
22+
attachmentCreatedKafkaHandler,
23+
} from './attachments'
2024

2125
const kafkaHandlers = {
2226
/**
@@ -37,6 +41,9 @@ const kafkaHandlers = {
3741
// Events coming from timeline/milestones (considering it as a separate module/service in future)
3842
[CONNECT_NOTIFICATION_EVENT.MILESTONE_TRANSITION_COMPLETED]: milestoneUpdatedKafkaHandler,
3943
[CONNECT_NOTIFICATION_EVENT.TIMELINE_ADJUSTED]: timelineAdjustedKafkaHandler,
44+
45+
// Events coming from attachments
46+
[BUS_API_EVENT.PROJECT_ATTACHMENT_SCAN_RESULT]: attachmentScanResultKafkaHandler,
4047
};
4148

4249
/**
@@ -95,6 +102,10 @@ registerKafkaHandler(
95102
RESOURCES.PHASE,
96103
projectPhaseRemovedKafkaHandler,
97104
);
98-
105+
registerKafkaHandler(
106+
BUS_API_EVENT.PROJECT_ATTACHMENT_ADDED,
107+
RESOURCES.ATTACHMENT,
108+
attachmentCreatedKafkaHandler,
109+
);
99110

100111
export default kafkaHandlers;

0 commit comments

Comments
 (0)