41 lines
1.9 KiB
Diff
41 lines
1.9 KiB
Diff
# UNDF: UNDF-2026-000000091
|
||
Fixes grape-0001: Grape::Validations::Validators::ValuesValidator#check_values? — values Array#include? inside param_array.all? O(P×V).
|
||
|
||
--- a/lib/grape/validations/validators/values_validator.rb
|
||
+++ b/lib/grape/validations/validators/values_validator.rb
|
||
|
||
@@ DEFECT grape-0001: lines 36-39 check_values?
|
||
|
||
def check_values?(val, attr_name)
|
||
values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
|
||
return true if values.nil?
|
||
|
||
param_array = val.nil? ? [nil] : Array.wrap(val)
|
||
- return param_array.all? { |param| values.include?(param) } unless values.is_a?(Proc)
|
||
+ unless values.is_a?(Proc) # FIX: build Set once for O(1) lookup
|
||
+ values_set = values.is_a?(Set) ? values : values.to_set
|
||
+ return param_array.all? { |param| values_set.include?(param) }
|
||
+ end
|
||
|
||
begin
|
||
param_array.all? { |param| values.call(param) }
|
||
rescue StandardError => e
|
||
warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
|
||
false
|
||
end
|
||
end
|
||
|
||
# BEFORE: values is a plain Array (the allowlist defined in params do ... values: [...] end)
|
||
# param_array.all? { |param| values.include?(param) }
|
||
# For a multi-value param (e.g., tags[]=a&tags[]=b&...): P params × V values = O(P×V)
|
||
# Every validated request incurs this cost.
|
||
#
|
||
# AFTER: values_set = values.to_set — O(V) once (or reuse if already a Set)
|
||
# values_set.include? is O(1) per param
|
||
# Total: O(P + V) per validation call
|
||
#
|
||
# Severity: HIGH — executed on every API request that includes a multi-value param
|
||
# with a large allowlist (e.g., enums with 100+ values, multi-select filters)
|
||
# At P=50 submitted params, V=200 allowed values: 10,000 → 250 ops per request — 40x
|
||
#
|
||
# Speedup: ~V× where V = allowlist size
|