Skip to content

Commit 46c7655

Browse files
authored
Merge pull request #354 from bcgov/bugfix/remove-null-characters
Request Sanitizing Middleware
2 parents bae359e + 65ee5bb commit 46c7655

14 files changed

Lines changed: 748 additions & 200 deletions

File tree

.codeclimate.yml

Lines changed: 0 additions & 51 deletions
This file was deleted.

app/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { randomBytes } from 'crypto';
99

1010
import { version as appVersion } from './package.json';
1111
import { getLogger, httpLogger } from './src/components/log';
12+
import { requestSanitizer } from './src/middleware/requestSanitizer';
1213
import router from './src/routes';
1314
import { Problem } from './src/utils';
1415
import { DEFAULTCORS } from './src/utils/constants/application';
@@ -26,6 +27,7 @@ app.use(compression());
2627
app.use(cors(DEFAULTCORS));
2728
app.use(express.json({ limit: config.get('server.bodyLimit') }));
2829
app.use(express.urlencoded({ extended: true }));
30+
app.use(requestSanitizer);
2931
app.set('query parser', 'extended');
3032

3133
app.use((_req, res, next) => {

app/src/controllers/roadmap.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ const controller = {
1919
const attachments: Array<EmailAttachment> = [];
2020

2121
if (req.currentContext?.bearerToken) {
22-
const comsObjects = await comsService.getObjects(req.currentContext?.bearerToken, req.body.selectedFileIds);
23-
2422
// Attempt to get the requested documents from COMS
2523
// If succesful it is converted to base64 encoding and added to the attachment list
2624
const objectPromises = req.body.selectedFileIds.map(async (id) => {
@@ -30,7 +28,7 @@ const controller = {
3028
);
3129

3230
if (status === 200) {
33-
const filename = comsObjects.find((x: { id: string }) => x.id === id)?.name;
31+
const filename = headers['x-amz-meta-name'];
3432
if (filename) {
3533
attachments.push({
3634
content: Buffer.from(data).toString('base64'),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { NextFunction, Request, Response } from 'express';
2+
3+
import { sanitize } from './utils';
4+
5+
/**
6+
* Express middleware to sanitize incoming request bodies
7+
* @param req The Express request object
8+
* @param _res The Express response object
9+
* @param next The next middleware function
10+
*/
11+
export function requestSanitizer(req: Request, _res: Response, next: NextFunction): void {
12+
if (req.body !== undefined) {
13+
req.body = sanitize(req.body);
14+
}
15+
next();
16+
}

app/src/middleware/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Removes all NUL characters from a string
3+
* @param str The string to sanitize
4+
* @returns The sanitized string if it includes NUL character(s), otherwise the original string
5+
*/
6+
export function stripNullCharactersFromString(str: string): string {
7+
return str.replace(/\u0000/g, ''); // eslint-disable-line no-control-regex
8+
}
9+
10+
/**
11+
* Recursively sanitize JSON-like data (strings, arrays, plain objects)
12+
* @param value The value to sanitize
13+
* @returns The sanitized value if a string otherwise the original value
14+
*/
15+
export function sanitize<T>(value: T): T | string {
16+
// Strip NUL characters from strings
17+
if (typeof value === 'string') return stripNullCharactersFromString(value);
18+
19+
// Recursively sanitize arrays items
20+
if (Array.isArray(value)) {
21+
for (let i = 0; i < value.length; i++) {
22+
value[i] = sanitize(value[i]);
23+
}
24+
return value;
25+
}
26+
27+
// Recursively sanitize plain objects
28+
if (value && typeof value === 'object') {
29+
for (const [k, v] of Object.entries(value)) (value as Record<string, unknown>)[k] = sanitize(v);
30+
return value;
31+
}
32+
33+
return value;
34+
}

app/src/services/coms.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import axios from 'axios';
22
import config from 'config';
33

44
import { Action } from '../utils/enums/application';
5+
import { Problem, uuidValidateV4 } from '../utils';
56

67
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
78

@@ -48,31 +49,19 @@ const service = {
4849
},
4950

5051
/**
51-
* @function getObject
5252
* Get an object
53-
* @param {string} bearerToken The bearer token of the authorized user
54-
* @param {string} objectId The id for the object to get
53+
* @param bearerToken The bearer token of the authorized user
54+
* @param objectId The id for the object to get
5555
*/
5656
async getObject(bearerToken: string, objectId: string) {
57+
if (!uuidValidateV4(objectId)) {
58+
throw new Problem(422, { detail: 'Invalid objectId parameter' });
59+
}
5760
const { status, headers, data } = await comsAxios({
5861
responseType: 'arraybuffer',
5962
headers: { Authorization: `Bearer ${bearerToken}` }
6063
}).get(`/object/${objectId}`);
6164
return { status, headers, data };
62-
},
63-
64-
/**
65-
* @function getObjects
66-
* Gets a list of objects
67-
* @param {string} bearerToken The bearer token of the authorized user
68-
* @param {string[]} objectIds Array of object ids to get
69-
*/
70-
async getObjects(bearerToken: string, objectIds: Array<string>) {
71-
const { data } = await comsAxios({ headers: { Authorization: `Bearer ${bearerToken}` } }).get('/object', {
72-
params: { objectId: objectIds }
73-
});
74-
75-
return data;
7665
}
7766
};
7867

app/src/types/IdpAttributes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export type IdpAttributes = {
2-
identityKey: string;
32
idp: string;
4-
name: string;
3+
kind: string;
54
username: string;
65
};

app/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { default as Problem } from './problem';
2+
export * from './utils';

0 commit comments

Comments
 (0)