Skip to content

Bug: Label_H1_Paren parseLat regex mis-parses 5-digit latitudes accepted by the outer regex (silent, out-of-range output) #468

Description

@kevinelliott

Summary

lib/plugins/Label_H1_Paren.ts decodes H1 position-report messages of the form (POS-<flight> <lat><lon>/<time> F<alt>\r\nRMK/FUEL <fuel> M<mach>). The outer regex captures the latitude as (?<lat>-?\d{4,5}[NS])4 or 5 digits — but the parseLat helper only understands the 4-digit DDMM form and its inner regex is unanchored, so on a 5-digit input the regex engine slides the match one character to the right and silently returns an out-of-range latitude as the aircraft's position.

Location

lib/plugins/Label_H1_Paren.ts:26,55-62

const regex =
  /^\(POS-(?<flight>\w+)\s+(?<lat>-?\d{4,5}[NS])(?<lon>\d{5}[EW])\/(?<timestamp>\d{6})\s+F(?<alt>\d{3})\r?\nRMK\/FUEL\s+(?<fuel>\d{2,3}\.\d)\s+M(?<mach>\d\.\d{2})\)/;
...
function parseLat(latStr: string): number {
  const match = latStr.match(/(-?)(\d{2})(\d{2})([NS])/);   // <-- unanchored, 2+2 digits only
  ...
}

Reproduction

const decoder = new MessageDecoder();
const result = decoder.decode({
  label: 'H1',
  text: '(POS-KLM296  39117N07600W/234212 F250\r\nRMK/FUEL  37.0 M0.69)',
});
console.log(result.raw.position.latitude); // → 91.28333... (invalid latitude, > 90)

Trace inside parseLat('39117N'):

  • Inner regex /(-?)(\d{2})(\d{2})([NS])/ is unanchored.
  • At offset 0: consumes "", 39, 11; next char is 7 (not [NS]) → fails.
  • At offset 1: consumes "", 91, 17; next char is Nmatches.
  • Returns sign * (91 + 17/60) = 91.283.

Expected

Either (a) the outer regex should be tightened to \d{4} so the 5-digit form fails the outer match (and decoded: false is returned) — or (b) parseLat should be updated to correctly handle the 5-digit DDMMM (degrees + minutes + tenths-of-minute) form and return 39° 11.7' N ≈ 39.195.

Either way, the current behaviour of emitting 91.283 for a syntactically-accepted input is silent, undetectable-downstream corruption of aircraft position data.

Actual

Silent out-of-range latitude (~91°) reported as the aircraft's position. No error, no fallback — decoded: true and decodeLevel: 'partial' are set.

Recommendation

Anchor and widen the helper, and reject offsets that don't consume the whole capture:

function parseLat(latStr: string): number {
  const match = latStr.match(/^(-?)(\d{2})(\d{2}(?:\d)?)([NS])$/);
  if (!match) return NaN;
  const deg = parseInt(match[2], 10);
  const rawMin = match[3];
  const min = rawMin.length === 3
    ? parseInt(rawMin, 10) / 10   // DDMMM → decimal minutes with tenths
    : parseInt(rawMin, 10);
  const sign = match[4] === 'S' ? -1 : 1;
  return sign * (deg + min / 60);
}

The same anchoring smell exists in parseLon — its outer regex fixes the digit count so it's not exploitable today, but the unanchored inner pattern is fragile if the outer regex is ever loosened.

Notes for the reviewer

  • Verified that MessageDecoder.decode() returns whatever the plugin sets; there is no downstream sanity check on latitude ∈ [-90, 90] to catch this.
  • No existing test in Label_H1_Paren.test.ts covers a 5-digit latitude.

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