34 lines
1.8 KiB
Diff
34 lines
1.8 KiB
Diff
# UNDF: UNDF-2026-000000092
|
||
Fixes grape-0002: Grape::Validations::Validators::ExceptValuesValidator#validate_param! — excepts Array#include? inside param_array.any? O(P×E).
|
||
|
||
--- a/lib/grape/validations/validators/except_values_validator.rb
|
||
+++ b/lib/grape/validations/validators/except_values_validator.rb
|
||
|
||
@@ DEFECT grape-0002: line 19 validate_param!
|
||
|
||
def validate_param!(attr_name, params)
|
||
return unless params.respond_to?(:key?) && params.key?(attr_name)
|
||
|
||
excepts = @except.is_a?(Proc) ? @except.call : @except
|
||
return if excepts.nil?
|
||
|
||
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
||
- raise Grape::Exceptions::Validation.new(...) if param_array.any? { |param| excepts.include?(param) }
|
||
+ excepts_set = excepts.is_a?(Set) ? excepts : excepts.to_set # FIX: O(1) lookup
|
||
+ raise Grape::Exceptions::Validation.new(...) if param_array.any? { |param| excepts_set.include?(param) }
|
||
end
|
||
|
||
# BEFORE: excepts is a plain Array (the blocklist defined in params do ... except_values: [...] end)
|
||
# param_array.any? { |param| excepts.include?(param) }
|
||
# For a multi-value param: P submitted values × E excluded values = O(P×E) per request
|
||
#
|
||
# AFTER: excepts_set = excepts.to_set — O(E) once
|
||
# excepts_set.include? is O(1) per submitted param
|
||
# Total: O(P + E) per validation call
|
||
#
|
||
# Severity: MEDIUM — executed on every API request containing a multi-value param
|
||
# with except_values configured as a long blocklist
|
||
# At P=30 submitted, E=150 excluded: 4,500 → 180 ops per request — 25x improvement
|
||
#
|
||
# Note: grape-0001 (ValuesValidator) and grape-0002 (ExceptValuesValidator) are companion defects —
|
||
# same pattern, opposite semantic (allowlist vs blocklist)
|