From eb8a00ab54e64279d74a75bcbb61b96b86c4a455 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 5 Mar 2026 14:27:37 +0000 Subject: [PATCH] Replace String.to_integer with safe Integer.parse Use Integer.parse/1 instead of String.to_integer/1 on user-supplied and external API input to prevent ArgumentError crashes on non-numeric values. Adds fallback defaults or nil returns at each call site. --- lib/last_fm/artist.ex | 16 ++++++++++---- lib/last_fm/track.ex | 6 +++-- lib/music_brainz/artist.ex | 8 ++++++- lib/music_library_web/components/release.ex | 4 ++-- .../controllers/collection_controller.ex | 22 +++++++++++-------- .../live/scrobble_live/show.ex | 2 +- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/lib/last_fm/artist.ex b/lib/last_fm/artist.ex index cfcd20a9..c525c842 100644 --- a/lib/last_fm/artist.ex +++ b/lib/last_fm/artist.ex @@ -67,10 +67,18 @@ defmodule LastFm.Artist do end defp get_play_count(api_response) do - if play_count = get_in(api_response, ["stats", "userplaycount"]) do - String.to_integer(play_count) - else - 0 + case get_in(api_response, ["stats", "userplaycount"]) do + nil -> 0 + value -> parse_play_count(value) end end + + defp parse_play_count(value) when is_binary(value) do + case Integer.parse(value) do + {int, ""} -> int + _ -> 0 + end + end + + defp parse_play_count(_), do: 0 end diff --git a/lib/last_fm/track.ex b/lib/last_fm/track.ex index ed5876f9..007109f9 100644 --- a/lib/last_fm/track.ex +++ b/lib/last_fm/track.ex @@ -73,8 +73,10 @@ defmodule LastFm.Track do end defp parse_scrobble_at_uts(track) do - track["date"]["uts"] - |> String.to_integer() + case Integer.parse(track["date"]["uts"]) do + {int, ""} -> int + _ -> nil + end end def changeset(track, attrs) do diff --git a/lib/music_brainz/artist.ex b/lib/music_brainz/artist.ex index c88fe574..859f79bb 100644 --- a/lib/music_brainz/artist.ex +++ b/lib/music_brainz/artist.ex @@ -53,7 +53,13 @@ defmodule MusicBrainz.Artist do end) end - defp parse_discogs_id("https://www.discogs.com/artist/" <> id), do: String.to_integer(id) + defp parse_discogs_id("https://www.discogs.com/artist/" <> id) do + case Integer.parse(id) do + {int, ""} -> int + _ -> nil + end + end + defp parse_discogs_id(_other), do: nil defp parse_wikidata_id("https://www.wikidata.org/wiki/" <> id), do: id diff --git a/lib/music_library_web/components/release.ex b/lib/music_library_web/components/release.ex index 01485b66..e61de6a1 100644 --- a/lib/music_library_web/components/release.ex +++ b/lib/music_library_web/components/release.ex @@ -282,7 +282,7 @@ defmodule MusicLibraryWeb.Components.Release do def handle_event("scrobble_medium", %{"number" => number}, socket) when release_loaded?(socket.assigns) do release_with_tracks = socket.assigns.release_with_tracks.result - number = String.to_integer(number) + {number, ""} = Integer.parse(number) case ScrobbleActivity.scrobble_medium(number, release_with_tracks, finished_at: DateTime.utc_now() @@ -324,7 +324,7 @@ defmodule MusicLibraryWeb.Components.Release do end def handle_event("toggle_medium", %{"medium-number" => number}, socket) do - number = String.to_integer(number) + {number, ""} = Integer.parse(number) selected_tracks = socket.assigns.selected_tracks release_with_tracks = socket.assigns.release_with_tracks.result diff --git a/lib/music_library_web/controllers/collection_controller.ex b/lib/music_library_web/controllers/collection_controller.ex index 3a3f2a7f..5d5ce66a 100644 --- a/lib/music_library_web/controllers/collection_controller.ex +++ b/lib/music_library_web/controllers/collection_controller.ex @@ -28,15 +28,8 @@ defmodule MusicLibraryWeb.CollectionController do end def index(conn, params) do - limit = - params - |> Map.get("limit", "20") - |> String.to_integer() - - offset = - params - |> Map.get("offset", "0") - |> String.to_integer() + limit = parse_int(params["limit"], 20) + offset = parse_int(params["offset"], 0) total = Collection.search_records_count("") @@ -44,4 +37,15 @@ defmodule MusicLibraryWeb.CollectionController do render(conn, :index, total: total, limit: limit, offset: offset, records: records) end + + defp parse_int(nil, default), do: default + + defp parse_int(value, default) when is_binary(value) do + case Integer.parse(value) do + {int, ""} -> int + _ -> default + end + end + + defp parse_int(_, default), do: default end diff --git a/lib/music_library_web/live/scrobble_live/show.ex b/lib/music_library_web/live/scrobble_live/show.ex index 0b87be83..f7988f44 100644 --- a/lib/music_library_web/live/scrobble_live/show.ex +++ b/lib/music_library_web/live/scrobble_live/show.ex @@ -162,7 +162,7 @@ defmodule MusicLibraryWeb.ScrobbleLive.Show do end def handle_event("scrobble_medium", %{"medium_number" => number}, socket) do - number = String.to_integer(number) + {number, ""} = Integer.parse(number) case ScrobbleActivity.scrobble_medium(number, socket.assigns.release, finished_at: DateTime.utc_now()