# rails-0009: FilterAttributeHandler — O(A×F) filter_parameters Array#include? per sensitive attribute **Severity:** MEDIUM **File:** activerecord/lib/active_record/filter_attribute_handler.rb **Line:** 69 **Status:** PATCHED ## Description `FilterAttributeHandler#apply_filter` is called once per sensitive attribute per model class during app initialization. Inside the loop it calls: ```ruby app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter) ``` `app.config.filter_parameters` is an Array (initialized as `[]` in railties). Each `.include?(filter)` call is O(F) where F is the current size of `filter_parameters`. With A total sensitive attributes across all models and F growing as attributes are added, total cost is O(A×F) — quadratic in the number of sensitive attributes declared across the application. This is the same structural pattern as rails-0010 (encryption variant) and runs at every app boot for all models using `has_secure_password`, `attr_encrypted`, or manual `filter_attributes` declarations. ## Root Cause `filter_parameters` is a plain Array. The deduplication guard `include?` is O(F) per insertion rather than using a Set or Hash for O(1) membership. ## Fix Pre-build a `Set` mirror of `filter_parameters` for O(1) membership checks, or change the underlying storage to a `Set`: ```ruby # BEFORE (filter_attribute_handler.rb:69) app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter) # AFTER existing = app.config.filter_parameters.to_set list.each do |attribute| next if klass.abstract_class? || klass == Base klass_name = klass.name ? klass.model_name.element : nil filter = [klass_name, attribute.to_s].compact.join(".") unless existing.include?(filter) app.config.filter_parameters << filter existing << filter end end ``` ## Speedup ~10x at A=500 total attributes, F=200 existing filter_parameters