diff --git a/.template-env b/.template-env index dfa7728d00..6b491e7708 100644 --- a/.template-env +++ b/.template-env @@ -84,7 +84,6 @@ FORMSG_SDK_MODE= # purposes. # MYINFO_CLIENT_CONFIG=stg # MYINFO_FORMSG_KEY_PATH=./node_modules/@opengovsg/mockpass/static/certs/key.pem -# SPCP_COOKIE_MAX_AGE=7200000 # SP_FORMSG_KEY_PATH=./node_modules/@opengovsg/mockpass/static/certs/key.pem # SP_FORMSG_CERT_PATH=./node_modules/@opengovsg/mockpass/static/certs/server.crt diff --git a/CHANGELOG.md b/CHANGELOG.md index fb739f1a20..5f24dac9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,50 @@ 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). +#### [v4.58.2](https://github.com/opengovsg/FormSG/compare/v4.58.1...v4.58.2) + +- fix: allow slash in msgSrvcName [`31fc57c`](https://github.com/opengovsg/FormSG/commit/31fc57c815fa903e822e4cce0384d96d8ca3329b) + +#### [v4.58.1](https://github.com/opengovsg/FormSG/compare/v4.58.0...v4.58.1) + +> 17 February 2021 + +- fix: Release 4.58.1 hotfix - mobile signature validation [`#1151`](https://github.com/opengovsg/FormSG/pull/1151) +- fix: add back homeno icon [`#1152`](https://github.com/opengovsg/FormSG/pull/1152) +- chore: bump version to 4.58.1 [`d4a29df`](https://github.com/opengovsg/FormSG/commit/d4a29dffe6af49c98c42f6675531731c228333e6) +- fix: allow 0 as fieldValue [`a81fda2`](https://github.com/opengovsg/FormSG/commit/a81fda281338e22a4c3af859e84cd5f1d6c66af6) + +#### [v4.58.0](https://github.com/opengovsg/FormSG/compare/v4.57.0...v4.58.0) + +> 16 February 2021 + +- chore(deps-dev): bump typescript from 4.1.4 to 4.1.5 [`#1137`](https://github.com/opengovsg/FormSG/pull/1137) +- chore(deps-dev): bump @babel/core from 7.12.13 to 7.12.16 [`#1136`](https://github.com/opengovsg/FormSG/pull/1136) +- fix(deps): bump moment-timezone from 0.5.32 to 0.5.33 [`#1130`](https://github.com/opengovsg/FormSG/pull/1130) +- fix(deps): bump jszip from 3.5.0 to 3.6.0 [`#1131`](https://github.com/opengovsg/FormSG/pull/1131) +- chore(deps-dev): bump lint-staged from 10.5.3 to 10.5.4 [`#1132`](https://github.com/opengovsg/FormSG/pull/1132) +- chore(deps-dev): bump stylelint from 13.8.0 to 13.10.0 [`#1135`](https://github.com/opengovsg/FormSG/pull/1135) +- refactor: remove SPCP_COOKIE_MAX_AGE [`#1134`](https://github.com/opengovsg/FormSG/pull/1134) +- feat: deprecate MyInfo fields for V3 (step 3 - drop support) [`#1073`](https://github.com/opengovsg/FormSG/pull/1073) +- build: docker-compose to use mongo:4.0 image [`#1114`](https://github.com/opengovsg/FormSG/pull/1114) +- feat: update SMS OTP message [`#1085`](https://github.com/opengovsg/FormSG/pull/1085) +- chore(deps-dev): bump @typescript-eslint/parser from 4.12.0 to 4.15.0 [`#1106`](https://github.com/opengovsg/FormSG/pull/1106) +- chore(deps-dev): bump ts-mock-imports from 1.3.1 to 1.3.3 [`#1122`](https://github.com/opengovsg/FormSG/pull/1122) +- fix(deps): bump fp-ts from 2.9.4 to 2.9.5 [`#1124`](https://github.com/opengovsg/FormSG/pull/1124) +- chore(deps-dev): bump @types/express-rate-limit from 5.1.0 to 5.1.1 [`#1123`](https://github.com/opengovsg/FormSG/pull/1123) +- fix(deps): bump web-streams-polyfill from 3.0.1 to 3.0.2 [`#1121`](https://github.com/opengovsg/FormSG/pull/1121) +- chore(deps-dev): bump typescript from 4.1.3 to 4.1.4 [`#1120`](https://github.com/opengovsg/FormSG/pull/1120) +- chore(deps-dev): bump @typescript-eslint/eslint-plugin [`#1107`](https://github.com/opengovsg/FormSG/pull/1107) +- chore(deps-dev): bump eslint from 7.17.0 to 7.19.0 [`#1108`](https://github.com/opengovsg/FormSG/pull/1108) +- build: merge Release 4.57.0 into develop [`#1117`](https://github.com/opengovsg/FormSG/pull/1117) +- fix: revert merge to extend [`#1115`](https://github.com/opengovsg/FormSG/pull/1115) +- ref: migrate encrypt SP/CP verified content flow into separate module [`#934`](https://github.com/opengovsg/FormSG/pull/934) +- chore: bump version to 4.58.0 [`ad2a796`](https://github.com/opengovsg/FormSG/commit/ad2a7969cc85c94c42a2b105baa9077eb9585917) + #### [v4.57.0](https://github.com/opengovsg/FormSG/compare/v4.56.0...v4.57.0) +> 10 February 2021 + - feat: mask cp uid in autoreply to respondent [`#1109`](https://github.com/opengovsg/FormSG/pull/1109) - fix: interrupt submission if field is blank [`#1096`](https://github.com/opengovsg/FormSG/pull/1096) - fix(deps): bump @babel/runtime from 7.12.5 to 7.12.13 [`#1098`](https://github.com/opengovsg/FormSG/pull/1098) @@ -50,6 +92,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - chore(deps-dev): bump @types/node from 14.14.20 to 14.14.22 [`#1043`](https://github.com/opengovsg/FormSG/pull/1043) - chore(deps-dev): bump husky from 4.3.7 to 4.3.8 [`#1033`](https://github.com/opengovsg/FormSG/pull/1033) - chore: merge 4.55.0 into develop [`#1051`](https://github.com/opengovsg/FormSG/pull/1051) +- chore: bump version to 4.57.0 [`8649e1d`](https://github.com/opengovsg/FormSG/commit/8649e1dc5467180fb80e52595eb8bfed1f7933e7) #### [v4.56.0](https://github.com/opengovsg/FormSG/compare/v4.55.0...v4.56.0) @@ -266,16 +309,16 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - build: Release 4.30.1 - Fix field creation on old clients [`#74`](https://github.com/opengovsg/FormSG/pull/74) - Release 4.30.0 - acknowledgement for secret key backup, TypeScript migrations [`#67`](https://github.com/opengovsg/FormSG/pull/67) - build: empty commit to trigger PR build [`d0c6583`](https://github.com/opengovsg/FormSG/commit/d0c65838efa6731a0f10e89ff954c132b9f8b854) -- feat: update table field styling to not rely on multiple divs [`db03da3`](https://github.com/opengovsg/FormSG/commit/db03da33eca2fc0a6174ca58d7dd8b3cfadadcea) - fix: return 401 for missing JWT [`e6c1947`](https://github.com/opengovsg/FormSG/commit/e6c19477b05fc4aed90b2db42916220aea2a263c) +- test: add tests for extractJwt [`16191a9`](https://github.com/opengovsg/FormSG/commit/16191a957be0dc7f63dc4221569fac2794b7d063) #### [v4.50.2](https://github.com/opengovsg/FormSG/compare/v4.50.1...v4.50.2) > 16 December 2020 -- feat: update table field styling to not rely on multiple divs [`e857aed`](https://github.com/opengovsg/FormSG/commit/e857aed667145441758f09f311c2b9f16f57645b) +- feat: update table field styling to not rely on multiple divs [`db03da3`](https://github.com/opengovsg/FormSG/commit/db03da33eca2fc0a6174ca58d7dd8b3cfadadcea) - fix: email format validation should allow 126/163.com, align frontend and backend validation [`be35522`](https://github.com/opengovsg/FormSG/commit/be35522e15d20521f58fada7ed3164e6c0c0894d) -- chore: bump version to v4.50.2 [`fad62ec`](https://github.com/opengovsg/FormSG/commit/fad62ec5730412201e208332a91286fd53e2b36a) +- chore: bump version to v4.50.2 [`1ac7be6`](https://github.com/opengovsg/FormSG/commit/1ac7be6cb69553d186ec194682ae25bd98318a8b) #### [v4.50.1](https://github.com/opengovsg/FormSG/compare/v4.50.0...v4.50.1) diff --git a/docker-compose.yml b/docker-compose.yml index 737172c725..b842b867ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,6 @@ services: - DB_HOST=mongodb://database:27017/formsg - APP_NAME=FormSG - MYINFO_CLIENT_CONFIG=stg - - SPCP_COOKIE_MAX_AGE=7200000 - ATTACHMENT_S3_BUCKET=local-attachment-bucket - IMAGE_S3_BUCKET=local-image-bucket - LOGO_S3_BUCKET=local-logo-bucket @@ -84,7 +83,7 @@ services: network_mode: 'service:formsg' # reuse formsg service's network stack so that it can resolve localhost:5156 to mockpass:5156 database: - image: 'mongo:3.6' + image: 'mongo:4.0' container_name: 'formsg-db' environment: - MONGO_INITDB_DATABASE=formsg diff --git a/package-lock.json b/package-lock.json index 6d79a1a633..0baac2fc88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "FormSG", - "version": "4.57.0", + "version": "4.58.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -20,16 +20,16 @@ "dev": true }, "@babel/core": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", - "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.16.tgz", + "integrity": "sha512-t/hHIB504wWceOeaOoONOhu+gX+hpjfeN6YRBT209X/4sibZQfSF1I0HFRRlBe97UZZosGx5XwUg1ZgNbelmNw==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.12.13", + "@babel/generator": "^7.12.15", "@babel/helper-module-transforms": "^7.12.13", "@babel/helpers": "^7.12.13", - "@babel/parser": "^7.12.13", + "@babel/parser": "^7.12.16", "@babel/template": "^7.12.13", "@babel/traverse": "^7.12.13", "@babel/types": "^7.12.13", @@ -52,9 +52,9 @@ } }, "@babel/generator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.13.tgz", - "integrity": "sha512-9qQ8Fgo8HaSvHEt6A5+BATP7XktD/AdAnObUeTRz5/e2y3kbrxZgz32qUJJsdmwUvBJzF4AeV21nGTNwv05Mpw==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { "@babel/types": "^7.12.13", @@ -82,71 +82,6 @@ "@babel/types": "^7.12.13" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", - "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", - "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", - "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13", - "@babel/helper-simple-access": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-replace-supers": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", - "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.13", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", - "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, "@babel/helper-split-export-declaration": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", @@ -174,9 +109,9 @@ } }, "@babel/parser": { - "version": "7.12.14", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.14.tgz", - "integrity": "sha512-xcfxDq3OrBnDsA/Z8eK5/2iPcLD8qbOaSSfOw4RA6jp4i7e6dEQ7+wTwxItEwzcXPQcsry5nZk96gmVPKletjQ==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { @@ -1129,9 +1064,9 @@ } }, "@babel/generator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.13.tgz", - "integrity": "sha512-9qQ8Fgo8HaSvHEt6A5+BATP7XktD/AdAnObUeTRz5/e2y3kbrxZgz32qUJJsdmwUvBJzF4AeV21nGTNwv05Mpw==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { "@babel/types": "^7.12.13", @@ -1186,9 +1121,9 @@ } }, "@babel/parser": { - "version": "7.12.14", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.14.tgz", - "integrity": "sha512-xcfxDq3OrBnDsA/Z8eK5/2iPcLD8qbOaSSfOw4RA6jp4i7e6dEQ7+wTwxItEwzcXPQcsry5nZk96gmVPKletjQ==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { @@ -3410,9 +3345,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -3422,7 +3357,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -4576,9 +4511,9 @@ } }, "@types/express-rate-limit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.0.tgz", - "integrity": "sha512-vmg7S3hUnfFmp06V01DrTB41mbQYXMV/F4aF5KKnfCIeSlnizatXaqO9UgR6LvNEEd3eMpuUTLxR6nv3d4hZ6g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.1.tgz", + "integrity": "sha512-6oMYZBLlhxC5sdcRXXz528QyfGz3zTy9YdHwqlxLfgx5Cd3zwYaUjjPpJcaTtHmRefLi9P8kLBPz2wB7yz4JtQ==", "dev": true, "requires": { "@types/express": "*" @@ -5061,13 +4996,13 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz", - "integrity": "sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz", + "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.14.2", - "@typescript-eslint/scope-manager": "4.14.2", + "@typescript-eslint/experimental-utils": "4.15.0", + "@typescript-eslint/scope-manager": "4.15.0", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", @@ -5077,58 +5012,57 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz", - "integrity": "sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", + "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.14.2", - "@typescript-eslint/types": "4.14.2", - "@typescript-eslint/typescript-estree": "4.14.2", + "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/typescript-estree": "4.15.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/scope-manager": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz", - "integrity": "sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", + "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.2", - "@typescript-eslint/visitor-keys": "4.14.2" + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0" } }, "@typescript-eslint/types": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.2.tgz", - "integrity": "sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", + "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz", - "integrity": "sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", + "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.2", - "@typescript-eslint/visitor-keys": "4.14.2", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", - "lodash": "^4.17.15", "semver": "^7.3.2", "tsutils": "^3.17.1" } }, "@typescript-eslint/visitor-keys": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz", - "integrity": "sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", + "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.14.2", + "@typescript-eslint/types": "4.15.0", "eslint-visitor-keys": "^2.0.0" } }, @@ -5188,56 +5122,55 @@ } }, "@typescript-eslint/parser": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.12.0.tgz", - "integrity": "sha512-9XxVADAo9vlfjfoxnjboBTxYOiNY93/QuvcPgsiKvHxW6tOZx1W4TvkIQ2jB3k5M0pbFP5FlXihLK49TjZXhuQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz", + "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.12.0", - "@typescript-eslint/types": "4.12.0", - "@typescript-eslint/typescript-estree": "4.12.0", + "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/typescript-estree": "4.15.0", "debug": "^4.1.1" }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.12.0.tgz", - "integrity": "sha512-QVf9oCSVLte/8jvOsxmgBdOaoe2J0wtEmBr13Yz0rkBNkl5D8bfnf6G4Vhox9qqMIoG7QQoVwd2eG9DM/ge4Qg==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", + "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", "dev": true, "requires": { - "@typescript-eslint/types": "4.12.0", - "@typescript-eslint/visitor-keys": "4.12.0" + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0" } }, "@typescript-eslint/types": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.12.0.tgz", - "integrity": "sha512-N2RhGeheVLGtyy+CxRmxdsniB7sMSCfsnbh8K/+RUIXYYq3Ub5+sukRCjVE80QerrUBvuEvs4fDhz5AW/pcL6g==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", + "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.12.0.tgz", - "integrity": "sha512-gZkFcmmp/CnzqD2RKMich2/FjBTsYopjiwJCroxqHZIY11IIoN0l5lKqcgoAPKHt33H2mAkSfvzj8i44Jm7F4w==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", + "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.12.0", - "@typescript-eslint/visitor-keys": "4.12.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", - "lodash": "^4.17.15", "semver": "^7.3.2", "tsutils": "^3.17.1" } }, "@typescript-eslint/visitor-keys": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.12.0.tgz", - "integrity": "sha512-hVpsLARbDh4B9TKYz5cLbcdMIOAoBYgFPCSP9FFS/liSF+b33gVNq8JHY3QGhHNVz85hObvL7BEYLlgx553WCw==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", + "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.12.0", + "@typescript-eslint/types": "4.15.0", "eslint-visitor-keys": "^2.0.0" } }, @@ -7581,12 +7514,6 @@ "color-convert": "^2.0.1" } }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10152,13 +10079,13 @@ } }, "eslint": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz", - "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", + "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -10182,7 +10109,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -10689,9 +10616,9 @@ "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=" }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -11545,9 +11472,9 @@ } }, "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "flush-write-stream": { @@ -11613,9 +11540,9 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fp-ts": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.9.4.tgz", - "integrity": "sha512-s4wbgHOylMdOALJuF07Y1jblFAhe+2I68tR/SymAb2a/BcwGRD/l/9oc6SESgiaTnCFR3d2FZ5LZpsMmXMWv5A==" + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.9.5.tgz", + "integrity": "sha512-MiHrA5teO6t8zKArE3DdMPT/Db6v2GUt5yfWnhBTrrsVfeCJUUnV6sgFvjGNBKDmEMqVwRFkEePL7wPwqrLKKA==" }, "fragment-cache": { "version": "0.2.1", @@ -15111,9 +15038,9 @@ } }, "jszip": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", - "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz", + "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -15169,9 +15096,9 @@ "dev": true }, "known-css-properties": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.20.0.tgz", - "integrity": "sha512-URvsjaA9ypfreqJ2/ylDr5MUERhJZ+DhguoWRr2xgS5C7aGCalXo+ewL+GixgKBfhT2vuL02nbIgNGqVWgTOYw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", "dev": true }, "kuler": { @@ -15230,9 +15157,9 @@ "dev": true }, "lint-staged": { - "version": "10.5.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.3.tgz", - "integrity": "sha512-TanwFfuqUBLufxCc3RUtFEkFraSPNR3WzWcGF39R3f2J7S9+iF9W0KTVLfSy09lYGmZS5NDCxjNvhGMSJyFCWg==", + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -15401,9 +15328,9 @@ } }, "listr2": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.3.tgz", - "integrity": "sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.3.1.tgz", + "integrity": "sha512-8Zoxe7s/8nNr4bJ8bdAduHD8uJce+exmMmUWTXlq0WuUdffnH3muisHPHPFtW2vvOfohIsq7FGCaguUxN/h3Iw==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -15413,9 +15340,16 @@ "log-update": "^4.0.0", "p-map": "^4.0.0", "rxjs": "^6.6.3", - "through": "^2.3.8" + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -15456,6 +15390,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -15465,6 +15405,26 @@ "tslib": "^1.9.0" } }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -15473,6 +15433,17 @@ "requires": { "has-flag": "^4.0.0" } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } } } }, @@ -15730,55 +15701,6 @@ "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } } }, "log-update-async-hook": { @@ -16044,21 +15966,22 @@ } }, "mdast-util-from-markdown": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.1.tgz", - "integrity": "sha512-qJXNcFcuCSPqUF0Tb0uYcFDIq67qwB3sxo9RPdf9vG8T90ViKnksFqdB/Coq2a7sTnxL/Ify2y7aIQXDkQFH0w==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dev": true, "requires": { "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^1.0.0", - "micromark": "~2.10.0", - "parse-entities": "^2.0.0" + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" } }, "mdast-util-to-markdown": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.5.4.tgz", - "integrity": "sha512-0jQTkbWYx0HdEA/h++7faebJWr5JyBoBeiRf0u3F4F3QtnyyGaWIsOwo749kRb1ttKrLLr+wRtOkfou9yB0p6A==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -16067,20 +15990,12 @@ "parse-entities": "^2.0.0", "repeat-string": "^1.0.0", "zwitch": "^1.0.0" - }, - "dependencies": { - "mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "dev": true - } } }, "mdast-util-to-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", - "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", "dev": true }, "mdn-data": { @@ -16111,13 +16026,14 @@ "optional": true }, "meow": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", - "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", "dev": true, "requires": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", @@ -16130,9 +16046,9 @@ }, "dependencies": { "hosted-git-info": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", - "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", + "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -16160,10 +16076,13 @@ } }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "type-fest": { "version": "0.18.1", @@ -16208,9 +16127,9 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromark": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.10.1.tgz", - "integrity": "sha512-fUuVF8sC1X7wsCS29SYQ2ZfIZYbTymp0EYr6sab3idFjigFFjGa5UwoniPlV9tAgntjuapW1t9U+S0yDYeGKHQ==", + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", "dev": true, "requires": { "debug": "^4.0.0", @@ -16558,9 +16477,9 @@ "dev": true }, "moment-timezone": { - "version": "0.5.32", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", - "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", "requires": { "moment": ">= 2.9.0" } @@ -19817,12 +19736,12 @@ } }, "remark-stringify": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.0.tgz", - "integrity": "sha512-8x29DpTbVzEc6Dwb90qhxCtbZ6hmj3BxWWDpMhA+1WM4dOEGH5U5/GFe3Be5Hns5MvPSFAr1e2KSVtKZkK5nUw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", "dev": true, "requires": { - "mdast-util-to-markdown": "^0.5.0" + "mdast-util-to-markdown": "^0.6.0" } }, "remove-trailing-separator": { @@ -19852,12 +19771,6 @@ "is-finite": "^1.0.0" } }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, "replicator": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/replicator/-/replicator-1.0.3.tgz", @@ -21398,9 +21311,9 @@ } }, "stylelint": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.8.0.tgz", - "integrity": "sha512-iHH3dv3UI23SLDrH4zMQDjLT9/dDIz/IpoFeuNxZmEx86KtfpjDOscxLTFioQyv+2vQjPlRZnK0UoJtfxLICXQ==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.10.0.tgz", + "integrity": "sha512-eDuLrL0wzPKbl5/TbNGZcbw0lTIGbDEr5W6lCODvb1gAg0ncbgCRt7oU0C2VFDvbrcY0A3MFZOwltwTRmc0XCw==", "dev": true, "requires": { "@stylelint/postcss-css-in-js": "^0.37.2", @@ -21409,24 +21322,24 @@ "balanced-match": "^1.0.0", "chalk": "^4.1.0", "cosmiconfig": "^7.0.0", - "debug": "^4.2.0", + "debug": "^4.3.1", "execall": "^2.0.0", - "fast-glob": "^3.2.4", + "fast-glob": "^3.2.5", "fastest-levenshtein": "^1.0.12", "file-entry-cache": "^6.0.0", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.1", + "globby": "^11.0.2", "globjoin": "^0.1.4", "html-tags": "^3.1.0", "ignore": "^5.1.8", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.20.0", + "known-css-properties": "^0.21.0", "lodash": "^4.17.20", "log-symbols": "^4.0.0", "mathml-tag-names": "^2.1.3", - "meow": "^8.0.0", + "meow": "^9.0.0", "micromatch": "^4.0.2", "normalize-selector": "^0.2.0", "postcss": "^7.0.35", @@ -21448,7 +21361,7 @@ "style-search": "^0.1.0", "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^6.0.3", + "table": "^6.0.7", "v8-compile-cache": "^2.2.0", "write-file-atomic": "^3.0.3" }, @@ -21468,12 +21381,6 @@ "color-convert": "^2.0.1" } }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -21508,37 +21415,40 @@ "ms": "2.1.2" } }, - "file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" } }, - "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", - "dev": true - }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true }, + "globby": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -21649,17 +21559,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -21695,18 +21594,6 @@ "has-flag": "^4.0.0" } }, - "table": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.4.tgz", - "integrity": "sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - } - }, "v8-compile-cache": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", @@ -21941,9 +21828,9 @@ }, "dependencies": { "ajv": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", - "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.4.tgz", + "integrity": "sha512-xzzzaqgEQfmuhbhAoqjJ8T/1okb6gAzXn/eQRNpAN1AEUoHJTNF9xCDRTtf/s3SKldtZfa+RJeTs+BQq+eZ/sw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -23518,9 +23405,9 @@ } }, "ts-mock-imports": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ts-mock-imports/-/ts-mock-imports-1.3.1.tgz", - "integrity": "sha512-MKEGXb40TUbpQ6b/if424zs0gfTyHfsebw+FUBkqbC0kVoPwoXhoe82lJH4dC92j4vDoId6pSjtIvwvtSMnS5w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/ts-mock-imports/-/ts-mock-imports-1.3.3.tgz", + "integrity": "sha512-zCAcb89Y+f3Bhw5VaHrHMh5tiuwAQEl5D3e0r5ELCdLl9EnZpb8Oeei/S60Qf4LORIfmJEXb3TpR5kxtL6j2cg==", "dev": true }, "ts-node": { @@ -23709,9 +23596,9 @@ } }, "typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", + "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", "dev": true }, "uglify-js": { @@ -23870,9 +23757,9 @@ } }, "unist-util-is": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.3.tgz", - "integrity": "sha512-bTofCFVx0iQM8Jqb1TBDVRIQW03YkD3p66JOd/aCWuqzlLyUtx1ZAGw/u+Zw+SttKvSVcvTiKYbfrtLoLefykw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", "dev": true }, "unist-util-stringify-position": { @@ -24164,14 +24051,13 @@ } }, "vfile": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz", - "integrity": "sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dev": true, "requires": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" } @@ -24510,9 +24396,9 @@ "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" }, "web-streams-polyfill": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.1.tgz", - "integrity": "sha512-M+EmTdszMWINywOZaqpZ6VIEDUmNpRaTOuizF0ZKPjSDC8paMRe/jBBwFv0Yeyn5WYnM5pMqMQa82vpaE+IJRw==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.2.tgz", + "integrity": "sha512-JTNkNbAKoSo8NKiqu2UUaqRFCDWWZaCOsXuJEsToWopikTA0YHKKUf91GNkS/SnD8JixOkJjVsiacNlrFnRECA==" }, "webauth": { "version": "1.1.0", diff --git a/package.json b/package.json index 5becdce9dd..ae7f1c2a38 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FormSG", "description": "Form Manager for Government", - "version": "4.57.0", + "version": "4.58.2", "homepage": "https://form.gov.sg", "authors": [ "FormSG " @@ -119,17 +119,17 @@ "file-loader": "^4.3.0", "file-saver": "^2.0.5", "font-awesome": "4.7.0", - "fp-ts": "^2.9.4", + "fp-ts": "^2.9.5", "has-ansi": "^4.0.0", "helmet": "^4.4.1", "http-status-codes": "^2.1.4", "intl-tel-input": "~12.4.0", "json-stringify-safe": "^5.0.1", - "jszip": "^3.2.2", + "jszip": "^3.6.0", "jwt-decode": "^3.1.2", "libphonenumber-js": "^1.9.8", "lodash": "^4.17.20", - "moment-timezone": "0.5.32", + "moment-timezone": "0.5.33", "mongodb-uri": "^0.9.7", "mongoose": "^5.11.10", "multiparty": ">=4.2.2", @@ -157,13 +157,13 @@ "uid-generator": "^2.0.0", "uuid": "^8.3.2", "validator": "^13.5.2", - "web-streams-polyfill": "^3.0.1", + "web-streams-polyfill": "^3.0.2", "whatwg-fetch": "^3.5.0", "winston": "^3.3.3", "winston-cloudwatch": "^2.5.0" }, "devDependencies": { - "@babel/core": "^7.12.13", + "@babel/core": "^7.12.16", "@babel/plugin-transform-runtime": "^7.12.15", "@babel/preset-env": "^7.12.13", "@opengovsg/mockpass": "^2.6.2", @@ -176,7 +176,7 @@ "@types/dedent": "^0.7.0", "@types/ejs": "^3.0.5", "@types/express": "^4.17.11", - "@types/express-rate-limit": "^5.1.0", + "@types/express-rate-limit": "^5.1.1", "@types/express-request-id": "^1.4.1", "@types/express-serve-static-core": "^4.17.18", "@types/express-session": "^1.17.0", @@ -198,8 +198,8 @@ "@types/uid-generator": "^2.0.2", "@types/uuid": "^8.3.0", "@types/validator": "^13.1.3", - "@typescript-eslint/eslint-plugin": "^4.14.2", - "@typescript-eslint/parser": "^4.12.0", + "@typescript-eslint/eslint-plugin": "^4.15.0", + "@typescript-eslint/parser": "^4.15.0", "auto-changelog": "^2.2.1", "axios-mock-adapter": "^1.19.0", "babel-loader": "^8.2.2", @@ -210,7 +210,7 @@ "css-loader": "^2.1.1", "csv-parse": "^4.15.1", "env-cmd": "^10.1.0", - "eslint": "^7.17.0", + "eslint": "^7.19.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-angular": "^4.0.1", "eslint-plugin-import": "^2.22.1", @@ -228,7 +228,7 @@ "jasmine-sinon": "^0.4.0", "jasmine-spec-reporter": "^6.0.0", "jest": "^26.6.3", - "lint-staged": "^10.5.3", + "lint-staged": "^10.5.4", "maildev": "^1.1.0", "mini-css-extract-plugin": "^0.5.0", "mockdate": "^3.0.2", @@ -241,7 +241,7 @@ "regenerator": "^0.14.4", "rimraf": "^3.0.2", "sinon": "^9.2.4", - "stylelint": "^13.8.0", + "stylelint": "^13.10.0", "stylelint-config-prettier": "^8.0.2", "stylelint-config-standard": "^20.0.0", "stylelint-prettier": "^1.1.2", @@ -251,11 +251,11 @@ "testcafe": "^1.10.1", "ts-jest": "^26.5.0", "ts-loader": "^7.0.5", - "ts-mock-imports": "^1.3.1", + "ts-mock-imports": "^1.3.3", "ts-node": "^9.1.1", "ts-node-dev": "^1.1.1", "type-fest": "^0.20.2", - "typescript": "^4.1.3", + "typescript": "^4.1.5", "url-loader": "^1.1.2", "webpack": "^4.46.0", "webpack-cli": "^3.3.12", diff --git a/src/app/controllers/spcp.server.controller.js b/src/app/controllers/spcp.server.controller.js deleted file mode 100644 index 313857ccf9..0000000000 --- a/src/app/controllers/spcp.server.controller.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict' - -const formsgSdk = require('../../config/formsg-sdk') -const { isEmpty } = require('lodash') - -const { StatusCodes } = require('http-status-codes') - -const { createReqMeta } = require('../utils/request') -const logger = require('../../config/logger').createLoggerWithLabel(module) -const { mapDataToKey } = require('../../shared/util/verified-content') - -/** - * Encrypt and sign verified fields if exist - * @param {Object} req - Express request object - * @param {Object} res - Express response object - */ -exports.encryptedVerifiedFields = (signingSecretKey) => { - return (req, res, next) => { - const authType = req.form && req.form.authType - // Early return if this is not a Singpass/Corppass submission. - if (!authType) return next() - - const verifiedContent = mapDataToKey({ type: authType, data: res.locals }) - - if (isEmpty(verifiedContent)) return next() - - try { - const encryptedVerified = formsgSdk.crypto.encrypt( - verifiedContent, - req.form.publicKey, - signingSecretKey, - ) - - res.locals.verified = encryptedVerified - return next() - } catch (error) { - logger.error({ - message: 'Unable to encrypt verified content', - meta: { - action: 'encryptedVerifiedFields', - formId: req.form._id, - ...createReqMeta(req), - }, - error, - }) - return res - .status(StatusCodes.BAD_REQUEST) - .json({ message: 'Invalid data was found. Please submit again.' }) - } - } -} diff --git a/src/app/factories/webhook-verified-content.factory.js b/src/app/factories/webhook-verified-content.factory.js index 46b5fb058b..924a5a7e70 100644 --- a/src/app/factories/webhook-verified-content.factory.js +++ b/src/app/factories/webhook-verified-content.factory.js @@ -1,18 +1,13 @@ const webhook = require('../modules/webhook/webhook.controller') -const spcp = require('../controllers/spcp.server.controller') const featureManager = require('../../config/feature-manager').default const webhookVerifiedContentFactory = ({ isEnabled, props }) => { if (isEnabled && props) { return { - encryptedVerifiedFields: spcp.encryptedVerifiedFields( - props.signingSecretKey, - ), post: webhook.post, } } else { return { - encryptedVerifiedFields: (req, res, next) => next(), post: (req, res, next) => next(), } } diff --git a/src/app/models/form.server.model.ts b/src/app/models/form.server.model.ts index 186c0392c3..db4630cbef 100644 --- a/src/app/models/form.server.model.ts +++ b/src/app/models/form.server.model.ts @@ -290,8 +290,8 @@ const compileFormModel = (db: Mongoose): IFormModel => { type: String, required: false, validate: [ - /^([a-zA-Z0-9-])+$/i, - 'msgSrvcName must be alphanumeric, dashes are allowed', + /^([a-zA-Z0-9-/])+$/i, + 'msgSrvcName must be alphanumeric, dashes and slashes are allowed', ], }, }, diff --git a/src/app/modules/form/form.utils.ts b/src/app/modules/form/form.utils.ts index 95a9f0b3df..bafb36c7fb 100644 --- a/src/app/modules/form/form.utils.ts +++ b/src/app/modules/form/form.utils.ts @@ -1,7 +1,13 @@ import { pick } from 'lodash' import { Merge } from 'type-fest' -import { IPopulatedForm, Permission } from '../../../types' +import { + IEncryptedFormSchema, + IFormSchema, + IPopulatedForm, + Permission, + ResponseMode, +} from '../../../types' // Kept in this file instead of form.types.ts so that this can be kept in sync // with FORM_PUBLIC_FIELDS more easily. @@ -56,6 +62,17 @@ export const removePrivateDetailsFromForm = ( } } +/** + * Typeguard to check if given form is an encrypt mode form. + * @param form the form to check + * @returns true if form is encrypt mode form, false otherwise. + */ +export const isFormEncryptMode = ( + form: IFormSchema | IPopulatedForm, +): form is IEncryptedFormSchema => { + return form.responseMode === ResponseMode.Encrypt +} + /** * Extracts emails of collaborators, optionally filtering for a specific * write permission. diff --git a/src/app/modules/spcp/spcp.errors.ts b/src/app/modules/spcp/spcp.errors.ts index a056fd90c8..7a523de4db 100644 --- a/src/app/modules/spcp/spcp.errors.ts +++ b/src/app/modules/spcp/spcp.errors.ts @@ -1,5 +1,6 @@ import { AuthType } from '../../../types' import { ApplicationError } from '../../modules/core/core.errors' + /** * Error while creating redirect URL */ @@ -36,7 +37,7 @@ export class VerifyJwtError extends ApplicationError { } } -/* +/** * Invalid OOB params passed to login endpoint. */ export class InvalidOOBParamsError extends ApplicationError { diff --git a/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.routes.spec.ts b/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.routes.spec.ts index 6b3fad5176..cf0b104dd0 100644 --- a/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.routes.spec.ts +++ b/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.routes.spec.ts @@ -12,6 +12,7 @@ import * as PublicFormMiddleware from 'src/app/modules/form/public-form/public-f import * as SpcpController from 'src/app/modules/spcp/spcp.controller' import * as EncryptSubmissionsMiddleware from 'src/app/modules/submission/encrypt-submission/encrypt-submission.middleware' import * as SubmissionsMiddleware from 'src/app/modules/submission/submission.middleware' +import * as VerifiedContentMiddleware from 'src/app/modules/verified-content/verified-content.middlewares' import { CaptchaFactory } from 'src/app/services/captcha/captcha.factory' import * as CaptchaMiddleware from 'src/app/services/captcha/captcha.middleware' import { AuthType, BasicField, Status } from 'src/types' @@ -93,7 +94,7 @@ EncryptSubmissionsRouter.post( EncryptSubmissionsMiddleware.validateAndProcessEncryptSubmission, SpcpController.isSpcpAuthenticated, MyInfoController.verifyMyInfoVals as RequestHandler, - webhookVerifiedContentFactory.encryptedVerifiedFields as RequestHandler, + VerifiedContentMiddleware.encryptVerifiedSpcpFields, EncryptSubmissionsMiddleware.prepareEncryptSubmission as RequestHandler, (encryptSubmissions.saveResponseToDb as unknown) as RequestHandler, webhookVerifiedContentFactory.post as RequestHandler, diff --git a/src/app/modules/verified-content/__tests__/verified-content.service.spec.ts b/src/app/modules/verified-content/__tests__/verified-content.service.spec.ts new file mode 100644 index 0000000000..390ccf6bda --- /dev/null +++ b/src/app/modules/verified-content/__tests__/verified-content.service.spec.ts @@ -0,0 +1,147 @@ +import formsgSdk from 'src/config/formsg-sdk' +import { AuthType } from 'src/types' + +import { + EncryptVerifiedContentError, + MalformedVerifiedContentError, +} from '../verified-content.errors' +import { + encryptVerifiedContent, + getVerifiedContent, +} from '../verified-content.service' +import { CpVerifiedContent, SpVerifiedContent } from '../verified-content.types' + +describe('verified-content.service', () => { + describe('getVerifiedContent', () => { + it('should return verified content for AuthType.SP data', async () => { + // Arrange + const mockData = { + extraData: 'some extra data', + uinFin: 'S1234567Z', + } + // Only contain uinFin key. + const expected: SpVerifiedContent = { + uinFin: mockData['uinFin'], + } + + // Act + const result = getVerifiedContent({ type: AuthType.SP, data: mockData }) + + // Assert + expect(result._unsafeUnwrap()).toEqual(expected) + }) + + it('should return verified content for AuthType.CP data', async () => { + // Arrange + const mockData = { + extraData: 'some extra data again', + uinFin: 'S1234567Z', + userInfo: 'U987654323FORMSG', + } + const expected: CpVerifiedContent = { + cpUen: mockData['uinFin'], + cpUid: mockData['userInfo'], + } + + // Act + const result = getVerifiedContent({ type: AuthType.CP, data: mockData }) + + // Assert + expect(result._unsafeUnwrap()).toEqual(expected) + }) + + it('should return error if retrieved SP data does not fit the expected shape', async () => { + // Arrange + const mockDataWithoutUin = { + extraData: 'some data', + anotherExtraData: 'more useless data', + } + + // Act + const result = getVerifiedContent({ + type: AuthType.SP, + data: mockDataWithoutUin, + }) + + // Assert + expect(result._unsafeUnwrapErr()).toEqual( + new MalformedVerifiedContentError(), + ) + }) + + it('should return error if retrieved CP data does not fit the expected shape', async () => { + // Arrange + // Only contain userInfo and not uin + const mockDataWithoutUin = { + extraData: 'some data', + anotherExtraData: 'more useless data', + userInfo: 'GG238486828FORMSG', + } + + // Act + const result = getVerifiedContent({ + type: AuthType.CP, + data: mockDataWithoutUin, + }) + + // Assert + expect(result._unsafeUnwrapErr()).toEqual( + new MalformedVerifiedContentError(), + ) + }) + }) + + describe('encryptVerifiedContent', () => { + beforeEach(() => jest.clearAllMocks()) + + it('should successfully encrypt given verified content', async () => { + // Arrange + const mockVerifiedContent = { + cpUen: 'S1234567Z', + cpUid: 'U987654323FORMSG', + } + const mockEncryptedContent = 'abc-thisistotallyencrypted' + // Mock return value of formsg sdk encrypt. + const sdkSpy = jest + .spyOn(formsgSdk.crypto, 'encrypt') + .mockReturnValueOnce(mockEncryptedContent) + + // Act + const result = encryptVerifiedContent({ + verifiedContent: mockVerifiedContent, + formPublicKey: 'mockPublicKey', + signingSecretKey: 'mockSecretKey', + }) + + // Assert + expect(sdkSpy).toHaveBeenCalledTimes(1) + expect(result._unsafeUnwrap()).toEqual(mockEncryptedContent) + }) + + it('should return EncryptVerifiedContentError when encryption error occurs', async () => { + // Arrange + const mockVerifiedContent = { + uinFin: 'S1234567Z', + } + // Mock throw error in formsg sdk encrypt. + const sdkSpy = jest + .spyOn(formsgSdk.crypto, 'encrypt') + .mockImplementationOnce(() => { + throw new Error('some error') + }) + + // Act + const result = encryptVerifiedContent({ + verifiedContent: mockVerifiedContent, + formPublicKey: 'mockPublicKey', + signingSecretKey: 'mockSecretKey', + }) + + // Assert + expect(sdkSpy).toHaveBeenCalledTimes(1) + expect(result._unsafeUnwrapErr()).toEqual( + new EncryptVerifiedContentError(), + ) + }) + }) +}) diff --git a/src/app/modules/verified-content/verified-content.errors.ts b/src/app/modules/verified-content/verified-content.errors.ts new file mode 100644 index 0000000000..df44fad3b9 --- /dev/null +++ b/src/app/modules/verified-content/verified-content.errors.ts @@ -0,0 +1,19 @@ +import { ApplicationError } from '../core/core.errors' + +/** + * Verified content has the wrong shape + */ +export class MalformedVerifiedContentError extends ApplicationError { + constructor(message = 'Verified content is malformed') { + super(message) + } +} + +/** + * Error to be returned when verified content fails to be encrypted. + */ +export class EncryptVerifiedContentError extends ApplicationError { + constructor(message = 'Failed to encrypt verified content') { + super(message) + } +} diff --git a/src/app/modules/verified-content/verified-content.factory.ts b/src/app/modules/verified-content/verified-content.factory.ts new file mode 100644 index 0000000000..63344ff3aa --- /dev/null +++ b/src/app/modules/verified-content/verified-content.factory.ts @@ -0,0 +1,66 @@ +import { err } from 'neverthrow' + +import FeatureManager, { + FeatureNames, + RegisteredFeature, +} from '../../../config/feature-manager' +import { MissingFeatureError } from '../core/core.errors' + +import { + EncryptVerifiedContentError, + MalformedVerifiedContentError, +} from './verified-content.errors' +import * as VerifiedContentService from './verified-content.service' +import { + CpVerifiedContent, + EncryptVerificationContentParams, + FactoryVerifiedContentResult, + GetVerifiedContentParams, + SpVerifiedContent, +} from './verified-content.types' + +interface IVerifiedContentFactory { + getVerifiedContent: ( + params: GetVerifiedContentParams, + ) => FactoryVerifiedContentResult< + CpVerifiedContent | SpVerifiedContent, + MalformedVerifiedContentError + > + encryptVerifiedContent: ( + params: EncryptVerificationContentParams, + ) => FactoryVerifiedContentResult +} + +const verifiedContentFeature = FeatureManager.get( + FeatureNames.WebhookVerifiedContent, +) + +export const createVerifiedContentFactory = ({ + isEnabled, + props, +}: RegisteredFeature): IVerifiedContentFactory => { + // Feature is enabled and valid. + if (isEnabled && props?.signingSecretKey) { + return { + getVerifiedContent: VerifiedContentService.getVerifiedContent, + // Pass in signing secret key from feature manager. + encryptVerifiedContent: ({ verifiedContent, formPublicKey }) => + VerifiedContentService.encryptVerifiedContent({ + verifiedContent, + formPublicKey, + signingSecretKey: props.signingSecretKey, + }), + } + } + + return { + getVerifiedContent: () => + err(new MissingFeatureError(FeatureNames.WebhookVerifiedContent)), + encryptVerifiedContent: () => + err(new MissingFeatureError(FeatureNames.WebhookVerifiedContent)), + } +} + +export const VerifiedContentFactory = createVerifiedContentFactory( + verifiedContentFeature, +) diff --git a/src/app/modules/verified-content/verified-content.middlewares.ts b/src/app/modules/verified-content/verified-content.middlewares.ts new file mode 100644 index 0000000000..6820a1bff9 --- /dev/null +++ b/src/app/modules/verified-content/verified-content.middlewares.ts @@ -0,0 +1,69 @@ +import { RequestHandler } from 'express' +import { StatusCodes } from 'http-status-codes' + +import { createLoggerWithLabel } from '../../../config/logger' +import { AuthType, WithForm } from '../../../types' +import { createReqMeta } from '../../utils/request' +import { MissingFeatureError } from '../core/core.errors' +import { isFormEncryptMode } from '../form/form.utils' + +import { VerifiedContentFactory } from './verified-content.factory' + +const logger = createLoggerWithLabel(module) + +export const encryptVerifiedSpcpFields: RequestHandler = (req, res, next) => { + const { form } = req as WithForm + + // Early return if this is not a Singpass/Corppass submission. + if (form.authType === AuthType.NIL) { + return next() + } + + const logMeta = { + action: 'encryptVerifiedSpcpFields', + formId: form._id, + ...createReqMeta(req), + } + + if (!isFormEncryptMode(form)) { + logger.error({ + message: 'encryptVerifiedSpcpFields called on non-encrypt mode form', + meta: logMeta, + }) + return res.status(StatusCodes.UNPROCESSABLE_ENTITY).send({ + message: + 'Unable to encrypt verified SPCP fields on non storage mode forms', + }) + } + + const encryptVerifiedContentResult = VerifiedContentFactory.getVerifiedContent( + { type: form.authType, data: res.locals }, + ).andThen((verifiedContent) => + VerifiedContentFactory.encryptVerifiedContent({ + verifiedContent, + formPublicKey: form.publicKey, + }), + ) + + if (encryptVerifiedContentResult.isErr()) { + const { error } = encryptVerifiedContentResult + logger.error({ + message: 'Unable to encrypt verified content', + meta: logMeta, + error, + }) + + // Passthrough if feature is not activated. + if (error instanceof MissingFeatureError) { + return next() + } + + return res + .status(StatusCodes.BAD_REQUEST) + .json({ message: 'Invalid data was found. Please submit again.' }) + } + + // No errors, set local variable to the encrypted string. + res.locals.verified = encryptVerifiedContentResult.value + return next() +} diff --git a/src/app/modules/verified-content/verified-content.service.ts b/src/app/modules/verified-content/verified-content.service.ts new file mode 100644 index 0000000000..a137423133 --- /dev/null +++ b/src/app/modules/verified-content/verified-content.service.ts @@ -0,0 +1,66 @@ +import { err, ok, Result } from 'neverthrow' + +import formsgSdk from '../../../config/formsg-sdk' +import { createLoggerWithLabel } from '../../../config/logger' +import { AuthType } from '../../../types' + +import { EncryptVerifiedContentError } from './verified-content.errors' +import { + CpVerifiedContent, + EncryptVerificationContentParams, + GetVerifiedContentParams, + SpVerifiedContent, + VerifiedContentResult, +} from './verified-content.types' +import { + getCpVerifiedContent, + getSpVerifiedContent, +} from './verified-content.utils' + +const logger = createLoggerWithLabel(module) + +/** + * Extracts and returns verified content from given type and data. + * @returns ok(CpVerifiedContent | SpVerifiedContent) if extracted content is valid and confirms to expected shape. + * @returns err(MalformedVerifiedContentError) if extracted shape mismatches. + */ +export const getVerifiedContent = ({ + type, + data, +}: GetVerifiedContentParams): VerifiedContentResult< + CpVerifiedContent | SpVerifiedContent +> => { + switch (type) { + case AuthType.SP: + return getSpVerifiedContent(data) + case AuthType.CP: + return getCpVerifiedContent(data) + } +} + +export const encryptVerifiedContent = ({ + verifiedContent, + formPublicKey, + signingSecretKey, +}: EncryptVerificationContentParams & { + signingSecretKey: string +}): Result => { + try { + const encryptedContent = formsgSdk.crypto.encrypt( + verifiedContent, + formPublicKey, + signingSecretKey, + ) + return ok(encryptedContent) + } catch (error) { + logger.error({ + message: 'Unable to encrypt verified content', + meta: { + action: 'encryptVerifiedContent', + }, + error, + }) + + return err(new EncryptVerifiedContentError()) + } +} diff --git a/src/app/modules/verified-content/verified-content.types.ts b/src/app/modules/verified-content/verified-content.types.ts new file mode 100644 index 0000000000..93a05553af --- /dev/null +++ b/src/app/modules/verified-content/verified-content.types.ts @@ -0,0 +1,43 @@ +import { Result } from 'neverthrow' + +import { VerifiedKeys } from '../../../shared/util/verified-content' +import { AuthType } from '../../../types' +import { MissingFeatureError } from '../core/core.errors' + +import { MalformedVerifiedContentError } from './verified-content.errors' + +export type SpVerifiedKeys = { + uinFin: VerifiedKeys.SpUinFin +} + +export type ICpVerifiedKeys = { + uinFin: VerifiedKeys.CpUen + userInfo: VerifiedKeys.CpUid +} + +export type VerifiedKeyMap = SpVerifiedKeys | ICpVerifiedKeys + +export type CpVerifiedContent = { + [VerifiedKeys.CpUen]: string + [VerifiedKeys.CpUid]: string +} + +export type SpVerifiedContent = { + [VerifiedKeys.SpUinFin]: string +} + +export type VerifiedContentResult = Result +export type FactoryVerifiedContentResult = Result< + T, + E | MissingFeatureError +> + +export type EncryptVerificationContentParams = { + verifiedContent: CpVerifiedContent | SpVerifiedContent + formPublicKey: string +} + +export type GetVerifiedContentParams = { + type: Exclude + data: Record +} diff --git a/src/app/modules/verified-content/verified-content.utils.ts b/src/app/modules/verified-content/verified-content.utils.ts new file mode 100644 index 0000000000..c53498ed58 --- /dev/null +++ b/src/app/modules/verified-content/verified-content.utils.ts @@ -0,0 +1,77 @@ +import { err, ok } from 'neverthrow' + +import { VerifiedKeys } from '../../../shared/util/verified-content' + +import { MalformedVerifiedContentError } from './verified-content.errors' +import { + CpVerifiedContent, + SpVerifiedContent, + VerifiedContentResult, +} from './verified-content.types' + +/** + * Typeguard to assert that the given data has the shape of `CpVerifiedContent`. + */ +const isCpVerifiedContent = ( + data: Record, +): data is CpVerifiedContent => { + const cpKeys: (keyof CpVerifiedContent)[] = [ + VerifiedKeys.CpUen, + VerifiedKeys.CpUid, + ] + return cpKeys.every( + (cpKey) => cpKey in data && typeof data[cpKey] === 'string', + ) +} + +/** + * Typeguard to assert that the given data has the shape of `SpVerifiedContent`. + */ +const isSpVerifiedContent = ( + data: Record, +): data is SpVerifiedContent => { + return typeof data[VerifiedKeys.SpUinFin] === 'string' +} + +/** + * Retrieve CorpPass verified content object from given data. + * @param data the data to retrieve the verified content object from + * @returns ok(verified content object) if retrieved object is of valid expected shape + * @returns err(MalformedVerifiedContentError) if object cannot be retrieved + */ +export const getCpVerifiedContent = ( + data: Record, +): VerifiedContentResult => { + // Create new CorpPass verifiedContent object from current data. + // Extract value of data.uinFin and data.userInfo set to their respective new keys. + const createdCpVerifiedContent = { + [VerifiedKeys.CpUen]: data.uinFin, + [VerifiedKeys.CpUid]: data.userInfo, + } + + // Check if the newly created object is of expected shape. + return isCpVerifiedContent(createdCpVerifiedContent) + ? ok(createdCpVerifiedContent) + : err(new MalformedVerifiedContentError()) +} + +/** + * Retrieve SingPass verified content object from given data. + * @param data the data to retrieve the verified content object from + * @returns ok(verified content object) if retrieved object is of valid expected shape + * @returns err(MalformedVerifiedContentError) if object cannot be retrieved + */ +export const getSpVerifiedContent = ( + data: Record, +): VerifiedContentResult => { + // Create new SingPass verifiedContent object from current data. + // Extract value of data.uinFin set to new VerifiedKeys.SpUinFin key. + const createdSpVerifiedContent = { + [VerifiedKeys.SpUinFin]: data.uinFin, + } + + // Check if the newly created object is of expected shape. + return isSpVerifiedContent(createdSpVerifiedContent) + ? ok(createdSpVerifiedContent) + : err(new MalformedVerifiedContentError()) +} diff --git a/src/app/routes/admin-forms.server.routes.js b/src/app/routes/admin-forms.server.routes.js index 22405c9866..8c5a6e283d 100644 --- a/src/app/routes/admin-forms.server.routes.js +++ b/src/app/routes/admin-forms.server.routes.js @@ -10,7 +10,6 @@ let adminForms = require('../../app/controllers/admin-forms.server.controller') let auth = require('../../app/controllers/authentication.server.controller') const EmailSubmissionsMiddleware = require('../../app/modules/submission/email-submission/email-submission.middleware') const SubmissionsMiddleware = require('../../app/modules/submission/submission.middleware') -const webhookVerifiedContentFactory = require('../factories/webhook-verified-content.factory') const AdminFormController = require('../modules/form/admin-form/admin-form.controller') const { withUserAuthentication } = require('../modules/auth/auth.middlewares') const EncryptSubmissionController = require('../modules/submission/encrypt-submission/encrypt-submission.controller') @@ -20,6 +19,7 @@ const { const EncryptSubmissionMiddleware = require('../modules/submission/encrypt-submission/encrypt-submission.middleware') const SpcpController = require('../modules/spcp/spcp.controller') const { BasicField, ResponseMode } = require('../../types') +const VerifiedContentMiddleware = require('../modules/verified-content/verified-content.middlewares') const YYYY_MM_DD_REGEX = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/ @@ -522,7 +522,7 @@ module.exports = function (app) { authActiveForm(PermissionLevel.Read), EncryptSubmissionMiddleware.validateAndProcessEncryptSubmission, AdminFormController.passThroughSpcp, - webhookVerifiedContentFactory.encryptedVerifiedFields, + VerifiedContentMiddleware.encryptVerifiedSpcpFields, EncryptSubmissionMiddleware.prepareEncryptSubmission, adminForms.passThroughSaveMetadataToDb, SubmissionsMiddleware.sendEmailConfirmations, diff --git a/src/app/routes/public-forms.server.routes.js b/src/app/routes/public-forms.server.routes.js index c3156bba85..2bd6ab8f91 100644 --- a/src/app/routes/public-forms.server.routes.js +++ b/src/app/routes/public-forms.server.routes.js @@ -18,6 +18,7 @@ const PublicFormController = require('../modules/form/public-form/public-form.co const SpcpController = require('../modules/spcp/spcp.controller') const { BasicField } = require('../../types') const EncryptSubmissionMiddleware = require('../modules/submission/encrypt-submission/encrypt-submission.middleware') +const VerifiedContentMiddleware = require('../modules/verified-content/verified-content.middlewares') module.exports = function (app) { /** * Redirect a form to the main index, with the specified path @@ -210,7 +211,7 @@ module.exports = function (app) { EncryptSubmissionMiddleware.validateAndProcessEncryptSubmission, SpcpController.isSpcpAuthenticated, myInfoController.verifyMyInfoVals, - webhookVerifiedContentFactory.encryptedVerifiedFields, + VerifiedContentMiddleware.encryptVerifiedSpcpFields, EncryptSubmissionMiddleware.prepareEncryptSubmission, encryptSubmissions.saveResponseToDb, webhookVerifiedContentFactory.post, diff --git a/src/app/services/myinfo/__tests__/myinfo.test.constants.ts b/src/app/services/myinfo/__tests__/myinfo.test.constants.ts index 15a7491b0d..3585e09904 100644 --- a/src/app/services/myinfo/__tests__/myinfo.test.constants.ts +++ b/src/app/services/myinfo/__tests__/myinfo.test.constants.ts @@ -18,7 +18,7 @@ export const MOCK_MYINFO_DATA = { classification: 'C', nbr: '97324992', }, - mailadd: { + regadd: { country: 'US', unit: '', street: '5TH AVENUE', @@ -39,14 +39,6 @@ export const MOCK_MYINFO_DATA = { } export const MOCK_MYINFO_FORMAT_DATA = { - homeno: { - code: '65', - prefix: '+', - lastupdated: '2017-11-20', - source: '2', - classification: 'C', - nbr: '66132665', - }, mobileno: { code: '65', prefix: '+', @@ -67,30 +59,6 @@ export const MOCK_MYINFO_FORMAT_DATA = { floor: '09', building: '', }, - mailadd: { - country: 'US', - unit: '', - street: '5TH AVENUE', - lastupdated: '2016-03-11', - block: '725', - source: '2', - postal: 'NY 10022', - classification: 'C', - floor: '', - building: 'TRUMP TOWER', - }, - billadd: { - country: 'SG', - street: 'SERANGOON AVE 3', - lastupdated: '2016-03-11', - block: '329', - source: '1', - postal: '550329', - classification: 'C', - floor: '09', - unit: '360', - building: '', - }, workpassexpirydate: { lastupdated: '2018-03-02', source: '1', @@ -116,13 +84,7 @@ export const MOCK_FORM_FIELDS = [ { fieldType: 'textfield', isVisible: true, - myInfo: { attr: 'homeno' }, - _id: new ObjectId().toHexString(), - }, - { - fieldType: 'textfield', - isVisible: true, - myInfo: { attr: 'mailadd' }, + myInfo: { attr: 'regadd' }, _id: new ObjectId().toHexString(), }, // Some non-MyInfo fields @@ -133,7 +95,7 @@ export const MOCK_FORM_FIELDS = [ export const MOCK_HASHES = { name: 'name', mobileno: 'mobileno', - mailadd: 'mailadd', + regadd: 'regadd', } // Based on MOCK_FORM_FIELDS and MOCK_HASHES, only expect these attributes to @@ -141,14 +103,13 @@ export const MOCK_HASHES = { // and homeno does not match because it does not have a hash. export const MOCK_HASHED_FIELD_IDS = new Set( MOCK_FORM_FIELDS.filter( - (field) => field.myInfo && ['name', 'mailadd'].includes(field.myInfo?.attr), + (field) => field.myInfo && ['name', 'regadd'].includes(field.myInfo?.attr), ).map((field) => field._id), ) const populatedValues = [ { fieldValue: 'TAN XIAO HUI', disabled: true }, { fieldValue: '+65 97324992', disabled: false }, - { fieldValue: '', disabled: false }, { fieldValue: 'TRUMP TOWER, 725 5TH AVENUE, UNITED STATES NY 10022', disabled: false, diff --git a/src/app/services/myinfo/__tests__/myinfo.util.spec.ts b/src/app/services/myinfo/__tests__/myinfo.util.spec.ts index 72e9f95fb0..c967539411 100644 --- a/src/app/services/myinfo/__tests__/myinfo.util.spec.ts +++ b/src/app/services/myinfo/__tests__/myinfo.util.spec.ts @@ -46,23 +46,6 @@ describe('myinfo.util', () => { }) describe('Phone numbers', () => { - it('should correctly return formatted home phone numbers if valid', () => { - // Arrange - // code: '65', - // prefix: '+', - // nbr: '66132665', - const expected = '+65 66132665' - - // Act - const actual = getMyInfoValue( - 'homeno' as MyInfoAttribute.HomeNo, - (MOCK_MYINFO_FORMAT_DATA as unknown) as IPersonBasic, - ) - - // Assert - expect(actual).toEqual(expected) - }) - it('should correctly return formatted mobile phone numbers if valid', () => { // Arrange // code: '65', @@ -102,60 +85,16 @@ describe('myinfo.util', () => { expect(actual).toEqual(expected) }) - it('should correctly return formatted mailing addresses', () => { - // Arrange - // country: 'US', - // unit: '', - // street: '5TH AVENUE', - // lastupdated: '2016-03-11', - // block: '725', - // source: '2', - // postal: 'NY 10022', - // classification: 'C', - // floor: '', - // building: 'TRUMP TOWER', - const expected = 'TRUMP TOWER, 725 5TH AVENUE, UNITED STATES NY 10022' - - // Act - const actual = getMyInfoValue( - 'mailadd' as MyInfoAttribute.MailingAddress, - (MOCK_MYINFO_FORMAT_DATA as unknown) as IPersonBasic, - ) - - // Assert - expect(actual).toEqual(expected) - }) - - it('should correctly return formatted billing addresses', () => { - // Arrange - // country: 'SG', - // street: 'SERANGOON AVE 3', - // block: '329', - // postal: '550329', - // floor: '09', - // unit: '360', - const expected = '329 SERANGOON AVE 3, #09-360, SINGAPORE 550329' - - // Act - const actual = getMyInfoValue( - 'billadd' as MyInfoAttribute.BillingAddress, - (MOCK_MYINFO_FORMAT_DATA as unknown) as IPersonBasic, - ) - - // Assert - expect(actual).toEqual(expected) - }) - it('should return empty string if address missing one of [block, street, country, postal] keys', () => { // Arrange - const mockBillAdd = cloneDeep(MOCK_MYINFO_FORMAT_DATA.billadd) - mockBillAdd.block = '' + const mockAddress = cloneDeep(MOCK_MYINFO_FORMAT_DATA.regadd) + mockAddress.block = '' // Act const actual = getMyInfoValue( - 'billadd' as MyInfoAttribute.BillingAddress, + 'regadd' as MyInfoAttribute.RegisteredAddress, ({ - billAdd: mockBillAdd, + regAdd: mockAddress, } as unknown) as IPersonBasic, ) diff --git a/src/app/services/myinfo/myinfo.util.ts b/src/app/services/myinfo/myinfo.util.ts index 8bf1b6769a..d7ca6bcfd1 100644 --- a/src/app/services/myinfo/myinfo.util.ts +++ b/src/app/services/myinfo/myinfo.util.ts @@ -54,11 +54,8 @@ export const getMyInfoValue = ( switch (myInfoAttr) { // Phone numbers case MyInfoAttribute.MobileNo: - case MyInfoAttribute.HomeNo: return formatPhoneNumber(myInfoData[myInfoAttr]) case MyInfoAttribute.RegisteredAddress: - case MyInfoAttribute.BillingAddress: - case MyInfoAttribute.MailingAddress: return formatAddress(myInfoData[myInfoAttr]) default: // Categorical data lookup diff --git a/src/app/services/sms/sms.service.ts b/src/app/services/sms/sms.service.ts index 19eb5b45fe..22bc872fa1 100644 --- a/src/app/services/sms/sms.service.ts +++ b/src/app/services/sms/sms.service.ts @@ -1,4 +1,5 @@ import { SecretsManager } from 'aws-sdk' +import dedent from 'dedent-js' import mongoose from 'mongoose' import { ResultAsync } from 'neverthrow' import NodeCache from 'node-cache' @@ -274,7 +275,11 @@ export const sendVerificationOtp = async ( } const twilioData = await getTwilio(otpData.msgSrvcName, defaultConfig) - const message = `Use the OTP ${otp} to complete your submission on ${config.app.title}.` + const message = dedent`Use the OTP ${otp} to complete your submission on ${ + new URL(config.app.appUrl).host + }. + + If you did not request this OTP, do not share the OTP with anyone else. You can safely ignore this message.` return send(twilioData, otpData, recipient, message, SmsType.Verification) } diff --git a/src/app/utils/field-validation/validators/mobileNoValidator.ts b/src/app/utils/field-validation/validators/mobileNoValidator.ts index 1637882ffe..3fa9ec81b7 100644 --- a/src/app/utils/field-validation/validators/mobileNoValidator.ts +++ b/src/app/utils/field-validation/validators/mobileNoValidator.ts @@ -2,9 +2,10 @@ import { chain, left, right } from 'fp-ts/lib/Either' import { flow } from 'fp-ts/lib/function' import { ProcessedSingleAnswerResponse } from 'src/app/modules/submission/submission.types' -import { IMobileField } from 'src/types/field' +import { IMobileFieldSchema } from 'src/types/field' import { ResponseValidator } from 'src/types/field/utils/validation' +import formsgSdk from '../../../../config/formsg-sdk' import { isMobilePhoneNumber, startsWithSgPrefix, @@ -14,7 +15,7 @@ import { notEmptySingleAnswerResponse } from './common' type MobileNoValidator = ResponseValidator type MobileNoValidatorConstructor = ( - mobileNumberField: IMobileField, + mobileNumberField: IMobileFieldSchema, ) => MobileNoValidator const mobilePhoneNumberValidator: MobileNoValidator = (response) => { @@ -37,11 +38,37 @@ const makePrefixValidator: MobileNoValidatorConstructor = ( return mobileNumberField.allowIntlNumbers ? right : sgPrefixValidator } +const makeMobileSignatureValidator: MobileNoValidatorConstructor = ( + mobileNumberField, +) => (response) => { + const { isVerifiable, _id } = mobileNumberField + if (!isVerifiable) { + return right(response) // no validation occurred + } + const { signature, answer } = response + if (!signature) { + return left(`MobileNoValidator:\t answer does not have valid signature`) + } + const isSigned = + formsgSdk.verification.authenticate && + formsgSdk.verification.authenticate({ + signatureString: signature, + submissionCreatedAt: Date.now(), + fieldId: _id, + answer, + }) + + return isSigned + ? right(response) + : left(`MobileNoValidator:\t answer does not have valid signature`) +} + export const constructMobileNoValidator: MobileNoValidatorConstructor = ( mobileNumberField, ) => flow( notEmptySingleAnswerResponse, chain(mobilePhoneNumberValidator), + chain(makeMobileSignatureValidator(mobileNumberField)), chain(makePrefixValidator(mobileNumberField)), ) diff --git a/src/config/ndi-config.ts b/src/config/ndi-config.ts index bea519aad2..823d443e46 100644 --- a/src/config/ndi-config.ts +++ b/src/config/ndi-config.ts @@ -12,11 +12,9 @@ export const FIELD_MAPPING = { 'housingtype', 'hdbtype', 'marital', - 'edulevel', 'countryofmarriage', 'workpassstatus', 'householdincome', - 'schoolname', 'occupation', ], textfield: [ @@ -27,14 +25,11 @@ export const FIELD_MAPPING = { 'hanyupinyinaliasname', 'passportnumber', 'regadd', - 'mailadd', - 'billadd', 'employment', 'vehno', 'marriagecertno', ], mobile: ['mobileno'], - homeno: ['homeno'], date: [ 'dob', 'passportexpirydate', @@ -42,5 +37,4 @@ export const FIELD_MAPPING = { 'divorcedate', 'workpassexpirydate', ], - number: ['gradyear'], } diff --git a/src/public/modules/forms/base/directives/field.client.directive.js b/src/public/modules/forms/base/directives/field.client.directive.js index b62789db61..15fdfb1214 100644 --- a/src/public/modules/forms/base/directives/field.client.directive.js +++ b/src/public/modules/forms/base/directives/field.client.directive.js @@ -57,8 +57,6 @@ function fieldDirective(FormFields) { function _getMyInfoPlaceholder(myInfoAttr, readOnly) { if (readOnly) { return '' - } else if (myInfoAttr === 'homeno') { - return '+65 6XXXXXXX' } else if (myInfoAttr === 'mobileno') { return '+65 9XXXXXXX' } diff --git a/src/public/modules/forms/base/resources/icon-types.js b/src/public/modules/forms/base/resources/icon-types.js index 605bfa88ae..bdcdfa28c9 100644 --- a/src/public/modules/forms/base/resources/icon-types.js +++ b/src/public/modules/forms/base/resources/icon-types.js @@ -29,7 +29,6 @@ const iconTypeMap = { housingtype: 'bx bx-home-alt', hdbtype: 'bx bx-home', marital: 'bx bx-diamond', - edulevel: 'bx bx-book-bookmark', countryofmarriage: 'bx bx-globe-alt', workpassstatus: 'bx bx-task', // householdincome: "", @@ -40,9 +39,6 @@ const iconTypeMap = { // secondaryrace: "", passportnumber: 'bx bx-world', regadd: 'bx bx-building', - mailadd: 'bx bx-newsletter', - billadd: 'bx bx-wallet', - schoolname: 'bx bx-book-open', occupation: 'bx bx-briefcase', employment: 'bx bx-contact', vehno: 'bx bx-car', @@ -54,7 +50,6 @@ const iconTypeMap = { homeno: 'bx bx-phone', mobileno: 'bx bx-mobile-alt', marriagecertno: 'bx bx-award', - gradyear: 'bx bx-calendar', } module.exports = { diff --git a/src/public/modules/forms/services/form-fields.client.service.js b/src/public/modules/forms/services/form-fields.client.service.js index 5a202e7b5c..aa8efee5a2 100644 --- a/src/public/modules/forms/services/form-fields.client.service.js +++ b/src/public/modules/forms/services/form-fields.client.service.js @@ -181,23 +181,13 @@ function FormFields() { case 'passportnumber': return 'E1234567X' case 'regadd': - case 'mailadd': - case 'billadd': return '411 CHUA CHU KANG AVE 3, #12-3, SINGAPORE 238823' case 'mobileno': return '+65 98765432' - case 'homeno': - return '+65 65123456' - case 'edulevel': - return 'PRIMARY' - case 'schoolname': - return 'TECK WHYE PRIMARY SCHOOL' case 'occupation': return 'MANAGING DIRECTOR/CHIEF EXECUTIVE OFFICER' case 'employment': return 'PCK PTE LTD' - case 'gradyear': - return 1977 case 'marital': return 'MARRIED' case 'dob': diff --git a/src/public/modules/forms/viewmodels/Fields/SingleAnswerField.class.js b/src/public/modules/forms/viewmodels/Fields/SingleAnswerField.class.js index 497bbb3427..e9a4f677b3 100644 --- a/src/public/modules/forms/viewmodels/Fields/SingleAnswerField.class.js +++ b/src/public/modules/forms/viewmodels/Fields/SingleAnswerField.class.js @@ -24,6 +24,7 @@ class SingleAnswerField extends AnswerField { this.isVisible && this.required && this.fieldType !== 'section' && + this.fieldValue !== 0 && !this.fieldValue ) { console.error( diff --git a/src/shared/resources/myinfo/index.ts b/src/shared/resources/myinfo/index.ts index 9e1fbf869b..6e27ba8b76 100644 --- a/src/shared/resources/myinfo/index.ts +++ b/src/shared/resources/myinfo/index.ts @@ -5,7 +5,6 @@ import DIALECTS from './myinfo-dialects' import NATIONALITIES from './myinfo-nationalities' import OCCUPATIONS from './myinfo-occupations' import RACES from './myinfo-races' -import SCHOOLS from './myinfo-schools' type MyInfoVerifiedType = 'SG' | 'PR' | 'F' interface IMyInfoFieldType { @@ -20,14 +19,7 @@ interface IMyInfoFieldType { ValidationOptions?: Record } -// TODO: Enable more MyInfo fields. export const types: IMyInfoFieldType[] = [ - // Email fieldType - // { - // name: 'email', - // value: 'Email', - // fieldType: 'email' - // }, { name: MyInfoAttribute.Name, value: 'Name', @@ -92,12 +84,6 @@ export const types: IMyInfoFieldType[] = [ fieldType: BasicField.Dropdown, fieldOptions: COUNTRIES, }, - // { - // name: 'secondaryrace', - // value: 'Race (Secondary)', - // category: "personal", - // fieldType: BasicField.Dropdown - // }, { name: MyInfoAttribute.ResidentialStatus, value: 'Residential Status', @@ -183,28 +169,6 @@ export const types: IMyInfoFieldType[] = [ fieldType: BasicField.Dropdown, fieldOptions: ['SINGLE', 'MARRIED', 'WIDOWED', 'DIVORCED'], }, - { - name: MyInfoAttribute.EducationLevel, - value: 'Highest education', - category: 'employment_education', - verified: [], - source: 'User-provided', - description: 'Highest education level of form-filler.', - fieldType: BasicField.Dropdown, - fieldOptions: [ - 'NO FORMAL QUALIFICATION / PRE-PRIMARY / LOWER PRIMARY', - 'PRIMARY', - 'LOWER SECONDARY', - 'SECONDARY', - 'POST-SECONDARY (NON-TERTIARY): GENERAL & VOCATION', - 'POLYTECHNIC DIPLOMA', - 'PROFESSIONAL QUALIFICATION AND OTHER DIPLOMA', - "BACHELOR'S OR EQUIVALENT", - "POSTGRADUATE DIPLOMA / CERTIFICATE (EXCLUDING MASTER'S AND DOCTORATE)", - "MASTER'S AND DOCTORATE OR EQUIVALENT", - 'MODULAR CERTIFICATION (NON-AWARD COURSES / NON-FULL QUALIFICATIONS)', - ], - }, { name: MyInfoAttribute.CountryOfMarriage, value: 'Country of marriage', @@ -216,39 +180,6 @@ export const types: IMyInfoFieldType[] = [ fieldType: BasicField.Dropdown, fieldOptions: COUNTRIES, }, - // { - // name: 'householdincome', - // value: 'Household Income', - // category: "employment_education", - // verified: [], - // source: "User-provided" - // description: "", - // fieldType: BasicField.Dropdown, - // }, - // { - // name: 'marriedname', - // value: 'Married Name', - // category: "family", - // fieldType: 'textfield' - // }, - // { - // name: 'hanyupinyinname', - // value: 'Hanyu Pinyin Name', - // category: "personal", - // fieldType: 'textfield' - // }, - // { - // name: 'aliasname', - // value: 'Alias Name', - // category: "personal", - // fieldType: 'textfield' - // }, - // { - // name: 'hanyupinyinaliasname', - // value: 'Hanyu Pinyin Alias Name', - // category: "personal", - // fieldType: 'textfield' - // }, { name: MyInfoAttribute.RegisteredAddress, value: 'Registered address', @@ -258,35 +189,6 @@ export const types: IMyInfoFieldType[] = [ description: 'The registered address of the form-filler.', fieldType: BasicField.ShortText, }, - { - name: MyInfoAttribute.MailingAddress, - value: 'Mailing address', - category: 'contact', - verified: [], - source: 'User-provided', - description: 'The mailing address of the form-filler.', - fieldType: BasicField.ShortText, - }, - { - name: MyInfoAttribute.BillingAddress, - value: 'Billing address', - category: 'contact', - verified: [], - source: 'User-provided', - description: 'The billing address of the form-filler.', - fieldType: BasicField.ShortText, - }, - { - name: MyInfoAttribute.SchoolName, - value: 'School name', - category: 'employment_education', - verified: [], - source: 'User-provided', - description: - 'List of primary, secondary and tertiary educational institutions in Singapore. Does not include private or international educational institutions.', - fieldType: BasicField.Dropdown, - fieldOptions: SCHOOLS, - }, { name: MyInfoAttribute.Occupation, value: 'Occupation', @@ -375,29 +277,4 @@ export const types: IMyInfoFieldType[] = [ description: 'Mobile telephone number of form-filler.', fieldType: BasicField.Mobile, }, - { - name: MyInfoAttribute.HomeNo, - value: 'Home number', - category: 'contact', - verified: [], - source: 'User-provided', - description: 'Home telephone number of form-filler.', - fieldType: BasicField.HomeNo, - }, - { - name: MyInfoAttribute.GraduationYear, - value: 'Year of graduation', - category: 'employment_education', - verified: [], - source: 'User-provided', - description: - "Graduation year of form filler's last attended educational institution.", - fieldType: BasicField.Number, - ValidationOptions: { - selectedValidation: 'Exact', - customVal: 4, - customMin: 4, - customMax: 4, - }, - }, ] diff --git a/src/shared/resources/myinfo/myinfo-schools.ts b/src/shared/resources/myinfo/myinfo-schools.ts deleted file mode 100644 index 386427d00e..0000000000 --- a/src/shared/resources/myinfo/myinfo-schools.ts +++ /dev/null @@ -1,387 +0,0 @@ -const myInfoSchools = [ - 'ADMIRALTY PRIMARY SCHOOL', - 'ADMIRALTY SECONDARY SCHOOL', - 'AHMAD IBRAHIM PRIMARY SCHOOL', - 'AHMAD IBRAHIM SECONDARY SCHOOL', - 'AI TONG SCHOOL', - 'ALEXANDRA PRIMARY SCHOOL', - 'ANCHOR GREEN PRIMARY SCHOOL', - 'ANDERSON JUNIOR COLLEGE', - 'ANDERSON PRIMARY SCHOOL', - 'ANDERSON SECONDARY SCHOOL', - 'ANG MO KIO PRIMARY SCHOOL', - 'ANG MO KIO SECONDARY SCHOOL', - 'ANGLICAN HIGH SCHOOL', - 'ANGLO-CHINESE JUNIOR COLLEGE', - 'ANGLO-CHINESE SCHOOL (BARKER ROAD)', - 'ANGLO-CHINESE SCHOOL (INDEPENDENT)', - 'ANGLO-CHINESE SCHOOL (JUNIOR)', - 'ANGLO-CHINESE SCHOOL (PRIMARY)', - 'ASSUMPTION ENGLISH SCHOOL', - 'ASSUMPTION PATHWAY SCHOOL', - 'BALESTIER HILL PRIMARY SCHOOL', - 'BALESTIER HILL SECONDARY SCHOOL', - 'BARTLEY SECONDARY SCHOOL', - 'BEACON PRIMARY SCHOOL', - 'BEATTY SECONDARY SCHOOL', - 'BEDOK GREEN PRIMARY SCHOOL', - 'BEDOK GREEN SECONDARY SCHOOL', - 'BEDOK NORTH SECONDARY SCHOOL', - 'BEDOK SOUTH SECONDARY SCHOOL', - 'BEDOK TOWN SECONDARY SCHOOL', - 'BEDOK VIEW SECONDARY SCHOOL', - 'BENDEMEER PRIMARY SCHOOL', - 'BENDEMEER SECONDARY SCHOOL', - 'BISHAN PARK SECONDARY SCHOOL', - 'BLANGAH RISE PRIMARY SCHOOL', - 'BOON LAY GARDEN PRIMARY SCHOOL', - 'BOON LAY SECONDARY SCHOOL', - 'BOWEN SECONDARY SCHOOL', - 'BROADRICK SECONDARY SCHOOL', - 'BUKIT BATOK SECONDARY SCHOOL', - 'BUKIT MERAH SECONDARY SCHOOL', - 'BUKIT PANJANG GOVT. HIGH SCHOOL', - 'BUKIT PANJANG PRIMARY SCHOOL', - 'BUKIT TIMAH PRIMARY SCHOOL', - 'BUKIT VIEW PRIMARY SCHOOL', - 'BUKIT VIEW SECONDARY SCHOOL', - 'CANBERRA PRIMARY SCHOOL', - 'CANBERRA SECONDARY SCHOOL', - 'CANOSSA CONVENT PRIMARY SCHOOL', - 'CANTONMENT PRIMARY SCHOOL', - 'CASUARINA PRIMARY SCHOOL', - 'CATHOLIC HIGH SCHOOL', - 'CATHOLIC JUNIOR COLLEGE', - "CEDAR GIRLS' SECONDARY SCHOOL", - 'CEDAR PRIMARY SCHOOL', - 'CG PROTEGE ANIMATION SCHOOL', - 'CHANGKAT CHANGI SECONDARY SCHOOL', - 'CHANGKAT PRIMARY SCHOOL', - 'CHESTNUT DRIVE SECONDARY SCHOOL', - 'CHIJ (KATONG) PRIMARY', - 'CHIJ (KELLOCK)', - 'CHIJ KATONG CONVENT', - 'CHIJ OUR LADY OF GOOD COUNSEL', - 'CHIJ OUR LADY OF THE NATIVITY', - 'CHIJ OUR LADY QUEEN OF PEACE', - 'CHIJ PRIMARY (TOA PAYOH)', - 'CHIJ SECONDARY (TOA PAYOH)', - "CHIJ ST. JOSEPH'S CONVENT", - "CHIJ ST. NICHOLAS GIRLS' SCHOOL", - "CHIJ ST. THERESA'S CONVENT", - 'CHONG BOON SECONDARY SCHOOL', - 'CHONGFU PRIMARY SCHOOL', - 'CHONGZHENG PRIMARY SCHOOL', - 'CHRIST CHURCH SECONDARY SCHOOL', - 'CHUA CHU KANG PRIMARY SCHOOL', - 'CHUA CHU KANG SECONDARY SCHOOL', - 'CHUNG CHENG HIGH SCHOOL (MAIN)', - 'CHUNG CHENG HIGH SCHOOL (YISHUN)', - 'CLEMENTI PRIMARY SCHOOL', - 'CLEMENTI TOWN SECONDARY SCHOOL', - 'CLEMENTI WOODS SECONDARY SCHOOL', - 'COMMONWEALTH SECONDARY SCHOOL', - 'COMPASSVALE PRIMARY SCHOOL', - 'COMPASSVALE SECONDARY SCHOOL', - 'CONCORD PRIMARY SCHOOL', - 'CORAL PRIMARY SCHOOL', - 'CORAL SECONDARY SCHOOL', - 'CORPORATION PRIMARY SCHOOL', - "CRESCENT GIRLS' SCHOOL", - 'CREST SECONDARY SCHOOL', - 'DA QIAO PRIMARY SCHOOL', - 'DAMAI PRIMARY SCHOOL', - 'DAMAI SECONDARY SCHOOL', - 'DAZHONG PRIMARY SCHOOL', - 'DE LA SALLE SCHOOL', - 'DEYI SECONDARY SCHOOL', - 'DUNEARN SECONDARY SCHOOL', - 'DUNMAN HIGH SCHOOL', - 'DUNMAN SECONDARY SCHOOL', - 'EAST COAST PRIMARY SCHOOL', - 'EAST SPRING PRIMARY SCHOOL', - 'EAST SPRING SECONDARY SCHOOL', - 'EAST VIEW PRIMARY SCHOOL', - 'EAST VIEW SECONDARY SCHOOL', - 'EDGEFIELD PRIMARY SCHOOL', - 'EDGEFIELD SECONDARY SCHOOL', - 'ELIAS PARK PRIMARY SCHOOL', - 'ENDEAVOUR PRIMARY SCHOOL', - 'EUNOS PRIMARY SCHOOL', - 'EVERGREEN PRIMARY SCHOOL', - 'EVERGREEN SECONDARY SCHOOL', - 'FAIRFIELD METHODIST SCHOOL (PRIMARY)', - 'FAIRFIELD METHODIST SCHOOL (SECONDARY)', - 'FAJAR SECONDARY SCHOOL', - 'FARRER PARK PRIMARY SCHOOL', - 'FENGSHAN PRIMARY SCHOOL', - 'FERNVALE PRIMARY SCHOOL', - 'FIRST TOA PAYOH PRIMARY SCHOOL', - 'FIRST TOA PAYOH SECONDARY SCHOOL', - 'FRONTIER PRIMARY SCHOOL', - 'FUCHUN PRIMARY SCHOOL', - 'FUCHUN SECONDARY SCHOOL', - 'FUHUA PRIMARY SCHOOL', - 'FUHUA SECONDARY SCHOOL', - 'GAN ENG SENG PRIMARY SCHOOL', - 'GAN ENG SENG SCHOOL', - 'GEYLANG METHODIST SCHOOL (PRIMARY)', - 'GEYLANG METHODIST SCHOOL (SECONDARY)', - 'GONGSHANG PRIMARY SCHOOL', - 'GREENDALE PRIMARY SCHOOL', - 'GREENDALE SECONDARY SCHOOL', - 'GREENRIDGE PRIMARY SCHOOL', - 'GREENRIDGE SECONDARY SCHOOL', - 'GREENVIEW SECONDARY SCHOOL', - 'GREENWOOD PRIMARY SCHOOL', - 'GUANGYANG PRIMARY SCHOOL', - 'GUANGYANG SECONDARY SCHOOL', - 'HAI SING CATHOLIC SCHOOL', - "HAIG GIRLS' SCHOOL", - 'HENDERSON SECONDARY SCHOOL', - 'HENRY PARK PRIMARY SCHOOL', - 'HILLGROVE SECONDARY SCHOOL', - "HOLY INNOCENTS' HIGH SCHOOL", - "HOLY INNOCENTS' PRIMARY SCHOOL", - 'HONG KAH SECONDARY SCHOOL', - 'HONG WEN SCHOOL', - 'HORIZON PRIMARY SCHOOL', - 'HOUGANG PRIMARY SCHOOL', - 'HOUGANG SECONDARY SCHOOL', - 'HUA YI SECONDARY SCHOOL', - 'HUAMIN PRIMARY SCHOOL', - 'HWA CHONG INSTITUTION', - 'INNOVA JUNIOR COLLEGE', - 'INNOVA PRIMARY SCHOOL', - 'JIEMIN PRIMARY SCHOOL', - 'JING SHAN PRIMARY SCHOOL', - 'JUNYUAN PRIMARY SCHOOL', - 'JUNYUAN SECONDARY SCHOOL', - 'JURONG JUNIOR COLLEGE', - 'JURONG PRIMARY SCHOOL', - 'JURONG SECONDARY SCHOOL', - 'JURONG WEST PRIMARY SCHOOL', - 'JURONG WEST SECONDARY SCHOOL', - 'JURONGVILLE SECONDARY SCHOOL', - 'JUYING PRIMARY SCHOOL', - 'JUYING SECONDARY SCHOOL', - 'KEMING PRIMARY SCHOOL', - 'KENT RIDGE SECONDARY SCHOOL', - 'KHENG CHENG SCHOOL', - 'KONG HWA SCHOOL', - 'KRANJI PRIMARY SCHOOL', - 'KRANJI SECONDARY SCHOOL', - 'KUO CHUAN PRESBYTERIAN PRIMARY SCHOOL', - 'KUO CHUAN PRESBYTERIAN SECONDARY SCHOOL', - 'LAKESIDE PRIMARY SCHOOL', - 'LASALLE COLLEGE OF THE ARTS', - 'LIANHUA PRIMARY SCHOOL', - 'LOYANG PRIMARY SCHOOL', - 'LOYANG SECONDARY SCHOOL', - 'MACPHERSON PRIMARY SCHOOL', - 'MACPHERSON SECONDARY SCHOOL', - 'MAHA BODHI SCHOOL', - 'MANJUSRI SECONDARY SCHOOL', - 'MARIS STELLA HIGH SCHOOL', - 'MARSILING PRIMARY SCHOOL', - 'MARSILING SECONDARY SCHOOL', - 'MARYMOUNT CONVENT SCHOOL', - 'MAYFLOWER PRIMARY SCHOOL', - 'MAYFLOWER SECONDARY SCHOOL', - 'MEE TOH SCHOOL', - 'MERIDIAN JUNIOR COLLEGE', - 'MERIDIAN PRIMARY SCHOOL', - "METHODIST GIRLS' SCHOOL (PRIMARY)", - "METHODIST GIRLS' SCHOOL (SECONDARY)", - 'MILLENNIA INSTITUTE', - 'MONTFORT JUNIOR SCHOOL', - 'MONTFORT SECONDARY SCHOOL', - 'NAN CHIAU HIGH SCHOOL', - 'NAN CHIAU PRIMARY SCHOOL', - 'NAN HUA HIGH SCHOOL', - 'NAN HUA PRIMARY SCHOOL', - 'NANYANG ACADEMY OF FINE ARTS', - "NANYANG GIRLS' HIGH SCHOOL", - 'NANYANG JUNIOR COLLEGE', - 'NANYANG POLYTECHNIC', - 'NANYANG PRIMARY SCHOOL', - 'NANYANG TECHNOLOGICAL UNIVERSITY', - 'NATIONAL JUNIOR COLLEGE', - 'NATIONAL UNIVERSITY OF SINGAPORE', - 'NAVAL BASE PRIMARY SCHOOL', - 'NAVAL BASE SECONDARY SCHOOL', - 'NEW TOWN PRIMARY SCHOOL', - 'NEW TOWN SECONDARY SCHOOL', - 'NGEE ANN POLYTECHNIC', - 'NGEE ANN PRIMARY SCHOOL', - 'NGEE ANN SECONDARY SCHOOL', - 'NORTH SPRING PRIMARY SCHOOL', - 'NORTH VIEW PRIMARY SCHOOL', - 'NORTH VIEW SECONDARY SCHOOL', - 'NORTH VISTA PRIMARY SCHOOL', - 'NORTH VISTA SECONDARY SCHOOL', - 'NORTHBROOKS SECONDARY SCHOOL', - 'NORTHLAND PRIMARY SCHOOL', - 'NORTHLAND SECONDARY SCHOOL', - 'NORTHLIGHT SCHOOL', - 'NORTHOAKS PRIMARY SCHOOL', - 'NUS HIGH SCHOOL OF MATHEMATICS AND SCIENCE', - 'OPERA ESTATE PRIMARY SCHOOL', - 'ORCHID PARK SECONDARY SCHOOL', - 'OUTRAM SECONDARY SCHOOL', - 'PALM VIEW PRIMARY SCHOOL', - 'PARK VIEW PRIMARY SCHOOL', - 'PASIR RIS CREST SECONDARY SCHOOL', - 'PASIR RIS PRIMARY SCHOOL', - 'PASIR RIS SECONDARY SCHOOL', - "PAYA LEBAR METHODIST GIRLS' SCHOOL (PRIMARY)", - "PAYA LEBAR METHODIST GIRLS' SCHOOL (SECONDARY)", - 'PEI CHUN PUBLIC SCHOOL', - 'PEI HWA PRESBYTERIAN PRIMARY SCHOOL', - 'PEI HWA SECONDARY SCHOOL', - 'PEI TONG PRIMARY SCHOOL', - 'PEICAI SECONDARY SCHOOL', - 'PEIRCE SECONDARY SCHOOL', - 'PEIYING PRIMARY SCHOOL', - 'PING YI SECONDARY SCHOOL', - 'PIONEER JUNIOR COLLEGE', - 'PIONEER PRIMARY SCHOOL', - 'PIONEER SECONDARY SCHOOL', - 'POI CHING SCHOOL', - 'PRESBYTERIAN HIGH SCHOOL', - 'PRINCESS ELIZABETH PRIMARY SCHOOL', - 'PUNGGOL GREEN PRIMARY SCHOOL', - 'PUNGGOL PRIMARY SCHOOL', - 'PUNGGOL SECONDARY SCHOOL', - 'PUNGGOL VIEW PRIMARY SCHOOL', - 'QIFA PRIMARY SCHOOL', - 'QIHUA PRIMARY SCHOOL', - 'QUEENSTOWN PRIMARY SCHOOL', - 'QUEENSTOWN SECONDARY SCHOOL', - 'QUEENSWAY SECONDARY SCHOOL', - 'RADIN MAS PRIMARY SCHOOL', - "RAFFLES GIRLS' PRIMARY SCHOOL", - "RAFFLES GIRLS' SCHOOL (SECONDARY)", - 'RAFFLES INSTITUTION', - 'RED SWASTIKA SCHOOL', - 'REGENT SECONDARY SCHOOL', - 'REPUBLIC POLYTECHNIC', - 'RIVER VALLEY HIGH SCHOOL', - 'RIVER VALLEY PRIMARY SCHOOL', - 'RIVERSIDE PRIMARY SCHOOL', - 'RIVERSIDE SECONDARY SCHOOL', - 'RIVERVALE PRIMARY SCHOOL', - 'ROSYTH SCHOOL', - 'RULANG PRIMARY SCHOOL', - 'SCHOOL OF SCIENCE AND TECHNOLOGY, SINGAPORE', - 'SCHOOL OF THE ARTS, SINGAPORE', - 'SEMBAWANG PRIMARY SCHOOL', - 'SEMBAWANG SECONDARY SCHOOL', - 'SENG KANG PRIMARY SCHOOL', - 'SENG KANG SECONDARY SCHOOL', - 'SENGKANG GREEN PRIMARY SCHOOL', - 'SERANGOON GARDEN SECONDARY SCHOOL', - 'SERANGOON JUNIOR COLLEGE', - 'SERANGOON SECONDARY SCHOOL', - 'SHUQUN PRIMARY SCHOOL', - 'SHUQUN SECONDARY SCHOOL', - 'SI LING PRIMARY SCHOOL', - 'SI LING SECONDARY SCHOOL', - 'SIGLAP SECONDARY SCHOOL', - 'SIM UNIVERSITY', - "SINGAPORE CHINESE GIRLS' PRIMARY SCHOOL", - "SINGAPORE CHINESE GIRLS' SCHOOL", - 'SINGAPORE INSTITUTE OF TECHNOLOGY', - 'SINGAPORE MANAGEMENT UNIVERSITY', - 'SINGAPORE POLYTECHNIC', - 'SINGAPORE RAFFLES MUSIC COLLEGE', - 'SINGAPORE SPORTS SCHOOL', - 'SINGAPORE UNIVERSITY OF TECHNOLOGY AND DESIGN', - 'SOUTH VIEW PRIMARY SCHOOL', - 'SPECTRA SECONDARY SCHOOL', - 'SPRINGDALE PRIMARY SCHOOL', - 'SPRINGFIELD SECONDARY SCHOOL', - "ST. ANDREW'S JUNIOR COLLEGE", - "ST. ANDREW'S JUNIOR SCHOOL", - "ST. ANDREW'S SECONDARY SCHOOL", - "ST. ANTHONY'S CANOSSIAN PRIMARY SCHOOL", - "ST. ANTHONY'S CANOSSIAN SECONDARY SCHOOL", - "ST. ANTHONY'S PRIMARY SCHOOL", - "ST. GABRIEL'S PRIMARY SCHOOL", - "ST. GABRIEL'S SECONDARY SCHOOL", - "ST. HILDA'S PRIMARY SCHOOL", - "ST. HILDA'S SECONDARY SCHOOL", - "ST. JOSEPH'S INSTITUTION", - "ST. JOSEPH'S INSTITUTION JUNIOR", - "ST. MARGARET'S PRIMARY SCHOOL", - "ST. MARGARET'S SECONDARY SCHOOL", - "ST. PATRICK'S SCHOOL", - "ST. STEPHEN'S SCHOOL", - 'STAMFORD PRIMARY SCHOOL', - 'SWISS COTTAGE SECONDARY SCHOOL', - 'TAMPINES JUNIOR COLLEGE', - 'TAMPINES NORTH PRIMARY SCHOOL', - 'TAMPINES PRIMARY SCHOOL', - 'TAMPINES SECONDARY SCHOOL', - 'TANGLIN SECONDARY SCHOOL', - "TANJONG KATONG GIRLS' SCHOOL", - 'TANJONG KATONG PRIMARY SCHOOL', - 'TANJONG KATONG SECONDARY SCHOOL', - 'TAO NAN SCHOOL', - 'TECK GHEE PRIMARY SCHOOL', - 'TECK WHYE PRIMARY SCHOOL', - 'TECK WHYE SECONDARY SCHOOL', - 'TELOK KURAU PRIMARY SCHOOL', - 'TEMASEK JUNIOR COLLEGE', - 'TEMASEK POLYTECHNIC', - 'TEMASEK PRIMARY SCHOOL', - 'TEMASEK SECONDARY SCHOOL', - 'TOWNSVILLE PRIMARY SCHOOL', - 'UNITY PRIMARY SCHOOL', - 'UNITY SECONDARY SCHOOL', - 'VICTORIA JUNIOR COLLEGE', - 'VICTORIA SCHOOL', - 'WELLINGTON PRIMARY SCHOOL', - 'WEST GROVE PRIMARY SCHOOL', - 'WEST SPRING PRIMARY SCHOOL', - 'WEST SPRING SECONDARY SCHOOL', - 'WEST VIEW PRIMARY SCHOOL', - 'WESTWOOD PRIMARY SCHOOL', - 'WESTWOOD SECONDARY SCHOOL', - 'WHITE SANDS PRIMARY SCHOOL', - 'WHITLEY SECONDARY SCHOOL', - 'WOODGROVE PRIMARY SCHOOL', - 'WOODGROVE SECONDARY SCHOOL', - 'WOODLANDS PRIMARY SCHOOL', - 'WOODLANDS RING PRIMARY SCHOOL', - 'WOODLANDS RING SECONDARY SCHOOL', - 'WOODLANDS SECONDARY SCHOOL', - 'XINGHUA PRIMARY SCHOOL', - 'XINGNAN PRIMARY SCHOOL', - 'XINMIN PRIMARY SCHOOL', - 'XINMIN SECONDARY SCHOOL', - 'XISHAN PRIMARY SCHOOL', - 'YALE-NUS COLLEGE', - 'YANGZHENG PRIMARY SCHOOL', - 'YEW TEE PRIMARY SCHOOL', - 'YIO CHU KANG PRIMARY SCHOOL', - 'YIO CHU KANG SECONDARY SCHOOL', - 'YISHUN JUNIOR COLLEGE', - 'YISHUN PRIMARY SCHOOL', - 'YISHUN SECONDARY SCHOOL', - 'YISHUN TOWN SECONDARY SCHOOL', - 'YU NENG PRIMARY SCHOOL', - 'YUAN CHING SECONDARY SCHOOL', - 'YUHUA PRIMARY SCHOOL', - 'YUHUA SECONDARY SCHOOL', - 'YUMIN PRIMARY SCHOOL', - 'YUSOF ISHAK SECONDARY SCHOOL', - 'YUYING SECONDARY SCHOOL', - 'ZHANGDE PRIMARY SCHOOL', - 'ZHENGHUA PRIMARY SCHOOL', - 'ZHENGHUA SECONDARY SCHOOL', - 'ZHONGHUA PRIMARY SCHOOL', - 'ZHONGHUA SECONDARY SCHOOL', -] - -export default myInfoSchools diff --git a/src/shared/util/verified-content.ts b/src/shared/util/verified-content.ts index e276b8d4d9..fc82e4aa06 100644 --- a/src/shared/util/verified-content.ts +++ b/src/shared/util/verified-content.ts @@ -1,102 +1,14 @@ -// This file contains mapping layers for verifiedContent in encrypted submission -// data. -// The mapping layers will translate certain keys such as SingPass login key -// values to keys that our application has designated. -import pick from 'lodash/pick' import values from 'lodash/values' -// Centralised mapping layer for use in other files to get the mapped value. +// Shared centralised mapping layer for use in both frontend and backend to get the mapped value. export enum VerifiedKeys { SpUinFin = 'uinFin', CpUen = 'cpUen', CpUid = 'cpUid', } -interface ISpVerifiedKeys { - uinFin: VerifiedKeys.SpUinFin -} - -// Note: The same key in different contexts such as authType can be mapped to a -// different key. This is why there exists different mapping layers instead of -// just one. -const SpVerifiedKeys: ISpVerifiedKeys = { - uinFin: VerifiedKeys.SpUinFin, -} - -interface ICpVerifiedKeys { - uinFin: VerifiedKeys.CpUen - userInfo: VerifiedKeys.CpUid -} - -const CpVerifiedKeys: ICpVerifiedKeys = { - uinFin: VerifiedKeys.CpUen, - userInfo: VerifiedKeys.CpUid, -} - -// Array determines the order to process and display the verified fields in both -// the detailed responses page and the csv file. -export const CURRENT_VERIFIED_FIELDS: VerifiedKeys[] = ([] as VerifiedKeys[]) - .concat(values(SpVerifiedKeys)) - .concat(values(CpVerifiedKeys)) - -export type VerifiedKeyMap = - | ISpVerifiedKeys - | ICpVerifiedKeys - | Record - -/** - * Returns the correct mapping layer according to given type. If no type is - * given, an empty object is returned. - * @param type Type of mapping layer to retrieve - * @returns {Record { - switch (type) { - case 'SP': - return SpVerifiedKeys - case 'CP': - return CpVerifiedKeys - default: - return {} - } -} - /** - * Helper function to rename keys in given object {@param obj} with a mapping - * {@param newKeys}. - * @param obj The object to rename keys for - * @param newKeys The oldKey-newKey mapping - * @returns A new object with the renamed keys + * Array determines the order to process and display the verified fields in both + * the detailed responses page and the csv file. */ -const renameKeys = (obj: Record, newKeys: Record) => { - const keyValues = Object.keys(obj).map((key) => { - const newKey = newKeys[key] || key - return { [newKey]: obj[key] } - }) - return Object.assign({}, ...keyValues) -} - -interface IMappableData { - type?: 'SP' | 'CP' - data: Record -} - -/** - * Return new object with only the keys that exist in the field mapping, - * with their new keys as mapped. For example, - * type = 'CP', - * data = { uinFin: '1234567', other: '123' } - * will return - * { cpUen: '1234567' } - * as `uinFin` is mapped to `cpUen`. `other` is removed - * as the key does not map to anything. - */ -export const mapDataToKey = ({ - type, - data, -}: IMappableData): Record => { - const fieldMap = getVerifiedKeyMap(type) - const subsetKeys = pick(data, Object.keys(fieldMap)) - - return renameKeys(subsetKeys, fieldMap) -} +export const CURRENT_VERIFIED_FIELDS: VerifiedKeys[] = values(VerifiedKeys) diff --git a/src/types/field/fieldTypes.ts b/src/types/field/fieldTypes.ts index c270c227c3..0e7eda7360 100644 --- a/src/types/field/fieldTypes.ts +++ b/src/types/field/fieldTypes.ts @@ -24,8 +24,6 @@ export enum MyInfoAttribute { Name = 'name', PassportNumber = 'passportnumber', RegisteredAddress = 'regadd', - MailingAddress = 'mailadd', - BillingAddress = 'billadd', Employment = 'employment', VehicleNo = 'vehno', MarriageCertNo = 'marriagecertno', @@ -38,19 +36,15 @@ export enum MyInfoAttribute { HousingType = 'housingtype', HdbType = 'hdbtype', Marital = 'marital', - EducationLevel = 'edulevel', CountryOfMarriage = 'countryofmarriage', WorkpassStatus = 'workpassstatus', - SchoolName = 'schoolname', Occupation = 'occupation', MobileNo = 'mobileno', - HomeNo = 'homeno', DateOfBirth = 'dob', PassportExpiryDate = 'passportexpirydate', MarriageDate = 'marriagedate', DivorceDate = 'divorcedate', WorkpassExpiryDate = 'workpassexpirydate', - GraduationYear = 'gradyear', } export enum SPCPFieldTitle { diff --git a/tests/.test-full-env b/tests/.test-full-env index c347b6ef4c..09fecbb0ac 100644 --- a/tests/.test-full-env +++ b/tests/.test-full-env @@ -24,7 +24,6 @@ CORPPASS_PARTNER_ENTITY_ID=https://staging.form.gov.sg/corppass CORPPASS_ESRVC_ID=FORMSG-CP-TEST CORPPASS_IDP_ID=https://saml.corppass.gov.sg/FIM/sps/CorpIDPFed/saml20 -SPCP_COOKIE_MAX_AGE=7200000 SHOW_LOGIN_PAGE=true IS_SP_MAINTENANCE=Date/Time-SP IS_CP_MAINTENANCE=Date/Time-CP diff --git a/tests/unit/backend/controllers/encrypt-submissions.server.controller.spec.js b/tests/unit/backend/controllers/encrypt-submissions.server.controller.spec.js index bac35620b8..ec919c6d61 100644 --- a/tests/unit/backend/controllers/encrypt-submissions.server.controller.spec.js +++ b/tests/unit/backend/controllers/encrypt-submissions.server.controller.spec.js @@ -5,6 +5,7 @@ const request = require('supertest') const dbHandler = require('../helpers/db-handler') const EncryptSubmissionsMiddleware = require('../../../../dist/backend/app/modules/submission/encrypt-submission/encrypt-submission.middleware') +const VerifiedContentMiddleware = require('../../../../dist/backend/app/modules/verified-content/verified-content.middlewares') const User = dbHandler.makeModel('user.server.model', 'User') const Agency = dbHandler.makeModel('agency.server.model', 'Agency') @@ -22,13 +23,6 @@ describe('Encrypt Submissions Controller', () => { mongoose: Object.assign(mongoose, { '@noCallThru': true }), }, ) - const SpcpController = spec( - 'dist/backend/app/controllers/spcp.server.controller', - { - mongoose: Object.assign(mongoose, { '@noCallThru': true }), - '../../config/ndi-config': {}, - }, - ) beforeAll(async () => await dbHandler.connect()) afterEach(async () => await dbHandler.clearDatabase()) @@ -228,14 +222,13 @@ describe('Encrypt Submissions Controller', () => { } const app = express() - const secretSigningKey = process.env.SIGNING_SECRET_KEY beforeAll(() => { app .route(endpointPath) .post( injectFixtures, - SpcpController.encryptedVerifiedFields(secretSigningKey), + VerifiedContentMiddleware.encryptVerifiedSpcpFields, EncryptSubmissionsMiddleware.prepareEncryptSubmission, sendSubmissionBack, ) @@ -247,6 +240,13 @@ describe('Encrypt Submissions Controller', () => { body: { encryptedContent: correctlyEncryptedContent, }, + form: new Form({ + title: 'Test Form', + authType: 'NIL', + responseMode: 'encrypt', + publicKey: publicKey, + form_fields: [], + }).toObject(), }, } request(app) @@ -316,7 +316,7 @@ describe('Encrypt Submissions Controller', () => { res: { locals: { uinFin: 'ABCDEFG', - userData: 'SXXXXXXYZ', + userInfo: 'SXXXXXXYZ', }, }, }