Skip to content

Commit

Permalink
feat: test lambda invoke v3
Browse files Browse the repository at this point in the history
  • Loading branch information
LinHuiqing committed Sep 18, 2023
1 parent 89ea568 commit e60f0ff
Show file tree
Hide file tree
Showing 10 changed files with 3,199 additions and 1 deletion.
3,086 changes: 3,086 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.347.1",
"@aws-sdk/client-lambda": "^3.414.0",
"@babel/runtime": "^7.20.13",
"@faker-js/faker": "^8.0.1",
"@joi/date": "^2.1.0",
Expand Down
5 changes: 5 additions & 0 deletions src/app/config/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Lambda } from '@aws-sdk/client-lambda'
import aws from 'aws-sdk'
import convict from 'convict'
import { SessionOptions } from 'express-session'
Expand Down Expand Up @@ -89,10 +90,14 @@ const s3 = new aws.S3({
s3ForcePathStyle: isDev ? true : undefined,
})

// using aws-sdk v3
const virusScannerLambda = new Lambda({ region: basicVars.awsConfig.region })

const awsConfig: AwsConfig = {
...s3BucketUrlVars,
...basicVars.awsConfig,
s3,
virusScannerLambda,
}

let dbUri: string | undefined
Expand Down
6 changes: 6 additions & 0 deletions src/app/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ export const optionalVarsSchema: Schema<IOptionalVarsSchema> = {
default: '',
env: 'CUSTOM_CLOUDWATCH_LOG_GROUP',
},
virusScannerLambdaFunctionName: {
doc: 'Virus scanner lambda function name',
format: String,
default: '',
env: 'VIRUS_SCANNER_LAMBDA_FUNCTION_NAME',
},
},
core: {
port: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ export const handleStorageSubmission = [
EncryptSubmissionMiddleware.validateStorageSubmissionParams,
EncryptSubmissionMiddleware.createFormsgAndRetrieveForm,
EncryptSubmissionMiddleware.checkNewBoundaryEnabled,
EncryptSubmissionMiddleware.checkAttachmentQuarantineKeys,
EncryptSubmissionMiddleware.validateStorageSubmission,
EncryptSubmissionMiddleware.encryptSubmission,
submitEncryptModeForm,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import {
EncryptedPayloadExistsError,
FormsgReqBodyExistsError,
} from './encrypt-submission.errors'
import { checkFormIsEncryptMode } from './encrypt-submission.service'
import {
checkFormIsEncryptMode,
triggerVirusScanning,
} from './encrypt-submission.service'
import {
CreateFormsgAndRetrieveFormMiddlewareHandlerRequest,
CreateFormsgAndRetrieveFormMiddlewareHandlerType,
Expand Down Expand Up @@ -176,6 +179,58 @@ export const checkNewBoundaryEnabled = async (
return next()
}

/**
* 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.
*/
export const checkAttachmentQuarantineKeys = async (
req: StorageSubmissionMiddlewareHandlerRequest,
res: Parameters<StorageSubmissionMiddlewareHandlerType>[1],
next: NextFunction,
) => {
const logMeta = {
action: 'checkAttachmentQuarantineKeys',
...createReqMeta(req),
}

// Step 1: If virus scanner is not enabled, skip this middleware.

const virusScannerEnabled = req.formsg.featureFlags.includes(
featureFlags.encryptionBoundaryShiftVirusScanner,
)

if (!virusScannerEnabled) {
logger.warn({
message: 'Virus scanner is not enabled.',
meta: logMeta,
})

return next()
}

// // Step 2: If virus scanner is enabled, check if quarantine keys for attachments are present. Quarantine keys
// // should only be present if the virus scanner is enabled on the frontend.
// // If not, skip this middleware.

// let quarantineKeysPresent = false

// for (const response of req.body.responses) {
// if (isQuarantinedAttachmentResponse(response)) quarantineKeysPresent = true
// }

// if (!quarantineKeysPresent) return next()

// At this point, virus scanner is enabled and quarantine keys are present. This means that both the FE and BE
// have virus scanning enabled.

// Step 3: Trigger lambda to scan attachments.
triggerVirusScanning('16ca3303-743a-4cec-ab20-3d10042d88ce')

// Step 4: Retrieve attachments from the clean bucket.

return next()
}

/**
* Validates storage submissions to the new endpoint (/api/v3/forms/:formId/submissions/storage).
* This uses the same validators as email mode submissions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,3 +578,30 @@ export const getQuarantinePresignedPostData = (
}),
)
}

export const triggerVirusScanning = (quarantineFileKey: string) => {
return AwsConfig.virusScannerLambda.invoke(
{
FunctionName: AwsConfig.virusScannerLambdaFunctionName,
Payload: JSON.stringify({ key: quarantineFileKey }),
},
(error, data) => {
if (error) {
logger.error({
message: 'Error invoking lambda function',
meta: {
action: 'triggerVirusScanning',
},
error,
})
}
logger.info({
message: 'Successfully invoked lambda function',
meta: {
action: 'triggerVirusScanning',
data,
},
})
},
)
}
12 changes: 12 additions & 0 deletions src/app/modules/submission/submission.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,18 @@ export const isAttachmentResponse = (
)
}

/**
* Checks if a response is a quarantined attachment response to be processed by the virus scanner.
*/
export const isQuarantinedAttachmentResponse = (
response: ParsedClearFormFieldResponse,
): response is ParsedClearAttachmentResponse => {
return (
response.fieldType === BasicField.Attachment &&
response.fileKey !== undefined
)
}

/**
* Checks an array of attachments to see ensure that every
* one of them is valid. The validity is determined by an
Expand Down
1 change: 1 addition & 0 deletions src/types/api/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AttachmentResponse, FieldResponse } from '../../../shared/types'
export type ParsedClearAttachmentResponse = AttachmentResponse & {
filename: string
content: Buffer
fileKey?: string
}

export type ParsedClearFormFieldResponse =
Expand Down
4 changes: 4 additions & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Lambda } from '@aws-sdk/client-lambda'
import { PackageMode } from '@opengovsg/formsg-sdk/dist/types'
import aws from 'aws-sdk'
import { SessionOptions } from 'express-session'
Expand Down Expand Up @@ -39,6 +40,8 @@ export type AwsConfig = {
virusScannerCleanS3Bucket: string
s3: aws.S3
endPoint: string
virusScannerLambda: Lambda // using aws-sdk-v3
virusScannerLambdaFunctionName: string
}

export type MailConfig = {
Expand Down Expand Up @@ -159,6 +162,7 @@ export interface IOptionalVarsSchema {
awsConfig: {
region: string
customCloudWatchGroup: string
virusScannerLambdaFunctionName: string
}
mail: {
from: string
Expand Down

0 comments on commit e60f0ff

Please sign in to comment.