ITADN

Bug: Label_2P_FM3 / FM4 / FM5 missing-header guard is dead (split.length is never 0); FM3 then throws on header[1].length, FM4/FM5 emit undefined fields

#462Openkevinelliott 创建于 2026-06-26
bug
K
kevinelliottcommented
## Summary All three `Label_2P_FM*` plugins try to detect a missing `FM3 ` / `FM4` / `FM5 ` preamble with `if (header.length == 0)` after splitting `parts[0]`. `String.prototype.split` always returns at least one element, so the guard is **never true** — it's dead code. When the preamble token actually is missing, `header[1]` is `undefined`: - **FM3** dereferences `header[1].length` → throws `TypeError: Cannot read properties of undefined (reading 'length')`. Because the dispatch in `MessageDecoder.decode` does not wrap individual plugins in `try`/`catch`, the exception propagates out and aborts the whole decode. - **FM4** and **FM5** silently pass `undefined` to `ResultFormatter.departureAirport`, producing a "successfully decoded" result with `departureAirport: { value: undefined }`. All three plugins are reachable from `MessageDecoder` for label `2P` (see `MessageDecoder.ts:40-42`), and any 2P message whose comma-count matches the plugin's expectation but whose `parts[0]` doesn't actually contain the FMx token hits this path. This is not a duplicate of #459 (different file, different handlers). ## Affected code Same shape in three files. FM3: ```ts // lib/plugins/Label_2P_FM3.ts:24-40 if (parts.length === 7) { const header = parts[0].split('FM3 '); if (header.length == 0) { // never true ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } if (header[0].length > 0) { ResultFormatter.unknown(decodeResult, header[0].substring(0, 4)); ResultFormatter.flightNumber(decodeResult, header[0].substring(4)); } if (header[1].length === 4) { ... } // throws when header[1] is undefined ``` FM4 (`lib/plugins/Label_2P_FM4.ts:23-37`) and FM5 (`lib/plugins/Label_2P_FM5.ts:23-34`) have the same dead guard and then pass `header[1]` straight into `ResultFormatter.departureAirport` without checking. Note FM3 and FM5 split on `'FM3 '`/`'FM5 '` (with trailing space) while FM4 splits on `'FM4'` (no space). The fix is the same shape in all three. ## Reproduction ```ts const decoder = new MessageDecoder(); // FM3 throws decoder.decode({ label: '2P', text: 'noFMheader,1454,N 45.206,E 17.726,34030, 440,98', }); // → TypeError: Cannot read properties of undefined (reading 'length') // FM4 silently produces undefined fields (10-field text, no 'FM4' token) decoder.decode({ label: '2P', text: 'noFMheader,KORD,2114000,1518,N 45.206,E 17.726,34030,180,foo,bar', }); // → decoded: true, but result includes departureAirport.value === undefined // FM5 silently produces undefined fields (12-field text, no 'FM5 ' token) decoder.decode({ label: '2P', text: 'noFMhdr,KORD,142000,1518,N 45.206,E 17.726,34030,a,b,c,FLT123,d', }); // → decoded: true, but result includes departureAirport.value === undefined ``` ## Suggested fix Change the guard in each plugin to check for the case the comment actually describes — the FMx token wasn't present: ```ts if (header.length < 2) { // or: if (parts[0].indexOf('FM3 ') === -1) ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } ``` (Replace `'FM3 '` / `'FM4'` / `'FM5 '` accordingly per file.) ## Test coverage The existing `Label_2P_FM3` test only covers well-formed `FM3 …` input, so the FM3 throw is uncovered. Adding a "no preamble" case per plugin that asserts the result has `decoded === false` and (for FM3) that the call does not throw would lock the fix in.
0 条评论