diff --git a/CHANGELOG.md b/CHANGELOG.md index d66fc5f5c1..653e7e1cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v6.89.1](https://github.com/opengovsg/FormSG/compare/v6.89.0...v6.89.1) +#### [v6.89.2](https://github.com/opengovsg/FormSG/compare/v6.89.0...v6.89.2) +- fix: hotfix v6.89.1 for proper error handling in encrypt-submission middleware [`#6903`](https://github.com/opengovsg/FormSG/pull/6903) - build: release v6.89.0 [`#6898`](https://github.com/opengovsg/FormSG/pull/6898) - fix: remove myinfo child from storage mode [`#6901`](https://github.com/opengovsg/FormSG/pull/6901) -- fix: add error handling [`d6c4985`](https://github.com/opengovsg/FormSG/commit/d6c4985aa8e35dd2278af9b70d00d4e86a48bde1) -- fix: remove email mode from myinfo limit message [`5a45c98`](https://github.com/opengovsg/FormSG/commit/5a45c980dbe3fc8c15eacb3ff1827f3003fcbfc4) +- fix: add learn more link [`#6897`](https://github.com/opengovsg/FormSG/pull/6897) +- chore: security upgrade axios to 1.6.2 [`#6893`](https://github.com/opengovsg/FormSG/pull/6893) +- chore: revert commit 6869 [`efab3cf`](https://github.com/opengovsg/FormSG/commit/efab3cf844113573d758b1b2c57147d5d0656a28) +- chore: bump version to v6.89.0 [`1665a48`](https://github.com/opengovsg/FormSG/commit/1665a48b0ff133378c7126d3fedf8f1145f67111) +- fix: remove error block [`fb415fc`](https://github.com/opengovsg/FormSG/commit/fb415fcd7189a90056557c242ed98f1dc10e757e) #### [v6.89.0](https://github.com/opengovsg/FormSG/compare/v6.88.0...v6.89.0) > 15 November 2023 -- fix: add learn more link [`#6897`](https://github.com/opengovsg/FormSG/pull/6897) -- chore: security upgrade axios to 1.6.2 [`#6893`](https://github.com/opengovsg/FormSG/pull/6893) - feat: myinfo for storage-mode [`#6870`](https://github.com/opengovsg/FormSG/pull/6870) - feat: announcement modal and what's new for myinfo storage-mode [`#6892`](https://github.com/opengovsg/FormSG/pull/6892) - chore: remove eb shift frontend feature flags [`#6869`](https://github.com/opengovsg/FormSG/pull/6869) @@ -26,7 +28,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - fix(deps): bump type-fest from 4.5.0 to 4.7.1 in /shared [`#6883`](https://github.com/opengovsg/FormSG/pull/6883) - build: merge release 6.88.0 into develop [`#6882`](https://github.com/opengovsg/FormSG/pull/6882) - build: release v6.88.0 [`#6881`](https://github.com/opengovsg/FormSG/pull/6881) -- chore: bump version to v6.89.0 [`1665a48`](https://github.com/opengovsg/FormSG/commit/1665a48b0ff133378c7126d3fedf8f1145f67111) +- chore: bump version to v6.89.0 [`3a15ceb`](https://github.com/opengovsg/FormSG/commit/3a15ceb52806aae4a0aaa91c0e2c49d4bcae8606) #### [v6.88.0](https://github.com/opengovsg/FormSG/compare/v6.87.0...v6.88.0) @@ -47,14 +49,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - fix: add case for SGID MyInfo when field value is missing [`#6874`](https://github.com/opengovsg/FormSG/pull/6874) - build: merge release v6.86.0 into develop [`#6873`](https://github.com/opengovsg/FormSG/pull/6873) - build: release v6.86.0 [`#6866`](https://github.com/opengovsg/FormSG/pull/6866) -- chore: use non-testing branch for font-wqy-zenhei [`#6867`](https://github.com/opengovsg/FormSG/pull/6867) -- chore: bump version to v6.86.0 [`1eec9b6`](https://github.com/opengovsg/FormSG/commit/1eec9b63c914b56b7b10adffd03554e07fde0f3a) - chore: bump version to v6.87.0 [`5054803`](https://github.com/opengovsg/FormSG/commit/50548038804b03f30ce6d23b4d43b7a8cf7d9620) #### [v6.86.0](https://github.com/opengovsg/FormSG/compare/v6.85.1...v6.86.0) > 6 November 2023 +- chore: use non-testing branch for font-wqy-zenhei [`#6867`](https://github.com/opengovsg/FormSG/pull/6867) - chore: update credits and terms of use [`#6865`](https://github.com/opengovsg/FormSG/pull/6865) - fix: add cloudflareinsights as allowable csp [`#6864`](https://github.com/opengovsg/FormSG/pull/6864) - feat: optimise submission query [`#6863`](https://github.com/opengovsg/FormSG/pull/6863) @@ -62,7 +63,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - fix: only render delete button if owner [`#6837`](https://github.com/opengovsg/FormSG/pull/6837) - build: merge release v6.85.1 into develop [`#6861`](https://github.com/opengovsg/FormSG/pull/6861) - fix: hotfix v6.85.1 to prevent creation of SGID_MyInfo storage mode forms [`#6860`](https://github.com/opengovsg/FormSG/pull/6860) -- chore: bump version to v6.86.0 [`1c827cd`](https://github.com/opengovsg/FormSG/commit/1c827cd11844649ca303bb5dc9987db5367637c9) +- chore: bump version to v6.86.0 [`1eec9b6`](https://github.com/opengovsg/FormSG/commit/1eec9b63c914b56b7b10adffd03554e07fde0f3a) #### [v6.85.1](https://github.com/opengovsg/FormSG/compare/v6.85.0...v6.85.1) diff --git a/frontend/src/features/public-form/PublicFormProvider.tsx b/frontend/src/features/public-form/PublicFormProvider.tsx index ecc8b777ff..fc3feeaffb 100644 --- a/frontend/src/features/public-form/PublicFormProvider.tsx +++ b/frontend/src/features/public-form/PublicFormProvider.tsx @@ -11,6 +11,11 @@ import { SubmitHandler } from 'react-hook-form' import { useNavigate } from 'react-router-dom' import { useDisclosure } from '@chakra-ui/react' import { datadogLogs } from '@datadog/browser-logs' +import { + useFeatureIsOn, + useFeatureValue, + useGrowthBook, +} from '@growthbook/growthbook-react' import { differenceInMilliseconds, isPast } from 'date-fns' import get from 'lodash/get' import simplur from 'simplur' @@ -130,6 +135,23 @@ export const PublicFormProvider = ({ /* enabled= */ !submissionData, ) + const growthbook = useGrowthBook() + + useEffect(() => { + if (growthbook) { + growthbook.setAttributes({ + // Only update the `formId` attribute, keep the rest the same + ...growthbook.getAttributes(), + formId, + }) + } + }, [growthbook, formId]) + + const enableEncryptionBoundaryShift = useFeatureValue( + featureFlags.encryptionBoundaryShift, + true, + ) + // Scroll to top of page when user has finished their submission. useLayoutEffect(() => { if (submissionData) { @@ -247,11 +269,18 @@ export const PublicFormProvider = ({ } }, [data?.form.form_fields, toast, vfnToastIdRef]) + const enableVirusScanner = useFeatureIsOn( + featureFlags.encryptionBoundaryShiftVirusScanner, + ) + const { submitEmailModeFormMutation, + submitStorageModeFormMutation, submitEmailModeFormFetchMutation, - submitStorageModeFormWithVirusScanningFetchMutation, - submitStorageModeFormWithVirusScanningMutation, + submitStorageModeFormFetchMutation, + submitStorageModeClearFormMutation, + submitStorageModeClearFormFetchMutation, + submitStorageModeClearFormWithVirusScanningMutation, } = usePublicFormMutations(formId, submissionData?.id ?? '') const { handleLogoutMutation } = usePublicAuthMutations(formId) @@ -455,7 +484,9 @@ export const PublicFormProvider = ({ : {}), } - const submitStorageFormWithFetch = function () { + const submitStorageFormWithFetch = function ( + routeToNewStorageModeSubmission: boolean, + ) { datadogLogs.logger.info(`handleSubmitForm: submitting via fetch`, { meta: { ...logMeta, @@ -464,11 +495,16 @@ export const PublicFormProvider = ({ }, }) - return submitStorageModeFormWithVirusScanningFetchMutation + return ( + routeToNewStorageModeSubmission + ? submitStorageModeClearFormFetchMutation + : submitStorageModeFormFetchMutation + ) .mutateAsync( { ...formData, ...formPaymentData, + publicKey: form.publicKey, }, { onSuccess: ({ @@ -510,7 +546,7 @@ export const PublicFormProvider = ({ // TODO (#5826): Toggle to use fetch for submissions instead of axios. If enabled, this is used for testing and to use fetch instead of axios by default if testing shows fetch is more stable. Remove once network error is resolved if (useFetchForSubmissions) { - return submitStorageFormWithFetch() + return submitStorageFormWithFetch(enableEncryptionBoundaryShift) } datadogLogs.logger.info(`handleSubmitForm: submitting via axios`, { meta: { @@ -520,8 +556,9 @@ export const PublicFormProvider = ({ }, }) - return submitStorageModeFormWithVirusScanningMutation - .mutateAsync( + // TODO (FRM-1413): Move to main return statement once virus scanner has been fully rolled out + if (enableEncryptionBoundaryShift && enableVirusScanner) { + return submitStorageModeClearFormWithVirusScanningMutation.mutateAsync( { ...formData, ...formPaymentData, @@ -545,27 +582,86 @@ export const PublicFormProvider = ({ timestamp, }) }, + onError: (error) => { + // TODO(#5826): Remove when we have resolved the Network Error + datadogLogs.logger.warn( + `handleSubmitForm: submit with virus scan`, + { + meta: { + ...logMeta, + responseMode: 'storage', + method: 'axios', + error, + }, + }, + ) + + // defaults to the safest option of storage submission without virus scanning + return submitStorageFormWithFetch( + enableEncryptionBoundaryShift, + ) + }, }, ) - .catch(async (error) => { - datadogLogs.logger.warn( - `handleSubmitForm: submit with virus scan`, + } + + return ( + ( + enableEncryptionBoundaryShift + ? submitStorageModeClearFormMutation + : submitStorageModeFormMutation + ) + .mutateAsync( + { + ...formData, + ...formPaymentData, + publicKey: form.publicKey, + }, { + onSuccess: ({ + submissionId, + timestamp, + // payment forms will have non-empty paymentData field + paymentData, + }) => { + trackSubmitForm(form) + + if (paymentData) { + navigate(getPaymentPageUrl(formId, paymentData.paymentId)) + storePaymentMemory(paymentData.paymentId) + return + } + setSubmissionData({ + id: submissionId, + timestamp, + }) + }, + }, + ) + // Using catch since we are using mutateAsync and react-hook-form will continue bubbling this up. + .catch(async (error) => { + // TODO(#5826): Remove when we have resolved the Network Error + datadogLogs.logger.warn(`handleSubmitForm: ${error.message}`, { meta: { ...logMeta, responseMode: 'storage', method: 'axios', - error, + error: { + message: error.message, + stack: error.stack, + }, }, - }, - ) - if (/Network Error/i.test(error.message)) { - axiosDebugFlow() - // fallback to fetch - return submitStorageFormWithFetch() - } - showErrorToast(error, form) - }) + }) + + if (/Network Error/i.test(error.message)) { + axiosDebugFlow() + return submitStorageFormWithFetch( + enableEncryptionBoundaryShift, + ) + } + showErrorToast(error, form) + }) + ) } } }, @@ -582,11 +678,16 @@ export const PublicFormProvider = ({ getCaptchaResponse, submitEmailModeFormFetchMutation, submitEmailModeFormMutation, - submitStorageModeFormWithVirusScanningMutation, - submitStorageModeFormWithVirusScanningFetchMutation, + enableEncryptionBoundaryShift, + enableVirusScanner, + submitStorageModeClearFormMutation, + submitStorageModeFormMutation, + submitStorageModeClearFormFetchMutation, + submitStorageModeFormFetchMutation, navigate, formId, storePaymentMemory, + submitStorageModeClearFormWithVirusScanningMutation, ], ) diff --git a/frontend/src/features/public-form/PublicFormService.ts b/frontend/src/features/public-form/PublicFormService.ts index 9cab540bee..1ae78819d0 100644 --- a/frontend/src/features/public-form/PublicFormService.ts +++ b/frontend/src/features/public-form/PublicFormService.ts @@ -1,7 +1,10 @@ import { PresignedPost } from 'aws-sdk/clients/s3' import axios from 'axios' -import { VIRUS_SCANNER_SUBMISSION_VERSION } from '~shared/constants' +import { + ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, + VIRUS_SCANNER_SUBMISSION_VERSION, +} from '~shared/constants' import { SubmitFormIssueBodyDto, SuccessMessageDto } from '~shared/types' import { AttachmentPresignedPostDataMapType, @@ -36,6 +39,7 @@ import { FormFieldValues } from '~templates/Field' import { createClearSubmissionFormData, createClearSubmissionWithVirusScanningFormData, + createEncryptedSubmissionData, getAttachmentsMap, } from './utils/createSubmission' import { filterHiddenInputs } from './utils/filterHiddenInputs' @@ -151,39 +155,113 @@ export const submitEmailModeForm = async ({ ).then(({ data }) => data) } -// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved -// Submit storage mode form with virus scanning (storage v2.1+) -export const submitStorageModeFormWithVirusScanningWithFetch = async ({ +export const submitStorageModeForm = async ({ formFields, formLogics, formInputs, formId, + publicKey, captchaResponse = null, captchaType = '', paymentReceiptEmail, responseMetadata, paymentProducts, payments, - fieldIdToQuarantineKeyMap, -}: SubmitStorageFormWithVirusScanningArgs) => { +}: SubmitStorageFormArgs) => { const filteredInputs = filterHiddenInputs({ formFields, formInputs, formLogics, }) + const submissionContent = await createEncryptedSubmissionData({ + formFields, + formInputs: filteredInputs, + publicKey, + responseMetadata, + paymentReceiptEmail, + payments, + paymentProducts, + }) + return ApiService.post( + `${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/encrypt`, + submissionContent, + { + params: { + captchaResponse: String(captchaResponse), + captchaType, + }, + }, + ).then(({ data }) => data) +} - const formData = createClearSubmissionWithVirusScanningFormData( +export const submitStorageModeClearForm = async ({ + formFields, + formLogics, + formInputs, + formId, + captchaResponse = null, + captchaType = '', + paymentReceiptEmail, + responseMetadata, + paymentProducts, + payments, +}: SubmitStorageFormClearArgs) => { + const filteredInputs = filterHiddenInputs({ + formFields, + formInputs, + formLogics, + }) + + const formData = createClearSubmissionFormData({ + formFields, + formInputs: filteredInputs, + responseMetadata, + paymentReceiptEmail, + paymentProducts, + payments, + version: ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, + }) + + return ApiService.post( + `${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/storage`, + formData, { - formFields, - formInputs: filteredInputs, - responseMetadata, - paymentReceiptEmail, - paymentProducts, - payments, - version: VIRUS_SCANNER_SUBMISSION_VERSION, + params: { + captchaResponse: String(captchaResponse), + captchaType: captchaType, + }, }, - fieldIdToQuarantineKeyMap, - ) + ).then(({ data }) => data) +} + +// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved +export const submitStorageModeClearFormWithFetch = async ({ + formFields, + formLogics, + formInputs, + formId, + captchaResponse = null, + captchaType = '', + paymentReceiptEmail, + responseMetadata, + paymentProducts, + payments, +}: SubmitStorageFormClearArgs) => { + const filteredInputs = filterHiddenInputs({ + formFields, + formInputs, + formLogics, + }) + + const formData = createClearSubmissionFormData({ + formFields, + formInputs: filteredInputs, + responseMetadata, + paymentReceiptEmail, + paymentProducts, + payments, + version: ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, + }) // Add captcha response to query string const queryString = new URLSearchParams({ @@ -206,7 +284,7 @@ export const submitStorageModeFormWithVirusScanningWithFetch = async ({ } // Submit storage mode form with virus scanning (storage v2.1+) -export const submitStorageModeFormWithVirusScanning = async ({ +export const submitStorageModeClearFormWithVirusScanning = async ({ formFields, formLogics, formInputs, @@ -291,6 +369,56 @@ export const submitEmailModeFormWithFetch = async ({ return processFetchResponse(response) } +// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved +export const submitStorageModeFormWithFetch = async ({ + formFields, + formLogics, + formInputs, + formId, + publicKey, + captchaResponse = null, + captchaType = '', + paymentReceiptEmail, + responseMetadata, + paymentProducts, + payments, +}: SubmitStorageFormArgs) => { + const filteredInputs = filterHiddenInputs({ + formFields, + formInputs, + formLogics, + }) + const submissionContent = await createEncryptedSubmissionData({ + formFields, + formInputs: filteredInputs, + publicKey, + responseMetadata, + paymentReceiptEmail, + payments, + paymentProducts, + }) + + // Add captcha response to query string + const queryString = new URLSearchParams({ + captchaResponse: String(captchaResponse), + captchaType, + }).toString() + + const response = await fetch( + `${API_BASE_URL}${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/encrypt?${queryString}`, + { + method: 'POST', + body: JSON.stringify(submissionContent), + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }, + ) + + return processFetchResponse(response) +} + /** * Post feedback for a given form. * @param formId the id of the form to post feedback for diff --git a/frontend/src/features/public-form/mutations.ts b/frontend/src/features/public-form/mutations.ts index e39c2561f4..1975644ce7 100644 --- a/frontend/src/features/public-form/mutations.ts +++ b/frontend/src/features/public-form/mutations.ts @@ -20,9 +20,13 @@ import { submitEmailModeFormWithFetch, submitFormFeedback, submitFormIssue, + SubmitStorageFormArgs, SubmitStorageFormClearArgs, - submitStorageModeFormWithVirusScanning, - submitStorageModeFormWithVirusScanningWithFetch, + submitStorageModeClearForm, + submitStorageModeClearFormWithFetch, + submitStorageModeClearFormWithVirusScanning, + submitStorageModeForm, + submitStorageModeFormWithFetch, uploadAttachmentToQuarantine, } from './PublicFormService' @@ -78,6 +82,18 @@ export const usePublicFormMutations = ( }, ) + const submitStorageModeFormMutation = useMutation( + (args: Omit) => { + return submitStorageModeForm({ ...args, formId }) + }, + ) + + const submitStorageModeClearFormMutation = useMutation( + (args: Omit) => { + return submitStorageModeClearForm({ ...args, formId }) + }, + ) + // TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved const submitEmailModeFormFetchMutation = useMutation( (args: Omit) => { @@ -85,73 +101,15 @@ export const usePublicFormMutations = ( }, ) - const submitStorageModeFormWithVirusScanningFetchMutation = useMutation( - async (args: Omit) => { - const attachmentSizes = await getAttachmentSizes(args) - // If there are no attachments, submit form without virus scanning by passing in empty list - if (attachmentSizes.length === 0) { - return submitStorageModeFormWithVirusScanningWithFetch({ - ...args, - fieldIdToQuarantineKeyMap: [], - formId, - }) - } - - // Step 1: Get presigned post data for all attachment fields - return ( - getAttachmentPresignedPostData({ ...args, formId, attachmentSizes }) - .then( - // Step 2: Upload attachments to quarantine bucket asynchronously - (fieldToPresignedPostDataMap) => - Promise.all( - fieldToPresignedPostDataMap.map( - async (fieldToPresignedPostData) => { - const attachmentFile = - args.formInputs[fieldToPresignedPostData.id] - - // Check if response is a File object (from an attachment field) - if (!(attachmentFile instanceof File)) - throw new Error('Field is not attachment') - - const uploadResponse = await uploadAttachmentToQuarantine( - fieldToPresignedPostData.presignedPostData, - attachmentFile, - ) - - // If status code is not 200-299, throw error - if ( - uploadResponse.status < 200 || - uploadResponse.status > 299 - ) - throw new Error( - `Attachment upload failed - ${uploadResponse.statusText}`, - ) - - const quarantineBucketKey = - fieldToPresignedPostData.presignedPostData.fields.key - - if (!quarantineBucketKey) - throw new Error( - 'key is not defined in presigned post data', - ) + const submitStorageModeFormFetchMutation = useMutation( + (args: Omit) => { + return submitStorageModeFormWithFetch({ ...args, formId }) + }, + ) - return { - fieldId: fieldToPresignedPostData.id, - quarantineBucketKey, - } as FieldIdToQuarantineKeyType - }, - ), - ), - ) - // Step 3: Submit form with keys to quarantine bucket attachments - .then((fieldIdToQuarantineKeyMap) => { - return submitStorageModeFormWithVirusScanningWithFetch({ - ...args, - fieldIdToQuarantineKeyMap, - formId, - }) - }) - ) + const submitStorageModeClearFormFetchMutation = useMutation( + (args: Omit) => { + return submitStorageModeClearFormWithFetch({ ...args, formId }) }, ) @@ -165,12 +123,12 @@ export const usePublicFormMutations = ( }, ) - const submitStorageModeFormWithVirusScanningMutation = useMutation( + const submitStorageModeClearFormWithVirusScanningMutation = useMutation( async (args: Omit) => { const attachmentSizes = await getAttachmentSizes(args) // If there are no attachments, submit form without virus scanning by passing in empty list if (attachmentSizes.length === 0) { - return submitStorageModeFormWithVirusScanning({ + return submitStorageModeClearFormWithVirusScanning({ ...args, fieldIdToQuarantineKeyMap: [], formId, @@ -224,7 +182,7 @@ export const usePublicFormMutations = ( ) // Step 3: Submit form with keys to quarantine bucket attachments .then((fieldIdToQuarantineKeyMap) => { - return submitStorageModeFormWithVirusScanning({ + return submitStorageModeClearFormWithVirusScanning({ ...args, fieldIdToQuarantineKeyMap, formId, @@ -236,10 +194,13 @@ export const usePublicFormMutations = ( return { submitEmailModeFormMutation, + submitStorageModeFormMutation, submitFormFeedbackMutation, + submitStorageModeFormFetchMutation, submitEmailModeFormFetchMutation, - submitStorageModeFormWithVirusScanningFetchMutation, - submitStorageModeFormWithVirusScanningMutation, + submitStorageModeClearFormMutation, + submitStorageModeClearFormFetchMutation, + submitStorageModeClearFormWithVirusScanningMutation, } } diff --git a/package-lock.json b/package-lock.json index 554a33c83e..8ba0412c86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "FormSG", - "version": "6.89.1", + "version": "6.89.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "FormSG", - "version": "6.89.1", + "version": "6.89.2", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.347.1", diff --git a/package.json b/package.json index 342bd6a950..a4f209aa22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FormSG", "description": "Form Manager for Government", - "version": "6.89.1", + "version": "6.89.2", "homepage": "https://form.gov.sg", "authors": [ "FormSG " diff --git a/shared/constants/form.ts b/shared/constants/form.ts index 6b355aa7b6..a6a26085e1 100644 --- a/shared/constants/form.ts +++ b/shared/constants/form.ts @@ -68,4 +68,5 @@ export const PAYMENT_VARIABLE_INPUT_AMOUNT_FIELD_ID = export const E2EE_SUBMISSION_VERSION = 1 // Encryption boundary shift RFC: https://docs.google.com/document/d/1VmNXS_xYY2Yg30AwVqzdndBp5yRJGSDsyjBnH51ktyc/edit?usp=sharing // Encryption boundary shift implementation PR: https://github.com/opengovsg/FormSG/pull/6587 +export const ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION = 2 export const VIRUS_SCANNER_SUBMISSION_VERSION = 2.1