Skip to content

Bug: DateTimeUtils.timestampToString(NaN) throws RangeError, aborting MessageDecoder.decode() for any non-numeric time field #498

Description

@kevinelliott

Summary

DateTimeUtils.timestampToString has no guard against a NaN input. Every timestamp-family ResultFormatter (timestamp, eta, off, on, in, engineStart, engineStop) forwards its numeric arg into it, so when a plugin passes NaN (from convertHHMMSSToTod("ABCD") or similar) the call falls through to new Date(NaN).toISOString() and throws RangeError: Invalid time value. Because MessageDecoder.decode() has no top-level try/catch, one malformed timestamp aborts the entire message decode.

Location

lib/DateTimeUtils.ts:79-92

public static timestampToString(time: number): string {
  const date = new Date(time * 1000);

  if (time < 86400) {           // NaN < 86400  === false
    return date.toISOString().slice(11, 19);
  }
  if (time < 2678400) {          // NaN < 2678400 === false
    return `YYYY-MM-${date.toISOString().slice(8, 19)}`;
  }
  // Falls through -> Invalid Date.toISOString() -> RangeError
  return date.toISOString().slice(0, -5) + 'Z';
}

Reproduction

new MessageDecoder().decode({
  label: '44',
  text: '00ETA03,N38241W081357,330,KBNA,KBWI,1107,ABCD,0208,008.1',
});
// -> passes Label_44_ETA's 9-field length check
// -> data[6] = 'ABCD' -> convertHHMMSSToTod('ABCD') -> NaN
// -> ResultFormatter.timestamp(result, NaN) -> timestampToString(NaN)
// -> RangeError: Invalid time value, escaping MessageDecoder.decode()

Same crash path is reachable from plugins that feed a time field into ResultFormatter.timestamp/eta/off/on/in/engineStart/engineStop without validating the input is numeric — e.g. Label_44_IN, Label_44_OFF/ON, Label_QP/QR/QS, Label_22_OFF, Label_H1_M_POS, Label_H1_StarPOS, Label_16_POSA1, Label_10_Slash.

Impact

Crash-level — one malformed timestamp field takes down the whole decode, not just the field. Related NaN-guard issues (#494, #496) enumerate a different set of ResultFormatter methods and explicitly do not cover the timestamp family, so this is not already tracked.

Suggested fix

Return early if !Number.isFinite(time) (either as a NaN/undefined sentinel or an empty string), and have ResultFormatter.timestamp et al. skip pushing the item if the input isn't finite — mirroring the existing pattern in ResultFormatter.position / flightNumber / altitude.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions