prop/bugs.md
Graham McIntire 14b90ee9f3
fix(security,perf): address 9 audit findings (access control, DoS, crashes)
- Beacon detail endpoints (LiveView + REST API) now hide unapproved
  beacons from anonymous and unauthorized viewers; only the submitter
  and admins can see pending records before approval. Adds
  Beacons.get_visible_beacon/2 with scope-aware checks.
- API contact pagination now honors per_page end-to-end.
  Radio.list_contacts/1 accepts :per_page and clamps to 200.
- API rate limiter: ETS table is now owned by a long-lived Sweeper
  GenServer (won't die with a request task); Sweeper periodically
  prunes expired-window rows to bound memory; init_table/0 race is
  rescued.
- /scores/cells and /weather/cells: add per-IP rate limiting and a
  shared GridBounds clamp/413 guard so global / oversized viewports
  no longer drive unbounded binary responses.
- NEXRAD PNG unfilter (sub/up/average/paeth): replace acc++[byte]
  + Enum.at(acc, idx-bpp) with O(n) binary recursion. Decode time
  for the 12200x5400 n0q frame goes from quadratic to linear.
- LiveTableFooter.parse_page and ScoresFile.fetch_bound: switch
  String.to_integer/String.to_float to Integer.parse/Float.parse,
  fall back to defaults instead of raising.
- PathLive and MapLive band-event handlers: replace
  String.to_integer(params["band"]) with parse_int / parse_band_param
  so a non-numeric band parameter no longer crashes the LiveView.
2026-05-11 18:53:21 -05:00

1.3 KiB

Bugs Found 2026-05-11

The first batch (7 items: pending-beacon access control, per_page contract, rate-limiter ETS lifecycle, unbounded grid endpoints, NEXRAD PNG quadratic unfilter, LiveTableFooter parser, ScoresFile parser) was fixed and removed from this file as each was resolved. The items below are additional bugs surfaced by a follow-up audit.

A. String.to_integer on user-controlled band param crashes LiveViews

Status: Fixed. Severity: High Category: Availability / unhandled crash on user input Files:

  • lib/microwaveprop_web/live/path_live.ex (formerly lines 227, 284)
  • lib/microwaveprop_web/live/map_live.ex (formerly line 198)

PathLive.handle_event("calculate", …) and the auto-calculate path both called String.to_integer(params["band"]) directly on the band form parameter. MapLive.handle_event("select_band", %{"value" => band}, …) did the same. A non-numeric band value — supplied via a hand-crafted URL (/path?band=abc) or a misbehaving JS hook — raised ArgumentError inside the LiveView process and crashed the channel.

Fix: Use the existing LiveHelpers.parse_int/2 (PathLive) and the existing parse_band_param/1 plus a small normalize_band_event/1 helper (MapLive). Unknown bands now fall back to a safe default or a no-op instead of crashing the process.