From 5dea4c94d00775ef2ad383c4045a2181a5deb709 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 6 Sep 2022 00:55:24 +0200 Subject: [PATCH] UI server: do not crash at payload validation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ChargingStationWorkerBroadcastChannel.ts | 9 +++-- .../UIServiceWorkerBroadcastChannel.ts | 9 +++-- .../WorkerBroadcastChannel.ts | 26 +++++++++++-- .../ui-server/UIWebSocketServer.ts | 37 ++++++++++++++++--- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/charging-station/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/ChargingStationWorkerBroadcastChannel.ts index 3000626e..6ab170e5 100644 --- a/src/charging-station/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/ChargingStationWorkerBroadcastChannel.ts @@ -45,11 +45,14 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca } private async requestHandler(messageEvent: MessageEvent): Promise { - if (this.isResponse(messageEvent.data) === true) { + const validatedMessageEvent = this.validateMessageEvent(messageEvent); + if (validatedMessageEvent === false) { return; } - const [uuid, command, requestPayload] = this.validateMessageEvent(messageEvent) - .data as BroadcastChannelRequest; + if (this.isResponse(validatedMessageEvent.data) === true) { + return; + } + const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest; if ( requestPayload?.hashIds !== undefined && diff --git a/src/charging-station/UIServiceWorkerBroadcastChannel.ts b/src/charging-station/UIServiceWorkerBroadcastChannel.ts index 650a8b9e..7e22860a 100644 --- a/src/charging-station/UIServiceWorkerBroadcastChannel.ts +++ b/src/charging-station/UIServiceWorkerBroadcastChannel.ts @@ -29,11 +29,14 @@ export default class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChan } private responseHandler(messageEvent: MessageEvent): void { - if (this.isRequest(messageEvent.data) === true) { + const validatedMessageEvent = this.validateMessageEvent(messageEvent); + if (validatedMessageEvent === false) { return; } - const [uuid, responsePayload] = this.validateMessageEvent(messageEvent) - .data as BroadcastChannelResponse; + if (this.isRequest(validatedMessageEvent.data) === true) { + return; + } + const [uuid, responsePayload] = validatedMessageEvent.data as BroadcastChannelResponse; if (this.responses.has(uuid) === false) { this.responses.set(uuid, { responsesExpected: this.uiService.getBroadcastChannelExpectedResponses(uuid), diff --git a/src/charging-station/WorkerBroadcastChannel.ts b/src/charging-station/WorkerBroadcastChannel.ts index 2fa082dd..96fe7c17 100644 --- a/src/charging-station/WorkerBroadcastChannel.ts +++ b/src/charging-station/WorkerBroadcastChannel.ts @@ -1,12 +1,17 @@ import { BroadcastChannel } from 'worker_threads'; -import BaseError from '../exception/BaseError'; +import * as uuid from 'uuid'; + import type { JsonType } from '../types/JsonType'; import type { BroadcastChannelRequest, BroadcastChannelResponse, MessageEvent, } from '../types/WorkerBroadcastChannel'; +import logger from '../utils/Logger'; +import Utils from '../utils/Utils'; + +const moduleName = 'WorkerBroadcastChannel'; export default abstract class WorkerBroadcastChannel extends BroadcastChannel { protected constructor() { @@ -29,10 +34,25 @@ export default abstract class WorkerBroadcastChannel extends BroadcastChannel { return Array.isArray(message) && message.length === 2; } - protected validateMessageEvent(messageEvent: MessageEvent): MessageEvent { + protected validateMessageEvent(messageEvent: MessageEvent): MessageEvent | false { if (Array.isArray(messageEvent.data) === false) { - throw new BaseError('Worker broadcast channel protocol message event data is not an array'); + logger.error( + this.logPrefix(moduleName, 'validateMessageEvent') + + ' Worker broadcast channel protocol message event data is not an array' + ); + return false; + } + if (uuid.validate(messageEvent.data[0]) === false) { + logger.error( + this.logPrefix(moduleName, 'validateMessageEvent') + + ' Worker broadcast channel protocol message event data UUID field is invalid' + ); + return false; } return messageEvent; } + + private logPrefix(modName: string, methodName: string): string { + return Utils.logPrefix(` Worker Broadcast Channel | ${modName}.${methodName}:`); + } } diff --git a/src/charging-station/ui-server/UIWebSocketServer.ts b/src/charging-station/ui-server/UIWebSocketServer.ts index 01c57800..ea54bec0 100644 --- a/src/charging-station/ui-server/UIWebSocketServer.ts +++ b/src/charging-station/ui-server/UIWebSocketServer.ts @@ -2,9 +2,9 @@ import { IncomingMessage, createServer } from 'http'; import type internal from 'stream'; import { StatusCodes } from 'http-status-codes'; +import * as uuid from 'uuid'; import WebSocket, { RawData, WebSocketServer } from 'ws'; -import BaseError from '../../exception/BaseError'; import type { UIServerConfiguration } from '../../types/ConfigurationData'; import type { ProtocolRequest, ProtocolResponse } from '../../types/UIProtocol'; import { WebSocketCloseEventStatusCode } from '../../types/WebSocket'; @@ -44,7 +44,12 @@ export default class UIWebSocketServer extends AbstractUIServer { this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this)); } ws.on('message', (rawData) => { - const [messageId, procedureName, payload] = this.validateRawDataRequest(rawData); + const request = this.validateRawDataRequest(rawData); + if (request === false) { + ws.close(WebSocketCloseEventStatusCode.CLOSE_INVALID_PAYLOAD); + return; + } + const [messageId, procedureName, payload] = request as ProtocolRequest; this.uiServices .get(version) .requestHandler(this.buildProtocolRequest(messageId, procedureName, payload)) @@ -128,7 +133,7 @@ export default class UIWebSocketServer extends AbstractUIServer { } } - private validateRawDataRequest(rawData: RawData): ProtocolRequest { + private validateRawDataRequest(rawData: RawData): ProtocolRequest | false { // logger.debug( // `${this.logPrefix( // moduleName, @@ -139,11 +144,33 @@ export default class UIWebSocketServer extends AbstractUIServer { const request = JSON.parse(rawData.toString()) as ProtocolRequest; if (Array.isArray(request) === false) { - throw new BaseError('UI protocol request is not an array'); + logger.error( + `${this.logPrefix( + moduleName, + 'validateRawDataRequest' + )} UI protocol request is not an array:`, + request + ); + return false; } if (request.length !== 3) { - throw new BaseError('UI protocol request is malformed'); + logger.error( + `${this.logPrefix(moduleName, 'validateRawDataRequest')} UI protocol request is malformed:`, + request + ); + return false; + } + + if (uuid.validate(request[0]) === false) { + logger.error( + `${this.logPrefix( + moduleName, + 'validateRawDataRequest' + )} UI protocol request UUID field is invalid:`, + request + ); + return false; } return request; -- 2.34.1