diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts index 1e5c77bdc2..2de0e3b384 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts @@ -85,6 +85,7 @@ import { getPaymentAmount, getPaymentIntentDescription, mapRouteError, + sanitisePaymentProducts, } from './encrypt-submission.utils' const logger = createLoggerWithLabel(module) @@ -138,8 +139,11 @@ const submitEncryptModeForm = async ( const encryptedPayload = req.formsg.encryptedPayload // Create Incoming Submission - const { encryptedContent, responseMetadata, paymentProducts } = - encryptedPayload + const { + encryptedContent, + responseMetadata, + paymentProducts: _clientPaymentsProducts, + } = encryptedPayload // Checks if user is SPCP-authenticated before allowing submission let uinFin @@ -299,6 +303,10 @@ const submitEncryptModeForm = async ( form.payments_field?.enabled && form.payments_channel.channel === PaymentChannel.Stripe ) { + const paymentProducts = sanitisePaymentProducts( + form, + _clientPaymentsProducts, + ) return _createPaymentSubmission({ req, res, diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts index 631d04e0d9..42c26e3ce9 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts @@ -1,10 +1,12 @@ import { StatusCodes } from 'http-status-codes' +import _ from 'lodash' import moment from 'moment-timezone' import { FormPaymentsField, PaymentFieldsDto, PaymentType, + ProductItem, StorageModeSubmissionContentDto, StorageModeSubmissionDto, SubmissionPaymentDto, @@ -359,3 +361,38 @@ export const getPaymentIntentDescription = ( } } } + +const isNonEmpty = (value: T | null | undefined): value is T => { + return value != null +} + +/** + * Sanitizes the payment fields from the form and the incoming submission + * The payment products from incoming submission can be freely altered by the respondent + * which could result in undesirable data seeded into our database + * @param form + * @param uncleanPaymentProducts + */ +export const sanitisePaymentProducts = ( + form: IPopulatedEncryptedForm, + dirtyPaymentProducts: ProductItem[] | undefined, +): ProductItem[] | undefined => { + if (!dirtyPaymentProducts) return dirtyPaymentProducts + if (!form.payments_field.products) return dirtyPaymentProducts + + const sanitisedProducts = form.payments_field.products + .map((cleanProductData): ProductItem | null => { + const dirtyProduct = dirtyPaymentProducts.find( + ({ data }) => data._id === cleanProductData._id, + ) + if (!dirtyProduct) return null + + return { + ..._.pick(dirtyProduct, ['selected', 'quantity']), // only selected and quantity are allowed to be passed through + data: cleanProductData, // only clean product data from the form should be used + } + }) + .filter(isNonEmpty) + + return sanitisedProducts +}