Summary
Label_2P_FM4.decode and Label_2P_FM5.decode feed Number(parts[4].replaceAll(' ', '')) / Number(parts[5].replaceAll(' ', '')) directly into ResultFormatter.position with no validation. When either coordinate field is empty (adjacent commas in the CSV, e.g. parts[4] === '' || parts[5] === ''), Number('') returns 0 (not NaN), so ResultFormatter.position's isNaN guard doesn't fire — and the plugin silently reports latitude 0, longitude 0 as if the aircraft were at 0°N 0°E off the coast of Africa. This is the exact same silent-corruption pattern as #487, in two sibling plugins that share the FM3 family's shape but weren't covered by that fix.
Affected code
lib/plugins/Label_2P_FM4.ts:48-51
ResultFormatter.position(decodeResult, {
latitude: Number(parts[4].replaceAll(' ', '')), // ← Number('') === 0
longitude: Number(parts[5].replaceAll(' ', '')), // ← Number('') === 0
});
lib/plugins/Label_2P_FM5.ts:44-47
ResultFormatter.position(decodeResult, {
latitude: Number(parts[4].replaceAll(' ', '')), // ← Number('') === 0
longitude: Number(parts[5].replaceAll(' ', '')), // ← Number('') === 0
});
Neither plugin has a hemisphere-prefix branch to guard against (unlike FM3, which has both an N…/S… branch and a numeric-only else-branch), so every invocation of these plugins with an empty coordinate CSV field silently emits (0, 0). ResultFormatter.position (lib/utils/result_formatter.ts:51-65) rejects only NaN, not exactly-0, so it lets the bogus point through.
The equator/prime-meridian display bug in #452 compounds the visual output: today the formatted string reads "0.000 N, 0.000 E"; after #452 is fixed it will read "0.000 N, 0.000 W" (depending on which sign convention wins). Either way, users see a real-looking position for a completely unpopulated field.
Reproduction
const decoder = new MessageDecoder();
// FM4: 10 comma-separated fields, parts[4] and parts[5] are empty (adjacent commas)
decoder.decode({
label: '2P',
text: 'XXFM4EGLL,KJFK,151400,153000,,,35000,270,x,y',
});
// decoded === true
// raw.position === { latitude: 0, longitude: 0 }
// formatted.items includes an 'aircraft_position' entry showing "0.000 N, 0.000 E"
// FM5: 12 comma-separated fields, parts[4] and parts[5] empty
decoder.decode({
label: '2P',
text: 'XXFM5 EGLL,KJFK,151400,153000,,,35000,x,y,z,ABC123,q',
});
// same silent (0, 0) result
The existing test files (lib/plugins/Label_2P_FM4.test.ts, Label_2P_FM5.test.ts) only exercise valid numeric coordinates and never the empty-field path.
Suggested fix
Same shape as the fix suggested in #487, applied to both files: reject the position push when either coord field is empty or non-numeric before calling ResultFormatter.position:
const latRaw = parts[4].replaceAll(' ', '');
const lonRaw = parts[5].replaceAll(' ', '');
const latNum = Number(latRaw);
const lonNum = Number(lonRaw);
if (latRaw.length > 0 && lonRaw.length > 0 &&
Number.isFinite(latNum) && Number.isFinite(lonNum)) {
ResultFormatter.position(decodeResult, {
latitude: latNum,
longitude: lonNum,
});
}
An alternative that avoids duplicating the guard across the FM3/FM4/FM5 trio (and any future sibling) is to harden ResultFormatter.position itself to skip when both coords are exactly 0 and neither was explicitly provided — but that couples the formatter to input semantics, so the plugin-side guard (matching #487's chosen fix) is preferable.
Test coverage
Add cases to both Label_2P_FM4.test.ts and Label_2P_FM5.test.ts with empty lat/lon parts asserting that no aircraft_position entry is pushed and raw.position is left unset.
Summary
Label_2P_FM4.decodeandLabel_2P_FM5.decodefeedNumber(parts[4].replaceAll(' ', ''))/Number(parts[5].replaceAll(' ', ''))directly intoResultFormatter.positionwith no validation. When either coordinate field is empty (adjacent commas in the CSV, e.g.parts[4] === '' || parts[5] === ''),Number('')returns0(notNaN), soResultFormatter.position'sisNaNguard doesn't fire — and the plugin silently reports latitude 0, longitude 0 as if the aircraft were at 0°N 0°E off the coast of Africa. This is the exact same silent-corruption pattern as #487, in two sibling plugins that share the FM3 family's shape but weren't covered by that fix.Affected code
lib/plugins/Label_2P_FM4.ts:48-51lib/plugins/Label_2P_FM5.ts:44-47Neither plugin has a hemisphere-prefix branch to guard against (unlike FM3, which has both an
N…/S…branch and a numeric-only else-branch), so every invocation of these plugins with an empty coordinate CSV field silently emits(0, 0).ResultFormatter.position(lib/utils/result_formatter.ts:51-65) rejects onlyNaN, not exactly-0, so it lets the bogus point through.The equator/prime-meridian display bug in #452 compounds the visual output: today the formatted string reads
"0.000 N, 0.000 E"; after #452 is fixed it will read"0.000 N, 0.000 W"(depending on which sign convention wins). Either way, users see a real-looking position for a completely unpopulated field.Reproduction
The existing test files (
lib/plugins/Label_2P_FM4.test.ts,Label_2P_FM5.test.ts) only exercise valid numeric coordinates and never the empty-field path.Suggested fix
Same shape as the fix suggested in #487, applied to both files: reject the position push when either coord field is empty or non-numeric before calling
ResultFormatter.position:An alternative that avoids duplicating the guard across the FM3/FM4/FM5 trio (and any future sibling) is to harden
ResultFormatter.positionitself to skip when both coords are exactly0and neither was explicitly provided — but that couples the formatter to input semantics, so the plugin-side guard (matching #487's chosen fix) is preferable.Test coverage
Add cases to both
Label_2P_FM4.test.tsandLabel_2P_FM5.test.tswith empty lat/lon parts asserting that noaircraft_positionentry is pushed andraw.positionis left unset.