]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix: use truncateId consistently for all user identifiers in logs
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 18 Mar 2026 22:21:10 +0000 (23:21 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 18 Mar 2026 22:21:10 +0000 (23:21 +0100)
Replace 16 bare idTag/identifier.value references and 4 manual
substring(0,8) truncations with truncateId() across OCPP 1.6
ResponseService, IncomingRequestService, auth adapters, strategies,
and helpers.

User identifiers (RFID tags, auth tokens) are now consistently
truncated in all log output to prevent sensitive data exposure.

src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.ts
src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts
src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts
src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts
src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts
src/charging-station/ocpp/auth/utils/AuthHelpers.ts
tests/charging-station/ocpp/auth/utils/AuthHelpers.test.ts

index 18cf59b7fc9f791eccb1ba1d9e00e78806a10998..9471353a11d0225528cd5cae39179d9042f88092 100644 (file)
@@ -113,6 +113,7 @@ import {
   isNotEmptyString,
   logger,
   sleep,
+  truncateId,
 } from '../../../utils/index.js'
 import { OCPPConstants } from '../OCPPConstants.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
@@ -1241,7 +1242,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           `${chargingStation.logPrefix()} ${moduleName}.handleRequestRemoteStartTransaction: Remote start transaction REJECTED on ${
             // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
             chargingStation.stationInfo?.chargingStationId
-          }, idTag '${commandPayload.idTag}': no available connector found`
+          }, idTag '${truncateId(commandPayload.idTag)}': no available connector found`
         )
         return OCPP16Constants.OCPP_RESPONSE_REJECTED
       }
index 933d1916ba743527b0ca4ef5ca5d7b7d2f21b7a4..2ba96a03cc469cce82f6059ea776d5a6e7a4d0a8 100644 (file)
@@ -34,7 +34,7 @@ import {
   ReservationTerminationReason,
   type ResponseHandler,
 } from '../../../types/index.js'
-import { Constants, convertToInt, logger } from '../../../utils/index.js'
+import { Constants, convertToInt, logger, truncateId } from '../../../utils/index.js'
 import { OCPPResponseService } from '../OCPPResponseService.js'
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
@@ -182,9 +182,9 @@ export class OCPP16ResponseService extends OCPPResponseService {
         authorizeConnectorStatus.idTagAuthorized = false
         delete authorizeConnectorStatus.authorizeIdTag
         logger.debug(
-          `${chargingStation.logPrefix()} ${moduleName}.handleResponseAuthorize: idTag '${
+          `${chargingStation.logPrefix()} ${moduleName}.handleResponseAuthorize: idTag '${truncateId(
             requestPayload.idTag
-          }' rejected with status '${payload.idTagInfo.status}'`
+          )}' rejected with status '${payload.idTagInfo.status}'`
         )
       }
     } else {
@@ -394,9 +394,9 @@ export class OCPP16ResponseService extends OCPPResponseService {
         if (reservation != null) {
           if (reservation.idTag !== requestPayload.idTag) {
             logger.warn(
-              `${chargingStation.logPrefix()} ${moduleName}.handleResponseStartTransaction: Reserved transaction ${payload.transactionId.toString()} started with a different idTag ${
+              `${chargingStation.logPrefix()} ${moduleName}.handleResponseStartTransaction: Reserved transaction ${payload.transactionId.toString()} started with a different idTag ${truncateId(
                 requestPayload.idTag
-              } than the reservation one ${reservation.idTag}`
+              )} than the reservation one ${truncateId(reservation.idTag)}`
             )
           }
           if (hasReservationExpired(reservation)) {
@@ -432,7 +432,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
         `${chargingStation.logPrefix()} ${moduleName}.handleResponseStartTransaction: Transaction with id ${payload.transactionId.toString()} STARTED on ${
           // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
           chargingStation.stationInfo?.chargingStationId
-        }#${connectorId.toString()} for idTag '${requestPayload.idTag}'`
+        }#${connectorId.toString()} for idTag '${truncateId(requestPayload.idTag)}'`
       )
       if (chargingStation.stationInfo?.powerSharedByConnectors === true) {
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -453,9 +453,9 @@ export class OCPP16ResponseService extends OCPPResponseService {
         `${chargingStation.logPrefix()} ${moduleName}.handleResponseStartTransaction: Starting transaction with id ${payload.transactionId.toString()} REJECTED on ${
           // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
           chargingStation.stationInfo?.chargingStationId
-        }#${connectorId.toString()} with status '${payload.idTagInfo.status}', idTag '${
+        }#${connectorId.toString()} with status '${payload.idTagInfo.status}', idTag '${truncateId(
           requestPayload.idTag
-        }'${
+        )}'${
           OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, requestPayload.idTag)
             ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
               `, reservationId '${requestPayload.reservationId?.toString()}'`
index 0b34715c8786735aa6fc77a414b21c99829a8054..83cd7fe2716d23412bad5b2f4aefc92e4cda7858 100644 (file)
@@ -15,7 +15,7 @@ import {
   StandardParametersKey,
 } from '../../../../types/index.js'
 import { OCPPVersion } from '../../../../types/ocpp/OCPPVersion.js'
-import { logger } from '../../../../utils/index.js'
+import { logger, truncateId } from '../../../../utils/index.js'
 import {
   AuthContext,
   AuthenticationMethod,
@@ -55,7 +55,7 @@ export class OCPP16AuthAdapter implements OCPPAuthAdapter {
 
     try {
       logger.debug(
-        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier ${identifier.value} via OCPP 1.6`
+        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier ${truncateId(identifier.value)} via OCPP 1.6`
       )
 
       // Mark connector as authorizing if provided
index d481738e1ed3267095f54d7273533b3ddea6f054..ec5dbd58999e29b2c886b07ff170bf58749d3442 100644 (file)
@@ -20,7 +20,7 @@ import {
   OCPP20TriggerReasonEnumType,
 } from '../../../../types/ocpp/2.0/Transaction.js'
 import { OCPPVersion } from '../../../../types/ocpp/OCPPVersion.js'
-import { logger } from '../../../../utils/index.js'
+import { logger, truncateId } from '../../../../utils/index.js'
 import {
   AuthContext,
   AuthenticationMethod,
@@ -64,7 +64,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter {
 
     try {
       logger.debug(
-        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier ${identifier.value} via OCPP 2.0 TransactionEvent`
+        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier ${truncateId(identifier.value)} via OCPP 2.0 TransactionEvent`
       )
 
       // Check if remote authorization is configured
index 36a0605930fa3126e47b1f54df694acdb3af077f..a570d32b7683799928dc68003c24d227b1621f7a 100644 (file)
@@ -92,7 +92,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService {
     this.metrics.totalRequests++
 
     logger.debug(
-      `${this.chargingStation.logPrefix()} ${moduleName}.authenticate: Starting authentication for identifier: ${JSON.stringify(request.identifier)}`
+      `${this.chargingStation.logPrefix()} ${moduleName}.authenticate: Starting authentication for identifier: ${truncateId(request.identifier.value)}`
     )
 
     // Try each strategy in priority order
@@ -431,7 +431,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService {
    */
   public invalidateCache (identifier: UnifiedIdentifier): void {
     logger.debug(
-      `${this.chargingStation.logPrefix()} ${moduleName}.invalidateCache: Invalidating cache for identifier: ${identifier.value}`
+      `${this.chargingStation.logPrefix()} ${moduleName}.invalidateCache: Invalidating cache for identifier: ${truncateId(identifier.value)}`
     )
 
     // Invalidate in local strategy
@@ -439,7 +439,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService {
     if (localStrategy) {
       localStrategy.invalidateCache(identifier.value)
       logger.info(
-        `${this.chargingStation.logPrefix()} ${moduleName}.invalidateCache: Cache invalidated for identifier: ${identifier.value}`
+        `${this.chargingStation.logPrefix()} ${moduleName}.invalidateCache: Cache invalidated for identifier: ${truncateId(identifier.value)}`
       )
     } else {
       logger.debug(
index 181b95aa69f5a4853d1d531b2c4610578b14b4f9..a78b14756ad34b91f098be351bc99db87bde0673 100644 (file)
@@ -70,7 +70,7 @@ export class LocalAuthStrategy implements AuthStrategy {
 
     try {
       logger.debug(
-        `${moduleName}: Authenticating ${request.identifier.value} for ${request.context}`
+        `${moduleName}: Authenticating ${truncateId(request.identifier.value)} for ${request.context}`
       )
 
       // 1. Try local authorization list first (highest priority)
@@ -117,7 +117,9 @@ export class LocalAuthStrategy implements AuthStrategy {
         }
       }
 
-      logger.debug(`${moduleName}: No local authorization found for ${request.identifier.value}`)
+      logger.debug(
+        `${moduleName}: No local authorization found for ${truncateId(request.identifier.value)}`
+      )
       return undefined
     } catch (error) {
       const errorMessage = getErrorMessage(error)
index 3e406e9532ea37140ffef305ce8523fa54e9395f..6a1fb91b3db872333e12fec73d8acbb7ccba20db 100644 (file)
@@ -7,7 +7,7 @@ import type {
 } from '../interfaces/OCPPAuthService.js'
 import type { AuthConfiguration, AuthorizationResult, AuthRequest } from '../types/AuthTypes.js'
 
-import { ensureError, getErrorMessage, logger } from '../../../../utils/index.js'
+import { ensureError, getErrorMessage, logger, truncateId } from '../../../../utils/index.js'
 import {
   AuthenticationError,
   AuthenticationMethod,
@@ -93,7 +93,7 @@ export class RemoteAuthStrategy implements AuthStrategy {
 
     try {
       logger.debug(
-        `${moduleName}: Authenticating ${request.identifier.value.substring(0, 8)}... via CSMS for ${request.context}`
+        `${moduleName}: Authenticating ${truncateId(request.identifier.value)} via CSMS for ${request.context}`
       )
 
       // Get appropriate adapter for OCPP version
@@ -126,7 +126,7 @@ export class RemoteAuthStrategy implements AuthStrategy {
           const isInLocalList = await this.localAuthListManager.getEntry(request.identifier.value)
           if (isInLocalList) {
             logger.debug(
-              `${moduleName}: Skipping cache for local list identifier: ${request.identifier.value.substring(0, 8)}...`
+              `${moduleName}: Skipping cache for local list identifier: ${truncateId(request.identifier.value)}`
             )
           } else {
             this.cacheResult(
@@ -149,7 +149,7 @@ export class RemoteAuthStrategy implements AuthStrategy {
       }
 
       logger.debug(
-        `${moduleName}: No remote authorization result for ${request.identifier.value.substring(0, 8)}...`
+        `${moduleName}: No remote authorization result for ${truncateId(request.identifier.value)}`
       )
       return undefined
     } catch (error) {
@@ -385,7 +385,7 @@ export class RemoteAuthStrategy implements AuthStrategy {
       const cacheTtl = ttl ?? result.cacheTtl ?? 300 // Default 5 minutes
       this.authCache.set(identifier, result, cacheTtl)
       logger.debug(
-        `${moduleName}: Cached result for ${identifier.substring(0, 8)}... (TTL: ${String(cacheTtl)}s)`
+        `${moduleName}: Cached result for ${truncateId(identifier)} (TTL: ${String(cacheTtl)}s)`
       )
     } catch (error) {
       const errorMessage = getErrorMessage(error)
index 593295b20f11d619a9c261e25dc7f6e1ac69eb53..3358c720ca181315d2733b7e5ca08a2ccfb32c3a 100644 (file)
@@ -6,10 +6,10 @@ import type {
   UnifiedIdentifier,
 } from '../types/AuthTypes.js'
 
+import { truncateId } from '../../../../utils/index.js'
 import { AuthorizationStatus } from '../types/AuthTypes.js'
 
 /**
- * Compute remaining TTL in seconds from an expiry date.
  * @param expiryDate - Expiry timestamp to compute TTL from
  * @returns TTL in seconds, or undefined if already expired or no date provided
  */
@@ -80,8 +80,7 @@ function createRejectedResult (
  * @returns Formatted error string with truncated identifier
  */
 function formatAuthError (error: Error, identifier: UnifiedIdentifier): string {
-  const identifierValue = identifier.value.substring(0, 8) + '...'
-  return `Authentication failed for identifier ${identifierValue} (${identifier.type}): ${error.message}`
+  return `Authentication failed for identifier ${truncateId(identifier.value)} (${identifier.type}): ${error.message}`
 }
 
 /**
index b8fc286cf10d16b747bcd34338647a318ed82f45..653561cd623575c5af32794da98feda37a3e695a 100644 (file)
@@ -151,7 +151,8 @@ await describe('AuthHelpers', async () => {
 
       const message = AuthHelpers.formatAuthError(error, identifier)
 
-      assert.ok(message.includes('SHORT...'))
+      assert.ok(message.includes('SHORT'))
+      assert.ok(!message.includes('SHORT...'))
       assert.ok(message.includes('Local'))
       assert.ok(message.includes('Invalid format'))
     })