Improve payload type checking in OCPP, UI and Broadcast Channel
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 24 Aug 2022 20:10:01 +0000 (22:10 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 24 Aug 2022 20:10:01 +0000 (22:10 +0200)
protocols

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/Bootstrap.ts
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationWorkerBroadcastChannel.ts
src/charging-station/UIServiceWorkerBroadcastChannel.ts
src/charging-station/ui-server/ui-services/AbstractUIService.ts
src/types/ChargingStationWorker.ts
src/ui/web/src/composable/UIClient.ts
src/utils/Configuration.ts

index 516730b9ec87da6d25e0561cd39bbb7b2c763832..2bc650cc32ec9116f638b33da3eb1d4e4a361d21 100644 (file)
@@ -14,6 +14,7 @@ import {
   ChargingStationData,
   ChargingStationWorkerData,
   ChargingStationWorkerMessage,
+  ChargingStationWorkerMessageData,
   ChargingStationWorkerMessageEvents,
 } from '../types/ChargingStationWorker';
 import { StationTemplateUrl } from '../types/ConfigurationData';
@@ -173,14 +174,14 @@ export default class Bootstrap {
             workerChoiceStrategy: Configuration.getWorker().poolStrategy,
           },
           messageHandler: this.messageHandler.bind(this) as (
-            msg: ChargingStationWorkerMessage<ChargingStationData | Statistics>
+            msg: ChargingStationWorkerMessage<ChargingStationWorkerMessageData>
           ) => void,
         }
       ));
   }
 
   private messageHandler(
-    msg: ChargingStationWorkerMessage<ChargingStationData | Statistics>
+    msg: ChargingStationWorkerMessage<ChargingStationWorkerMessageData>
   ): void {
     // logger.debug(
     //   `${this.logPrefix()} ${moduleName}.messageHandler: Worker channel message received: ${JSON.stringify(
index 25d9733b0e67063939d7174bae430e39bb058841..5631a9845d8c2df6c131aedcf240b76f02f9b180 100644 (file)
@@ -626,7 +626,7 @@ export default class ChargingStation {
       );
       this.getConnectorStatus(connectorId).chargingProfiles = [];
     }
-    if (!Array.isArray(this.getConnectorStatus(connectorId).chargingProfiles)) {
+    if (Array.isArray(this.getConnectorStatus(connectorId).chargingProfiles) === false) {
       logger.error(
         `${this.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`
       );
@@ -1419,7 +1419,7 @@ export default class ChargingStation {
     let errMsg: string;
     try {
       const request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse;
-      if (Utils.isIterable(request)) {
+      if (Array.isArray(request) === true) {
         [messageType, messageId] = request;
         // Check the type of message
         switch (messageType) {
@@ -1456,12 +1456,12 @@ export default class ChargingStation {
             }
             // Respond
             cachedRequest = this.requests.get(messageId);
-            if (Utils.isIterable(cachedRequest)) {
+            if (Array.isArray(cachedRequest) === true) {
               [responseCallback, , requestCommandName, requestPayload] = cachedRequest;
             } else {
               throw new OCPPError(
                 ErrorType.PROTOCOL_ERROR,
-                `Cached request for message id ${messageId} response is not iterable`,
+                `Cached request for message id ${messageId} response is not an array`,
                 null,
                 cachedRequest as unknown as JsonType
               );
@@ -1486,12 +1486,12 @@ export default class ChargingStation {
               );
             }
             cachedRequest = this.requests.get(messageId);
-            if (Utils.isIterable(cachedRequest)) {
+            if (Array.isArray(cachedRequest) === true) {
               [, errorCallback, requestCommandName] = cachedRequest;
             } else {
               throw new OCPPError(
                 ErrorType.PROTOCOL_ERROR,
-                `Cached request for message id ${messageId} error response is not iterable`,
+                `Cached request for message id ${messageId} error response is not an array`,
                 null,
                 cachedRequest as unknown as JsonType
               );
@@ -1512,7 +1512,7 @@ export default class ChargingStation {
         }
         parentPort.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
       } else {
-        throw new OCPPError(ErrorType.PROTOCOL_ERROR, 'Incoming message is not iterable', null, {
+        throw new OCPPError(ErrorType.PROTOCOL_ERROR, 'Incoming message is not an array', null, {
           payload: request,
         });
       }
index 18cbadefdaf63d4915e6bb497abe0628072949c2..4e43e5ad024ac4706d54a45ef761f60c35e95094 100644 (file)
@@ -38,6 +38,9 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     if (this.isResponse(messageEvent.data)) {
       return;
     }
+    if (Array.isArray(messageEvent.data) === false) {
+      throw new BaseError('Worker broadcast channel protocol request is not an array');
+    }
 
     const [uuid, command, requestPayload] = messageEvent.data as BroadcastChannelRequest;
 
index d50d514a469496159b5aad15f752bde3c3a0d031..a94f2b31d263fa086153984c61d36a8ddffb4aa0 100644 (file)
@@ -1,3 +1,4 @@
+import BaseError from '../exception/BaseError';
 import { BroadcastChannelResponse, MessageEvent } from '../types/WorkerBroadcastChannel';
 import logger from '../utils/Logger';
 import type AbstractUIService from './ui-server/ui-services/AbstractUIService';
@@ -19,6 +20,9 @@ export default class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChan
     if (this.isRequest(messageEvent.data)) {
       return;
     }
+    if (Array.isArray(messageEvent.data) === false) {
+      throw new BaseError('Worker broadcast channel protocol response is not an array');
+    }
     const [uuid, responsePayload] = messageEvent.data as BroadcastChannelResponse;
 
     this.uiService.sendResponse(uuid, responsePayload);
index 224f78a2e03dd827f82f9b33c97325752003857e..ff44954ad234675cbab93059aef76a8c8cb9305e 100644 (file)
@@ -13,7 +13,6 @@ import {
   ResponseStatus,
 } from '../../../types/UIProtocol';
 import logger from '../../../utils/Logger';
-import Utils from '../../../utils/Utils';
 import Bootstrap from '../../Bootstrap';
 import UIServiceWorkerBroadcastChannel from '../../UIServiceWorkerBroadcastChannel';
 import type { AbstractUIServer } from '../AbstractUIServer';
@@ -119,8 +118,8 @@ export default abstract class AbstractUIService {
 
     const data = JSON.parse(rawData.toString()) as JsonType[];
 
-    if (Utils.isIterable(data) === false) {
-      throw new BaseError('UI protocol request is not iterable');
+    if (Array.isArray(data) === false) {
+      throw new BaseError('UI protocol request is not an array');
     }
 
     if (data.length !== 3) {
index 1a48e96da8dc3557ce4be95d600a591ea00b8eb4..0fba406bc8bea212492f8e365d94f451d9d6f0cf 100644 (file)
@@ -1,6 +1,7 @@
 import ChargingStationInfo from './ChargingStationInfo';
 import { ConnectorStatus } from './ConnectorStatus';
 import { JsonObject } from './JsonType';
+import Statistics from './Statistics';
 import { WorkerData, WorkerMessage, WorkerMessageEvents } from './Worker';
 
 export interface ChargingStationWorkerOptions extends JsonObject {
@@ -34,6 +35,8 @@ export const ChargingStationWorkerMessageEvents = {
   ...ChargingStationMessageEvents,
 };
 
+export type ChargingStationWorkerMessageData = ChargingStationData | Statistics;
+
 export interface ChargingStationWorkerMessage<T extends WorkerData>
   extends Omit<WorkerMessage<T>, 'id'> {
   id: ChargingStationWorkerMessageEvents;
index eb8e013e371e8e958e654bf00b62b80846a00922..6803ef03de2674486a9941406573ce48d4a3a48a 100644 (file)
@@ -112,8 +112,8 @@ export default class UIClient {
   private responseHandler(messageEvent: MessageEvent<string>): void {
     const data = JSON.parse(messageEvent.data) as ProtocolResponse;
 
-    if (Utils.isIterable(data) === false) {
-      throw new Error('Response not iterable: ' + JSON.stringify(data, null, 2));
+    if (Array.isArray(data) === false) {
+      throw new Error('Response not an array: ' + JSON.stringify(data, null, 2));
     }
 
     const [uuid, response] = data;
index c185133a0a1d92a410a427aba0eee8517c05bb30..5de867147666b9e2b5dfd201af3a20edfc968358 100644 (file)
@@ -389,7 +389,7 @@ export default class Configuration {
   }
 
   private static isObject(item): boolean {
-    return item && typeof item === 'object' && !Array.isArray(item);
+    return item && typeof item === 'object' && Array.isArray(item) === false;
   }
 
   private static deepMerge(target: object, ...sources: object[]): object {