Skip to content

Commit

Permalink
docs: add comments with relevant references to tix
Browse files Browse the repository at this point in the history
  • Loading branch information
LinHuiqing committed Sep 20, 2023
1 parent 69ce359 commit c9ab671
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 50 deletions.
5 changes: 4 additions & 1 deletion src/app/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,12 @@ const s3 = new aws.S3({
s3ForcePathStyle: isDev ? true : undefined,
})

// using aws-sdk v3
// using aws-sdk v3 (FRM-993)
const virusScannerLambda = new Lambda({
region: basicVars.awsConfig.region,
// Endpoint is set for development mode to point to the separate docker container running the lambda function.
// host.docker.internal is a special DNS name which resolves to the internal IP address used by the host.
// Reference: https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host
...(isDev ? { endpoint: 'http://host.docker.internal:9999' } : undefined),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ export const handleStorageSubmission = [
EncryptSubmissionMiddleware.validateStorageSubmissionParams,
EncryptSubmissionMiddleware.createFormsgAndRetrieveForm,
EncryptSubmissionMiddleware.checkNewBoundaryEnabled,
EncryptSubmissionMiddleware.scanAttachments,
EncryptSubmissionMiddleware.scanAndRetrieveAttachments,
EncryptSubmissionMiddleware.validateStorageSubmission,
EncryptSubmissionMiddleware.encryptSubmission,
submitEncryptModeForm,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { celebrate, Joi, Segments } from 'celebrate'
import { NextFunction } from 'express'
import { StatusCodes } from 'http-status-codes'
import { chain, omit } from 'lodash'
import { errAsync, okAsync, ResultAsync } from 'neverthrow'
import { okAsync, ResultAsync } from 'neverthrow'

import { featureFlags } from '../../../../../shared/constants'
import {
Expand Down Expand Up @@ -184,10 +184,9 @@ export const checkNewBoundaryEnabled = async (
}

/**
* Guardrail to prevent virus scanner code from being run if not enabled on frontend.
* TODO (FRM-1232): remove this guardrail when encryption boundary is shifted.
* Scan attachments on quarantine bucket and retrieve attachments from the clean bucket.
*/
export const scanAttachments = async (
export const scanAndRetrieveAttachments = async (
req: StorageSubmissionMiddlewareHandlerRequest,
res: Parameters<StorageSubmissionMiddlewareHandlerType>[1],
next: NextFunction,
Expand All @@ -197,6 +196,7 @@ export const scanAttachments = async (
...createReqMeta(req),
}

// TODO (FRM-1413): remove this guardrail when virus scanning has completed rollout.
// Step 1: If virus scanner is not enabled, skip this middleware.

const virusScannerEnabled = req.formsg.featureFlags.includes(
Expand All @@ -212,6 +212,7 @@ export const scanAttachments = async (
return next()
}

// TODO (FRM-1413): remove this guardrail when virus scanning has completed rollout.
// Step 2: If virus scanner is enabled, check if storage submission v2.1+. Storage submission v2.1 onwards
// should have virus scanning enabled. If not, skip this middleware.
// Note: Version number is sent by the frontend and should only be >=2.1 if virus scanning is enabled on the frontend.
Expand All @@ -225,43 +226,7 @@ export const scanAttachments = async (
const triggerLambdaResult = await ResultAsync.combine(
req.body.responses.map((response) => {
if (isQuarantinedAttachmentResponse(response)) {
return triggerVirusScanning(response.filename).andThen((data) => {
const { statusCode, body } = JSON.parse(
Buffer.from(data?.Payload ?? '').toString(),
) as {
statusCode: number
body: string
}
const parsedBody = JSON.parse(body)
const returnPayload = {
statusCode,
body: parsedBody,
}
logger.info({
message: 'Successfully invoked lambda function',
meta: {
...logMeta,
responseMetadata: data?.$metadata,
returnPayload,
},
})

// If return payload is not OK, either file cannot be found or virus scan failed. This should be
// treated as a fatal error we should investigate.
if (statusCode !== StatusCodes.OK) {
logger.warn({
message: 'Some lambda invocations failed',
meta: {
...logMeta,
responseMetadata: data?.$metadata,
returnPayload,
},
})
return errAsync(returnPayload)
}

return okAsync(returnPayload)
})
return triggerVirusScanning(response.filename)
}
// If response is not an attachment, return ok.
return okAsync(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ManagedUpload } from 'aws-sdk/clients/s3'
import Bluebird from 'bluebird'
import crypto from 'crypto'
import { StatusCodes } from 'http-status-codes'
import moment from 'moment'
import mongoose from 'mongoose'
import { err, errAsync, ok, okAsync, Result, ResultAsync } from 'neverthrow'
Expand Down Expand Up @@ -580,7 +581,19 @@ export const getQuarantinePresignedPostData = (
)
}

/**
* Invokes lambda to scan the file in the quarantine bucket for viruses.
* @param quarantineFileKey object key of the file in the quarantine bucket
* @returns okAsync(returnPayload) if file has been successfully scanned with status 200 OK
* @returns errAsync(returnPayload) if lambda invocation failed or file cannot be found
*/
export const triggerVirusScanning = (quarantineFileKey: string) => {
const logMeta = {
action: 'triggerVirusScanning',
meta: {
quarantineFileKey,
},
}
return ResultAsync.fromPromise(
AwsConfig.virusScannerLambda.invoke({
FunctionName: AwsConfig.virusScannerLambdaFunctionName,
Expand All @@ -589,13 +602,48 @@ export const triggerVirusScanning = (quarantineFileKey: string) => {
(error) => {
logger.error({
message: 'Error encountered when invoking virus scanning lambda',
meta: {
action: 'triggerVirusScanning',
},
meta: logMeta,
error,
})

return new VirusScanFailedError()
},
)
).andThen((data) => {
const { statusCode, body } = JSON.parse(
Buffer.from(data?.Payload ?? '').toString(),
) as {
statusCode: number
body: string
}
const parsedBody = JSON.parse(body)
const returnPayload = {
statusCode,
body: parsedBody,
}
// Note: returnPayload.statusCode and data?.$metadata.statusCode are different.
logger.info({
message: 'Successfully invoked lambda function',
meta: {
...logMeta,
responseMetadata: data?.$metadata,
returnPayload,
},
})

// If return payload is not OK, either file cannot be found or virus scan failed. This should be
// treated as a fatal error and we should investigate.
if (statusCode !== StatusCodes.OK) {
logger.warn({
message: 'Some lambda invocations failed',
meta: {
...logMeta,
responseMetadata: data?.$metadata,
returnPayload,
},
})
return errAsync(returnPayload)
}

return okAsync(returnPayload)
})
}
3 changes: 1 addition & 2 deletions src/app/modules/submission/submission.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ export const sharedSubmissionParams = {
.valid(...Object.values(BasicField)),
answer: Joi.string().allow(''),
answerArray: Joi.array(),
filename: Joi.string(),
filename: Joi.string(), // filename or quarantine bucket object key where file has been uploaded to
content: Joi.binary(),
fileKey: Joi.string(), // quarantine bucket object key where file has been uploaded to
isHeader: Joi.boolean(),
myInfo: Joi.object(),
signature: Joi.string().allow(''),
Expand Down
2 changes: 1 addition & 1 deletion src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type AwsConfig = {
virusScannerCleanS3Bucket: string
s3: aws.S3
endPoint: string
virusScannerLambda: Lambda // using aws-sdk-v3
virusScannerLambda: Lambda // using aws-sdk-v3 (FRM-993)
virusScannerLambdaFunctionName: string
}

Expand Down

0 comments on commit c9ab671

Please sign in to comment.