Skip to content

Commit

Permalink
Specify a maximum number of runs to limit the request params -> DoS.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Apr 29, 2024
1 parent 6afd9ad commit adff178
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
21 changes: 20 additions & 1 deletion lib/rack/contrib/profiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def initialize(app, options = {})
@profile = nil
@printer = parse_printer(options[:printer] || DEFAULT_PRINTER)
@times = (options[:times] || 1).to_i
@maximum = options.fetch(:maximum, 10)
end

def call(env)
Expand All @@ -61,12 +62,30 @@ def profiling?(env)
end
end

# How many times to run the request within the profiler.
# If the profiler_runs query parameter is set, use that.
# Otherwise, use the :times option passed to `#initialize`.
# If the profiler_runs query parameter is greater than the
# :maximum option passed to `#initialize`, use the :maximum
# option.
def runs(request)
if profiler_runs = request.params['profiler_runs']
profiler_runs = profiler_runs.to_i
if profiler_runs > @maximum
return @maximum
else
return profiler_runs
end
else
return @times
end
end

def profile(env, mode)
@profile = ::RubyProf::Profile.new(measure_mode: ::RubyProf.const_get(mode.upcase))

GC.enable_stats if GC.respond_to?(:enable_stats)
request = Rack::Request.new(env.clone)
runs = (request.params['profiler_runs'] || @times).to_i
result = @profile.profile do
runs.times { @app.call(env) }
end
Expand Down
7 changes: 7 additions & 0 deletions test/spec_rack_profiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def profiler(app, options = {})
_(body.to_enum.to_a.join).must_match(/\[#{runs} calls, #{runs} total\]/)
end

specify 'called more than the default maximum times via query params' do
runs = 20
req = Rack::MockRequest.env_for("/", :params => "profile=process_time&profiler_runs=#{runs}")
body = profiler(app).call(req)[2]
_(body.to_enum.to_a.join).must_match(/\[10 calls, 10 total\]/)
end

specify 'CallStackPrinter has content-type test/html' do
headers = profiler(app, :printer => :call_stack).call(request)[1]
_(headers).must_equal "content-type"=>"text/html"
Expand Down

0 comments on commit adff178

Please sign in to comment.