Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FPET-1227: Add middleware to sanitise form data #1726

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions config/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ services:
citizen:
uploadDocsEmail: UPLOAD_DOCUMENTS_EMAIL
case:
url: 'https://ccd-data-store-api-prl-ccd-definitions-pr-2518.preview.platform.hmcts.net'
url: 'http://ccd-data-store-api-aat.service.core-compute-aat.internal'
cos:
url: 'https://prl-cos-pr-2906.preview.platform.hmcts.net'
url: 'http://prl-cos-aat.service.core-compute-aat.internal'
documentManagement:
url: 'https://ccd-case-document-am-api-prl-ccd-definitions-pr-2518.preview.platform.hmcts.net'
url: 'http://ccd-case-document-am-api-aat.service.core-compute-aat.internal'
fact:
url: 'http://fact-api-aat.service.core-compute-aat.internal'
reasonableAdjustments:
Expand Down
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
roots: ['<rootDir>/src/main'],
testRegex: '(/src/test/.*|\\.test)\\.(ts|js)$',
testEnvironment: 'node',
preset: "ts-jest/presets/js-with-ts",
transform: {
'^.+\\.ts$': 'ts-jest',
},
Expand All @@ -13,4 +14,5 @@ module.exports = {
coverageThreshold: {
},
verbose: true,
transformIgnorePatterns: ['<rootDir>/node_modules/(?!node-emoji)'],
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"lodash": "^4.17.21",
"multer": "^1.4.5-lts.1",
"negotiator": "^0.6.2",
"node-emoji": "^2.2.0",
"nunjucks": "^3.2.4",
"otplib": "^12.0.1",
"pcf-start": "^1.31.2",
Expand All @@ -106,6 +107,7 @@
"query-string": "^7.1.1",
"redis": "^3.1.2",
"require-directory": "^2.1.1",
"sanitize-html": "^2.13.1",
"semver": "^7.6.2",
"serve-favicon": "^2.5.0",
"session-file-store": "^1.5.0",
Expand Down
23 changes: 23 additions & 0 deletions src/main/modules/sanitize-request/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { mockRequest } from '../../../test/unit/utils/mockRequest';

import { SanitizeRequest } from '.';

describe('sanitize request > index', () => {
describe('sanitizeRequestBody', () => {
test('should sanitize request body', () => {
const req = mockRequest({ body: { too_shortStatement: 'test<img src""> ☕️' } });
const mockNext = jest.fn();
new SanitizeRequest().sanitizeRequestBody(req, mockNext);
expect(req.body).toEqual({ too_shortStatement: 'test' });
});

test('should sanitize request body for arrays', () => {
const req = mockRequest({
body: { courtProceedingsOrders: ['childArrangementOrder', 'supervisionOrder<img src""> ☕️'] },
});
const mockNext = jest.fn();
new SanitizeRequest().sanitizeRequestBody(req, mockNext);
expect(req.body).toEqual({ courtProceedingsOrders: ['childArrangementOrder', 'supervisionOrder'] });
});
});
});
32 changes: 32 additions & 0 deletions src/main/modules/sanitize-request/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import _, { trim } from 'lodash';
import { strip } from 'node-emoji';
import sanitizeHtml from 'sanitize-html';
import { NextFunction } from 'webpack-dev-middleware';

import { AppRequest } from '../../app/controller/AppRequest';

export class SanitizeRequest {
hcarslaw marked this conversation as resolved.
Show resolved Hide resolved
private readonly formInputsToOmit = [
'_csrf',
'onlyContinue',
'saveAndComeLater',
'onlycontinue',
'accessCodeCheck',
'submit',
'startNow',
'goBack',
'link',
];

public sanitizeRequestBody(req: AppRequest, next: NextFunction): void {
const sanitizeText = _.flow([strip, sanitizeHtml, _.unescape, trim]);
hcarslaw marked this conversation as resolved.
Show resolved Hide resolved

Object.entries(req.body)
.filter(([key]) => !this.formInputsToOmit.includes(key))
.forEach(([key, value]) => {
req.body[key] = _.isArray(value) ? value.map(item => sanitizeText(item)) : sanitizeText(value);
});

next();
hcarslaw marked this conversation as resolved.
Show resolved Hide resolved
}
}
14 changes: 14 additions & 0 deletions src/main/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { Application } from 'express';

import { RAProvider } from '../main/modules/reasonable-adjustments/index';
import { mockRequest } from '../test/unit/utils/mockRequest';
import { mockResponse } from '../test/unit/utils/mockResponse';

import { Routes } from './routes';

Expand Down Expand Up @@ -37,4 +39,16 @@ describe('Routes', () => {
test('should setup routes', () => {
expect(appMock.get).toHaveBeenCalledWith('/csrf-token-error', mockCSRFTokenError);
});

test('should sanitize request body', () => {
const req = mockRequest({ body: { too_shortStatement: 'test<img src""> ☕️' } });
const res = mockResponse();
const mockNext = jest.fn();
const routes = new Routes();

routes.enableFor(appMock);
routes['sanitizeRequestBody'](req, res, mockNext);

expect(req.body).toEqual({ too_shortStatement: 'test' });
});
});
6 changes: 6 additions & 0 deletions src/main/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { RespondentSubmitResponseController } from './app/controller/RespondentS
import TSDraftController from './app/testingsupport/TSDraftController';
import { PaymentHandler, PaymentValidationHandler } from './modules/payments/paymentController';
import { RAProvider } from './modules/reasonable-adjustments';
import { SanitizeRequest } from './modules/sanitize-request';
import { StepWithContent, getStepsWithContent, stepsWithContent } from './steps/';
import UploadDocumentController from './steps/application-within-proceedings/document-upload/postController';
import { processAWPApplication } from './steps/application-within-proceedings/utils';
Expand Down Expand Up @@ -131,6 +132,7 @@ export class Routes {
: step.postController ?? PostController;
app.post(
step.url,
this.sanitizeRequestBody.bind(this),
// eslint-disable-next-line prettier/prettier
this.routeGuard.bind(this, step, 'post'),
errorHandler(new postController(step.form.fields).post)
Expand All @@ -156,4 +158,8 @@ export class Routes {
next();
}
}

private sanitizeRequestBody(req, res, next) {
new SanitizeRequest().sanitizeRequestBody(req, next);
hcarslaw marked this conversation as resolved.
Show resolved Hide resolved
}
}
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"routes/*": ["routes/*"],
"@c100/*": ["steps/c100-rebuild/*"]
},
"typeRoots": ["./node_modules/@types"]
"typeRoots": ["./node_modules/@types"],
"allowJs": true,
"moduleDetection": "force",
},
"exclude": ["**/*.test.ts", "**/codecept.conf.ts", "**/__mocks__/*"]
}
116 changes: 112 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3196,7 +3196,7 @@ __metadata:
languageName: node
linkType: hard

"@sindresorhus/is@npm:^4.0.0":
"@sindresorhus/is@npm:^4.0.0, @sindresorhus/is@npm:^4.6.0":
version: 4.6.0
resolution: "@sindresorhus/is@npm:4.6.0"
checksum: 83839f13da2c29d55c97abc3bc2c55b250d33a0447554997a85c539e058e57b8da092da396e252b11ec24a0279a0bed1f537fa26302209327060643e327f81d2
Expand Down Expand Up @@ -8641,7 +8641,18 @@ __metadata:
languageName: node
linkType: hard

"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0":
"dom-serializer@npm:^2.0.0":
version: 2.0.0
resolution: "dom-serializer@npm:2.0.0"
dependencies:
domelementtype: ^2.3.0
domhandler: ^5.0.2
entities: ^4.2.0
checksum: cd1810544fd8cdfbd51fa2c0c1128ec3a13ba92f14e61b7650b5de421b88205fd2e3f0cc6ace82f13334114addb90ed1c2f23074a51770a8e9c1273acbc7f3e6
languageName: node
linkType: hard

"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0":
version: 2.3.0
resolution: "domelementtype@npm:2.3.0"
checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6
Expand All @@ -8666,6 +8677,15 @@ __metadata:
languageName: node
linkType: hard

"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
version: 5.0.3
resolution: "domhandler@npm:5.0.3"
dependencies:
domelementtype: ^2.3.0
checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c
languageName: node
linkType: hard

"domutils@npm:^2.5.2, domutils@npm:^2.8.0":
version: 2.8.0
resolution: "domutils@npm:2.8.0"
Expand All @@ -8677,6 +8697,17 @@ __metadata:
languageName: node
linkType: hard

"domutils@npm:^3.0.1":
version: 3.1.0
resolution: "domutils@npm:3.1.0"
dependencies:
dom-serializer: ^2.0.0
domelementtype: ^2.3.0
domhandler: ^5.0.3
checksum: e5757456ddd173caa411cfc02c2bb64133c65546d2c4081381a3bafc8a57411a41eed70494551aa58030be9e58574fcc489828bebd673863d39924fb4878f416
languageName: node
linkType: hard

"dot-case@npm:^3.0.4":
version: 3.0.4
resolution: "dot-case@npm:3.0.4"
Expand Down Expand Up @@ -8877,6 +8908,13 @@ __metadata:
languageName: node
linkType: hard

"emojilib@npm:^2.4.0":
version: 2.4.0
resolution: "emojilib@npm:2.4.0"
checksum: ea241c342abda5a86ffd3a15d8f4871a616d485f700e03daea38c6ce38205847cea9f6ff8d5e962c00516b004949cc96c6e37b05559ea71a0a496faba53b56da
languageName: node
linkType: hard

"emojis-list@npm:^3.0.0":
version: 3.0.0
resolution: "emojis-list@npm:3.0.0"
Expand Down Expand Up @@ -8981,7 +9019,7 @@ __metadata:
languageName: node
linkType: hard

"entities@npm:^4.4.0, entities@npm:^4.5.0":
"entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0":
version: 4.5.0
resolution: "entities@npm:4.5.0"
checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7
Expand Down Expand Up @@ -11449,6 +11487,18 @@ __metadata:
languageName: node
linkType: hard

"htmlparser2@npm:^8.0.0":
version: 8.0.2
resolution: "htmlparser2@npm:8.0.2"
dependencies:
domelementtype: ^2.3.0
domhandler: ^5.0.3
domutils: ^3.0.1
entities: ^4.4.0
checksum: 29167a0f9282f181da8a6d0311b76820c8a59bc9e3c87009e21968264c2987d2723d6fde5a964d4b7b6cba663fca96ffb373c06d8223a85f52a6089ced942700
languageName: node
linkType: hard

"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
Expand Down Expand Up @@ -12191,6 +12241,13 @@ __metadata:
languageName: node
linkType: hard

"is-plain-object@npm:^5.0.0":
version: 5.0.0
resolution: "is-plain-object@npm:5.0.0"
checksum: e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c
languageName: node
linkType: hard

"is-potential-custom-element-name@npm:^1.0.1":
version: 1.0.1
resolution: "is-potential-custom-element-name@npm:1.0.1"
Expand Down Expand Up @@ -15592,6 +15649,18 @@ __metadata:
languageName: node
linkType: hard

"node-emoji@npm:^2.2.0":
version: 2.2.0
resolution: "node-emoji@npm:2.2.0"
dependencies:
"@sindresorhus/is": ^4.6.0
char-regex: ^1.0.2
emojilib: ^2.4.0
skin-tone: ^2.0.0
checksum: 9642bee0b8c5f2124580e6a2d4c5ec868987bc77b6ce3a335bbec8db677082cbe1a9b72c11aac60043396a8d36e0afad4bcc33d92105d103d2d1b6a59106219a
languageName: node
linkType: hard

