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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions