Skip to content

Bug: Label_83 variant-2 branch dereferences fields[2..6] with no length guard — short/truncated 4DH3 ETAT2… inputs throw TypeError and abort MessageDecoder.decode() #470

Description

@kevinelliott

Summary

lib/plugins/Label_83.ts variant-2 handler activates whenever text.startsWith('4DH3 ETAT2'). It then splits on whitespace and immediately reads fields[2].length, fields[3].split('/'), fields[4].replace(...), and fields[6] + '00' — none of which are guarded by a length check. Any short-form or truncated 4DH3 ETAT2… input throws TypeError at line 31. Because MessageDecoder.decode() does not wrap plugin calls in try/catch, the exception aborts the entire decode call for that message.

Location

lib/plugins/Label_83.ts:28-42

if (text.startsWith('4DH3 ETAT2')) {
  // variant 2
  const fields = text.split(WS_REGEX);
  if (fields[2].length > 5) {                                // <-- throws when fields.length <= 2
    decodeResult.raw.day = fields[2].substring(5);
  }
  ResultFormatter.unknown(decodeResult, fields[2].substring(0, 4));
  const subfields = fields[3].split('/');                    // <-- throws if fields.length <= 3
  ResultFormatter.departureAirport(decodeResult, subfields[0]);
  ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
  ResultFormatter.tail(decodeResult, fields[4].replace(ALL_DOTS_REGEX, ''));
  ResultFormatter.eta(
    decodeResult,
    DateTimeUtils.convertHHMMSSToTod(fields[6] + '00'),      // <-- undefined + '00' → NaN downstream
  );
}

Reproduction

const decoder = new MessageDecoder();
decoder.decode({ label: '83', text: '4DH3 ETAT2' });
// Uncaught TypeError: Cannot read properties of undefined (reading 'length')
//   at Label_83.decode (Label_83.ts:31)
//   at MessageDecoder.decode (MessageDecoder.ts:198)

'4DH3 ETAT2'.split(/\s+/) produces ['4DH3', 'ETAT2'] (length 2), so fields[2] is undefined and fields[2].length throws. Real-world truncation of the airline preamble message (or a variant with a slightly different but still 4DH3 ETAT2-prefixed header) exposes this.

Expected

For any input starting with 4DH3 ETAT2 but shorter than the fully-fleshed variant-2 form, the plugin should either return decoded: false or gracefully degrade — it should not throw.

Actual

Uncaught TypeError propagates out of MessageDecoder.decode(), aborting the decode call entirely. Later plugins in the candidates list are never tried, and the caller receives a thrown error rather than a normal DecodeResult.

Recommendation

Add an early length guard at the top of the variant-2 branch:

if (text.startsWith('4DH3 ETAT2')) {
  const fields = text.split(WS_REGEX);
  if (fields.length < 7) {
    decodeResult.decoded = false;
    ResultFormatter.unknown(decodeResult, text);
  } else {
    // existing decode …
  }
}

Consider also hardening MessageDecoder.decode() to catch plugin exceptions so a single buggy plugin cannot take down the whole decoder for a message — but the primary fix belongs in this plugin.

Notes

  • The existing test only covers the fully-fleshed happy-path input '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'. No truncation test.
  • Label_83 has no preambles in its qualifiers, so this branch is reached whenever the startsWith succeeds — even if surrounding routing changes.

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