"node-fetch@npm:2.6.7":
version: 2.6.7
resolution: "node-fetch@npm:2.6.7"
Expand Down Expand Up @@ -16476,6 +16545,13 @@ __metadata:
languageName: node
linkType: hard

"parse-srcset@npm:^1.0.2":
version: 1.0.2
resolution: "parse-srcset@npm:1.0.2"
checksum: 3a0380380c6082021fcce982f0b89fb8a493ce9dfd7d308e5e6d855201e80db8b90438649b31fdd82a3d6089a8ca17dccddaa2b730a718389af4c037b8539ebf
languageName: node
linkType: hard

"parse5@npm:7.1.2":
version: 7.1.2
resolution: "parse5@npm:7.1.2"
Expand Down Expand Up @@ -16984,7 +17060,7 @@ __metadata:
languageName: node
linkType: hard

"postcss@npm:^8.4.33":
"postcss@npm:^8.3.11, postcss@npm:^8.4.33":
version: 8.4.49
resolution: "postcss@npm:8.4.49"
dependencies:
Expand Down Expand Up @@ -17175,6 +17251,7 @@ __metadata:
moment: ^2.29.4
multer: ^1.4.5-lts.1
negotiator: ^0.6.2
node-emoji: ^2.2.0
node-sass: ^8.0.0
nodemon: ^2.0.16
npm-run-all: ^4.1.5
Expand All @@ -17188,6 +17265,7 @@ __metadata:
query-string: ^7.1.1
redis: ^3.1.2
require-directory: ^2.1.1
sanitize-html: ^2.13.1
sass-loader: ^13.1.0
semver: ^7.6.2
serve-favicon: ^2.5.0
Expand Down Expand Up @@ -18574,6 +18652,20 @@ __metadata:
languageName: node
linkType: hard

"sanitize-html@npm:^2.13.1":
version: 2.13.1
resolution: "sanitize-html@npm:2.13.1"
dependencies:
deepmerge: ^4.2.2
escape-string-regexp: ^4.0.0
htmlparser2: ^8.0.0
is-plain-object: ^5.0.0
parse-srcset: ^1.0.2
postcss: ^8.3.11
checksum: 645381375fcb9c70070644b02538f1d43d35db6c9761eba32ec5f0ea919fdc70aa19e186ae949e6e21de767cbf11ced3e6a31b322a6c705593dc9a902a257d7a
languageName: node
linkType: hard

"sass-graph@npm:^4.0.1":
version: 4.0.1
resolution: "sass-graph@npm:4.0.1"
Expand Down Expand Up @@ -19036,6 +19128,15 @@ __metadata:
languageName: node
linkType: hard

"skin-tone@npm:^2.0.0":
version: 2.0.0
resolution: "skin-tone@npm:2.0.0"
dependencies:
unicode-emoji-modifier-base: ^1.0.0
checksum: 19de157586b8019cacc55eb25d9d640f00fc02415761f3e41a4527142970fd4e7f6af0333bc90e879858766c20a976107bb386ffd4c812289c01d51f2c8d182c
languageName: node
linkType: hard

"slash@npm:^3.0.0":
version: 3.0.0
resolution: "slash@npm:3.0.0"
Expand Down Expand Up @@ -20901,6 +21002,13 @@ __metadata:
languageName: node
linkType: hard

"unicode-emoji-modifier-base@npm:^1.0.0":
version: 1.0.0
resolution: "unicode-emoji-modifier-base@npm:1.0.0"
checksum: 6e1521d35fa69493207eb8b41f8edb95985d8b3faf07c01d820a1830b5e8403e20002563e2f84683e8e962a49beccae789f0879356bf92a4ec7a4dd8e2d16fdb
languageName: node
linkType: hard

"unicode-match-property-ecmascript@npm:^2.0.0":
version: 2.0.0
resolution: "unicode-match-property-ecmascript@npm:2.0.0"
Expand Down