Severity: medium | Confidence: confirmed | Area: type-safety | Source: codebase review 2026-06-30, finding TS-03
Problem
The Signal K WebSocket connection in signalk-delta.service.ts is typed as WebSocketSubject<object> (line 62). Raw frames from the socket are passed directly into processWebsocketMessage(message: ISignalKDeltaMessage) (line 221) with no runtime guard in between. Because every field of ISignalKDeltaMessage is optional, TypeScript's structural type system accepts an object value as a valid argument — this is a compile-time coincidence, not a validation step. The downstream parsers then unconditionally trust the shape: parseUpdates calls item.path.startsWith(...) without checking that path is a non-null string, and parseSkMeta dereferences metadata.value.properties[key] where properties is typed object with no null guard. Malformed or hostile delta frames therefore produce uncaught runtime exceptions rather than explicit, recoverable failures.
Impact
Any deviation from the expected Signal K delta shape — a buggy server plugin, a protocol version mismatch, or a network interception attack — can throw an unhandled TypeError deep inside the service. In a marine instrument panel context this means silent widget failures or a blank display at sea with no actionable error surfaced to the user. The project already ships @signalk/server-api as a dependency but does not use its validated delta types at this boundary, so stronger typing is available at no extra cost.
Affected code
src/app/core/services/signalk-delta.service.ts:62 — socket typed WebSocketSubject<object>
src/app/core/services/signalk-delta.service.ts:221 — raw frame passed to processWebsocketMessage with no guard
src/app/core/services/signalk-delta.service.ts:333-357 — processWebsocketMessage(message: ISignalKDeltaMessage) entry point
src/app/core/services/signalk-delta.service.ts:479-495 — parseSkMeta dereferences metadata.value.properties[key] without null check
src/app/core/interfaces/signalk-interfaces.ts:67-103 — ISignalKDeltaMessage has every field optional, making it structurally equivalent to object
src/app/core/interfaces/signalk-interfaces.ts:186 — properties typed as bare object inside ISkMetadata
Suggested direction
Add a type guard at the WebSocket boundary before dispatching to processWebsocketMessage. The discriminator fields already present on valid deltas — updates, requestId, self, errorMessage — are sufficient to narrow the type without a heavy schema library. At minimum, guard that item.path is a non-null string before calling .startsWith. Consider adopting the delta types from @signalk/server-api (already a declared dependency) in place of the hand-rolled ISignalKDeltaMessage to benefit from any upstream validation improvements. The change is self-contained and does not require restructuring the service.
Verification
Verified against the codebase at commit HEAD of the SKip fork (confidence: confirmed). All cited file paths and line references were confirmed to exist and match the described code.
Severity: medium | Confidence: confirmed | Area: type-safety | Source: codebase review 2026-06-30, finding
TS-03Problem
The Signal K WebSocket connection in
signalk-delta.service.tsis typed asWebSocketSubject<object>(line 62). Raw frames from the socket are passed directly intoprocessWebsocketMessage(message: ISignalKDeltaMessage)(line 221) with no runtime guard in between. Because every field ofISignalKDeltaMessageis optional, TypeScript's structural type system accepts anobjectvalue as a valid argument — this is a compile-time coincidence, not a validation step. The downstream parsers then unconditionally trust the shape:parseUpdatescallsitem.path.startsWith(...)without checking thatpathis a non-null string, andparseSkMetadereferencesmetadata.value.properties[key]wherepropertiesis typedobjectwith no null guard. Malformed or hostile delta frames therefore produce uncaught runtime exceptions rather than explicit, recoverable failures.Impact
Any deviation from the expected Signal K delta shape — a buggy server plugin, a protocol version mismatch, or a network interception attack — can throw an unhandled
TypeErrordeep inside the service. In a marine instrument panel context this means silent widget failures or a blank display at sea with no actionable error surfaced to the user. The project already ships@signalk/server-apias a dependency but does not use its validated delta types at this boundary, so stronger typing is available at no extra cost.Affected code
src/app/core/services/signalk-delta.service.ts:62— socket typedWebSocketSubject<object>src/app/core/services/signalk-delta.service.ts:221— raw frame passed toprocessWebsocketMessagewith no guardsrc/app/core/services/signalk-delta.service.ts:333-357—processWebsocketMessage(message: ISignalKDeltaMessage)entry pointsrc/app/core/services/signalk-delta.service.ts:479-495—parseSkMetadereferencesmetadata.value.properties[key]without null checksrc/app/core/interfaces/signalk-interfaces.ts:67-103—ISignalKDeltaMessagehas every field optional, making it structurally equivalent toobjectsrc/app/core/interfaces/signalk-interfaces.ts:186—propertiestyped as bareobjectinsideISkMetadataSuggested direction
Add a type guard at the WebSocket boundary before dispatching to
processWebsocketMessage. The discriminator fields already present on valid deltas —updates,requestId,self,errorMessage— are sufficient to narrow the type without a heavy schema library. At minimum, guard thatitem.pathis a non-null string before calling.startsWith. Consider adopting the delta types from@signalk/server-api(already a declared dependency) in place of the hand-rolledISignalKDeltaMessageto benefit from any upstream validation improvements. The change is self-contained and does not require restructuring the service.Verification
Verified against the codebase at commit HEAD of the SKip fork (confidence: confirmed). All cited file paths and line references were confirmed to exist and match the described code.