Skip to content

Commit 0eec2a9

Browse files
authored
Specify a maximum number of runs to limit the request params -> DoS. (#191)
1 parent ddd96e9 commit 0eec2a9

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

lib/rack/contrib/profiler.rb

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ def initialize(app, options = {})
3535
@profile = nil
3636
@printer = parse_printer(options[:printer] || DEFAULT_PRINTER)
3737
@times = (options[:times] || 1).to_i
38+
@maximum_runs = options.fetch(:maximum_runs, 10)
3839
end
3940

41+
attr :maximum_runs
42+
4043
def call(env)
4144
if mode = profiling?(env)
4245
profile(env, mode)
@@ -61,14 +64,32 @@ def profiling?(env)
6164
end
6265
end
6366

67+
# How many times to run the request within the profiler.
68+
# If the profiler_runs query parameter is set, use that.
69+
# Otherwise, use the :times option passed to `#initialize`.
70+
# If the profiler_runs query parameter is greater than the
71+
# :maximum option passed to `#initialize`, use the :maximum
72+
# option.
73+
def runs(request)
74+
if profiler_runs = request.params['profiler_runs']
75+
profiler_runs = profiler_runs.to_i
76+
if profiler_runs > @maximum_runs
77+
return @maximum_runs
78+
else
79+
return profiler_runs
80+
end
81+
else
82+
return @times
83+
end
84+
end
85+
6486
def profile(env, mode)
6587
@profile = ::RubyProf::Profile.new(measure_mode: ::RubyProf.const_get(mode.upcase))
6688

6789
GC.enable_stats if GC.respond_to?(:enable_stats)
6890
request = Rack::Request.new(env.clone)
69-
runs = (request.params['profiler_runs'] || @times).to_i
7091
result = @profile.profile do
71-
runs.times { @app.call(env) }
92+
runs(request).times { @app.call(env) }
7293
end
7394
GC.disable_stats if GC.respond_to?(:disable_stats)
7495

test/spec_rack_profiler.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ def profiler(app, options = {})
2727
_(body.to_enum.to_a.join).must_match(/\[#{runs} calls, #{runs} total\]/)
2828
end
2929

30+
specify 'called more than the default maximum times via query params' do
31+
runs = 20
32+
req = Rack::MockRequest.env_for("/", :params => "profile=process_time&profiler_runs=#{runs}")
33+
body = profiler(app).call(req)[2]
34+
_(body.to_enum.to_a.join).must_match(/\[10 calls, 10 total\]/)
35+
end
36+
3037
specify 'CallStackPrinter has content-type test/html' do
3138
headers = profiler(app, :printer => :call_stack).call(request)[1]
3239
_(headers).must_equal "content-type"=>"text/html"

0 commit comments

Comments
 (0)