UI server: do not crash at payload validation
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 5 Sep 2022 22:55:24 +0000 (00:55 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 5 Sep 2022 22:55:24 +0000 (00:55 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ChargingStationWorkerBroadcastChannel.ts
src/charging-station/UIServiceWorkerBroadcastChannel.ts
src/charging-station/WorkerBroadcastChannel.ts
src/charging-station/ui-server/UIWebSocketServer.ts

index 3000626eced8cbd712fc6e047082cd9d54753a18..6ab170e5ba3a49c125236518f46577b3300dc0da 100644 (file)
@@ -45,11 +45,14 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
   }
 
   private async requestHandler(messageEvent: MessageEvent): Promise<void> {
-    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 &&
index 650a8b9e77905aa7049ce292b609762b4b7b5f4b..7e22860aa1c28e2b1aadfb3547bd610cf2bcc9fc 100644 (file)
@@ -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),
index 2fa082dddfd844fb872068f91a03e7c957f60ccb..96fe7c17df01c76471c384f03af3605175372013 100644 (file)
@@ -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}:`);
+  }
 }
index 01c57800a293c5d3d9da411812ee6d0388e63f16..ea54bec0526da573c31d1c5915dfe27f270ba890 100644 (file)
@@ -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;