fix: resolve pr remarks
This commit is contained in:
parent
a92f503752
commit
b4657cae23
6 changed files with 139 additions and 83 deletions
|
|
@ -778,9 +778,32 @@ defmodule MvWeb.MemberLive.Index do
|
|||
|> Ash.Query.new()
|
||||
|> Ash.Query.select(@overview_fields)
|
||||
|
||||
# Load custom field values for visible custom fields (based on user selection)
|
||||
# Load custom field values for visible custom fields AND active boolean filters
|
||||
# This ensures boolean filters work even when the custom field is not visible in overview
|
||||
visible_custom_field_ids = socket.assigns[:visible_custom_field_ids] || []
|
||||
query = load_custom_field_values(query, visible_custom_field_ids)
|
||||
|
||||
# Get IDs of active boolean filters (whitelisted against boolean_custom_fields)
|
||||
# Convert boolean_custom_fields list to map for efficient lookup (consistent with maybe_update_boolean_filters)
|
||||
boolean_custom_fields_map =
|
||||
socket.assigns.boolean_custom_fields
|
||||
|> Map.new(fn cf -> {to_string(cf.id), cf} end)
|
||||
|
||||
active_boolean_filter_ids =
|
||||
socket.assigns.boolean_custom_field_filters
|
||||
|> Map.keys()
|
||||
|> Enum.filter(fn id_str ->
|
||||
# Validate UUID format and check against whitelist
|
||||
String.length(id_str) <= @max_uuid_length &&
|
||||
match?({:ok, _}, Ecto.UUID.cast(id_str)) &&
|
||||
Map.has_key?(boolean_custom_fields_map, id_str)
|
||||
end)
|
||||
|
||||
# Union of visible IDs and active filter IDs
|
||||
ids_to_load =
|
||||
(visible_custom_field_ids ++ active_boolean_filter_ids)
|
||||
|> Enum.uniq()
|
||||
|
||||
query = load_custom_field_values(query, ids_to_load)
|
||||
|
||||
# Load membership fee cycles for status display
|
||||
query = MembershipFeeStatus.load_cycles_for_members(query, socket.assigns.show_current_cycle)
|
||||
|
|
@ -1233,35 +1256,43 @@ defmodule MvWeb.MemberLive.Index do
|
|||
|> Map.new(fn cf -> {to_string(cf.id), cf} end)
|
||||
|
||||
# Parse all boolean filter parameters
|
||||
# Security: Use reduce_while to abort early after @max_boolean_filters to prevent DoS attacks
|
||||
# This protects CPU/Parsing costs, not just memory/state
|
||||
# We count processed parameters (not just valid filters) to protect against parsing DoS
|
||||
prefix_length = String.length(@boolean_filter_prefix)
|
||||
|
||||
filters =
|
||||
{filters, total_processed} =
|
||||
params
|
||||
|> Enum.filter(fn {key, _value} -> String.starts_with?(key, @boolean_filter_prefix) end)
|
||||
|> Enum.reduce(%{}, fn {key, value_str}, acc ->
|
||||
process_boolean_filter_param(
|
||||
key,
|
||||
value_str,
|
||||
prefix_length,
|
||||
boolean_custom_fields,
|
||||
acc
|
||||
)
|
||||
|> Enum.reduce_while({%{}, 0}, fn {key, value_str}, {acc, count} ->
|
||||
if count >= @max_boolean_filters do
|
||||
Logger.warning(
|
||||
"Too many boolean filter parameters in request (#{count} processed), limiting to #{@max_boolean_filters} to prevent DoS"
|
||||
)
|
||||
|
||||
{:halt, {acc, count}}
|
||||
else
|
||||
new_acc =
|
||||
process_boolean_filter_param(
|
||||
key,
|
||||
value_str,
|
||||
prefix_length,
|
||||
boolean_custom_fields,
|
||||
acc
|
||||
)
|
||||
|
||||
# Increment counter for each processed parameter (DoS protection)
|
||||
# Note: We count processed params, not just valid filters, to protect parsing costs
|
||||
{:cont, {new_acc, count + 1}}
|
||||
end
|
||||
end)
|
||||
|
||||
# Security: Limit number of filters to prevent DoS attacks
|
||||
# Maximum @max_boolean_filters boolean filters allowed per request
|
||||
filters =
|
||||
if map_size(filters) > @max_boolean_filters do
|
||||
Logger.warning(
|
||||
"Too many boolean filters requested: #{map_size(filters)}, limiting to #{@max_boolean_filters}"
|
||||
)
|
||||
|
||||
filters
|
||||
|> Enum.take(@max_boolean_filters)
|
||||
|> Enum.into(%{})
|
||||
else
|
||||
filters
|
||||
end
|
||||
# Log additional warning if we hit the limit
|
||||
if total_processed >= @max_boolean_filters do
|
||||
Logger.warning(
|
||||
"Boolean filter limit reached: processed #{total_processed} parameters, accepted #{map_size(filters)} valid filters (max: #{@max_boolean_filters})"
|
||||
)
|
||||
end
|
||||
|
||||
assign(socket, :boolean_custom_field_filters, filters)
|
||||
end
|
||||
|
|
@ -1340,8 +1371,8 @@ defmodule MvWeb.MemberLive.Index do
|
|||
# This ensures no raw user input is ever passed to filter functions.
|
||||
#
|
||||
# Returns:
|
||||
# - `:true` for "true" string
|
||||
# - `:false` for "false" string
|
||||
# - `true` for "true" string
|
||||
# - `false` for "false" string
|
||||
# - `nil` for any other value
|
||||
defp determine_boolean_filter("true"), do: true
|
||||
defp determine_boolean_filter("false"), do: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue