Sanitize and validate user-submitted contact fields

- Callsigns: strip whitespace, upcase, validate alphanumeric + /
- Grids: strip whitespace, upcase before Maidenhead validation
- Email: stricter regex, max 254 chars
- Callsign max length 20 chars
- Fix static_paths to include downloads directory
- Update show test for removed contact details section
This commit is contained in:
Graham McIntire 2026-04-01 15:41:42 -05:00
parent ec388e4bf5
commit a25864bdcb
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
3 changed files with 40 additions and 9 deletions

View file

@ -48,14 +48,46 @@ defmodule Microwaveprop.Radio.Contact do
contact
|> cast(attrs, @submission_fields)
|> validate_required(@submission_fields)
|> validate_grid(:grid1)
|> validate_grid(:grid2)
|> sanitize_callsign(:station1)
|> sanitize_callsign(:station2)
|> sanitize_grid(:grid1)
|> sanitize_grid(:grid2)
|> validate_callsign(:station1)
|> validate_callsign(:station2)
|> validate_grid_format(:grid1)
|> validate_grid_format(:grid2)
|> validate_inclusion(:mode, @allowed_modes)
|> validate_inclusion(:band, @allowed_bands)
|> validate_format(:submitter_email, ~r/@/)
|> validate_format(:submitter_email, ~r/^[^\s<>]+@[^\s<>]+\.[^\s<>]+$/)
|> validate_length(:submitter_email, max: 254)
|> validate_length(:station1, max: 20)
|> validate_length(:station2, max: 20)
end
defp validate_grid(changeset, field) do
# Strip whitespace and upcase callsigns
defp sanitize_callsign(changeset, field) do
case get_change(changeset, field) do
nil -> changeset
val -> put_change(changeset, field, val |> String.trim() |> String.upcase())
end
end
# Upcase grid squares (Maidenhead uses uppercase letters + digits)
defp sanitize_grid(changeset, field) do
case get_change(changeset, field) do
nil -> changeset
val -> put_change(changeset, field, val |> String.trim() |> String.upcase())
end
end
# Callsigns: letters, digits, and / only (e.g. W5XD, KG5CCI/P, VE3/W5XD)
defp validate_callsign(changeset, field) do
validate_format(changeset, field, ~r/^[A-Z0-9\/]+$/,
message: "must contain only letters, digits, and /"
)
end
defp validate_grid_format(changeset, field) do
validate_change(changeset, field, fn _, value ->
if Maidenhead.valid?(value) do
[]

View file

@ -17,7 +17,7 @@ defmodule MicrowavepropWeb do
those modules here.
"""
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)
def static_paths, do: ~w(assets fonts images downloads favicon.ico robots.txt)
def router do
quote do

View file

@ -92,10 +92,9 @@ defmodule MicrowavepropWeb.ContactLiveTest do
{:ok, _lv, html} = live(conn, ~p"/contacts/#{contact.id}")
assert html =~ "W5XD"
assert html =~ "K5TR"
assert html =~ "CW"
assert html =~ "1296"
assert html =~ "EM12"
assert html =~ "EM00"
# Mode, band, grids are shown in the elevation profile section (requires SRTM)
# or in the header subtitle
assert html =~ "2026-03-28"
end
test "shows surface observations section", %{conn: conn} do