Skip to content

Rails filter_parameters silently ignored when precompile_filter_parameters is enabled (Rails 7.1+ default) #1207

Description

@cswilliams

Summary

Rollbar::RequestDataExtractor picks up the app's Rails parameter filters from env['action_dispatch.parameter_filter'] and merges them into scrubbing. Since Rails 7.1, config.precompile_filter_parameters (enabled by load_defaults "7.1" and later) makes Rails precompile that env value into a single combined Regexp instead of an array of names. Rollbar::Scrubbers::Params#build_fields_regex runs every entry through Regexp.escape(val.to_s), which turns the compiled regexp into a literal string that matches nothing.

Nothing raises — params, session, and cookie scrubbing silently degrades to the gem's small built-in scrub_fields default list. Any Rails app on load_defaults "7.1"+ that relies on filter_parameters being honored by Rollbar is sending those values unscrubbed in occurrence payloads.

Environment

  • rollbar-gem 3.8.0
  • Rails 7.1+ with config.load_defaults "7.1" or later (verified on Rails 8.1.3)
  • Not specific to Ruby version

Root cause

Rails builds the env value here — with precompilation on, this is [one_combined_regexp], not an array of names (railties lib/rails/application.rb):

# Rails::Application#env_config
"action_dispatch.parameter_filter" => filter_parameters,

# Rails::Application#filter_parameters (private)
if config.precompile_filter_parameters
  ActiveSupport::ParameterFilter.precompile_filters(config.filter_parameters)
...

The extractor forwards it as extra scrub fields (lib/rollbar/request_data_extractor.rb:271):

def sensitive_params_list(env)
  Array(env['action_dispatch.parameter_filter'])
end

And the Params scrubber destroys it (lib/rollbar/scrubbers/params.rb, build_fields_regex):

fields += Array(extra_fields)
...
Regexp.new(fields.map { |val| Regexp.escape(val.to_s).to_s }.join('|'), true)

Regexp.escape on a Regexp#to_s — e.g. "(?i-mx:(?i:passw)|(?i:token))" — produces an escaped literal that can never match a real parameter name.

Notably, Rollbar::Scrubbers::URL handles the same precompiled Regexp correctly (query string values in request.url do get scrubbed), which makes the params/session/cookies gap easy to miss: the URL looks scrubbed while GET/session leak the same values.

Reproduction

require 'rollbar'
require 'rollbar/request_data_extractor'
require 'rack'
require 'active_support/parameter_filter'

extractor = Object.new.extend(Rollbar::RequestDataExtractor)

def request_env(filters)
  env = Rack::MockRequest.env_for('https://example.com/login?token=SECRET123&user_email=foo@bar.com')
  env['rack.session'] = { '_csrf_token' => 'CSRFVALUE' }
  env['action_dispatch.parameter_filter'] = filters
  env
end

raw = [:passw, :email, :secret, :token]
precompiled = ActiveSupport::ParameterFilter.precompile_filters(raw)
# => [/(?i:passw)|(?i:email)|(?i:secret)|(?i:token)/]

data = extractor.extract_request_data_from_rack(request_env(raw))
data[:GET]     # => {"token" => "******", "user_email" => "******"}   (expected)
data[:session] # => {"_csrf_token" => "******"}                        (expected)

data = extractor.extract_request_data_from_rack(request_env(precompiled))
data[:GET]     # => {"token" => "SECRET123", "user_email" => "foo@bar.com"}  LEAKED
data[:session] # => {"_csrf_token" => "CSRFVALUE"}                           LEAKED
data[:url]     # => "https://example.com/login?token=*********&user_email=***********"
               #    (URL scrubber handles the Regexp fine — only Scrubbers::Params is broken)

In a real Rails 7.1+ app the same thing is observable by raising from any controller action with a sensitive query param and inspecting the occurrence payload: the param appears unscrubbed in request.GET/request.params even though it is filtered in the Rails logs.

Suggested fix

In Scrubbers::Params#build_fields_regex, pass Regexp entries through instead of escaping their to_s:

fields.map { |val| val.is_a?(Regexp) ? val.source : Regexp.escape(val.to_s) }.join('|')

(or build a union with Regexp.union so per-entry flags survive). Alternatively, sensitive_params_list could delegate to ActiveSupport::ParameterFilter when available.

Related

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions