refactor: fix credo issues

This commit is contained in:
Simon 2026-01-20 18:34:17 +01:00
parent 37c9da54d7
commit 7f4c22d072
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
2 changed files with 74 additions and 28 deletions

View file

@ -1194,32 +1194,13 @@ defmodule MvWeb.MemberLive.Index do
params
|> Enum.filter(fn {key, _value} -> String.starts_with?(key, @boolean_filter_prefix) end)
|> Enum.reduce(%{}, fn {key, value_str}, acc ->
# Extract custom field ID from parameter name (explicitly remove prefix)
# This is more secure than String.replace_prefix which only removes first occurrence
custom_field_id_str = String.slice(key, prefix_length, String.length(key) - prefix_length)
# Validate custom field ID length (UUIDs are max @max_uuid_length characters)
# This provides an additional security layer beyond UUID format validation
if String.length(custom_field_id_str) <= @max_uuid_length do
# Validate custom field ID exists and is boolean type
case Ecto.UUID.cast(custom_field_id_str) do
{:ok, _custom_field_id} ->
if Map.has_key?(boolean_custom_fields, custom_field_id_str) do
# Validate filter value
case determine_boolean_filter(value_str) do
nil -> acc
filter_value -> Map.put(acc, custom_field_id_str, filter_value)
end
else
acc
end
:error ->
acc
end
else
process_boolean_filter_param(
key,
value_str,
prefix_length,
boolean_custom_fields,
acc
end
)
end)
# Security: Limit number of filters to prevent DoS attacks
@ -1240,6 +1221,73 @@ defmodule MvWeb.MemberLive.Index do
assign(socket, :boolean_custom_field_filters, filters)
end
# Processes a single boolean filter parameter from URL params.
#
# Validates the parameter and adds it to the accumulator if valid.
# Returns the accumulator unchanged if validation fails.
defp process_boolean_filter_param(
key,
value_str,
prefix_length,
boolean_custom_fields,
acc
) do
# Extract custom field ID from parameter name (explicitly remove prefix)
# This is more secure than String.replace_prefix which only removes first occurrence
custom_field_id_str = String.slice(key, prefix_length, String.length(key) - prefix_length)
# Validate custom field ID length (UUIDs are max @max_uuid_length characters)
# This provides an additional security layer beyond UUID format validation
if String.length(custom_field_id_str) > @max_uuid_length do
acc
else
validate_and_add_boolean_filter(
custom_field_id_str,
value_str,
boolean_custom_fields,
acc
)
end
end
# Validates UUID format and custom field existence, then adds filter if valid.
defp validate_and_add_boolean_filter(
custom_field_id_str,
value_str,
boolean_custom_fields,
acc
) do
case Ecto.UUID.cast(custom_field_id_str) do
{:ok, _custom_field_id} ->
add_boolean_filter_if_valid(
custom_field_id_str,
value_str,
boolean_custom_fields,
acc
)
:error ->
acc
end
end
# Adds boolean filter to accumulator if custom field exists and value is valid.
defp add_boolean_filter_if_valid(
custom_field_id_str,
value_str,
boolean_custom_fields,
acc
) do
if Map.has_key?(boolean_custom_fields, custom_field_id_str) do
case determine_boolean_filter(value_str) do
nil -> acc
filter_value -> Map.put(acc, custom_field_id_str, filter_value)
end
else
acc
end
end
# Determines valid boolean filter value from URL parameter.
#
# SECURITY: This function whitelists allowed filter values. Only "true" and "false"

View file

@ -945,9 +945,7 @@ defmodule MvWeb.MemberLive.IndexTest do
# Build URL with all 60 filters
filter_params =
boolean_fields
|> Enum.map(fn cf -> "bf_#{cf.id}=true" end)
|> Enum.join("&")
Enum.map_join(boolean_fields, "&", fn cf -> "bf_#{cf.id}=true" end)
{:ok, view, _html} = live(conn, "/members?#{filter_params}")