Skip to content

Commit dd10c0d

Browse files
authored
Backport GHSA-98m9-hrrm-r99r fix to 1.x: add param_depth_limit to NestedParamsEncoder (#1681)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 6230807 commit dd10c0d

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

lib/faraday/encoders/nested_params_encoder.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ def decode(query)
100100

101101
def decode_pair(key, value, context)
102102
subkeys = key.scan(SUBKEYS_REGEX)
103+
validate_params_depth!(subkeys.length)
104+
103105
subkeys.each_with_index do |subkey, i|
104106
is_array = subkey =~ /[\[\]]+\Z/
105107
subkey = $` if is_array
@@ -139,6 +141,12 @@ def add_to_context(is_array, context, value, subkey)
139141
is_array ? context << value : context[subkey] = value
140142
end
141143

144+
def validate_params_depth!(depth)
145+
return unless @param_depth_limit && depth > @param_depth_limit
146+
147+
raise Faraday::Error, "exceeded nested parameter depth limit of #{@param_depth_limit}"
148+
end
149+
142150
# Internal: convert a nested hash with purely numeric keys into an array.
143151
# FIXME: this is not compatible with Rack::Utils.parse_nested_query
144152
# @!visibility private
@@ -161,14 +169,15 @@ def dehash(hash, depth)
161169
# for your requests.
162170
module NestedParamsEncoder
163171
class << self
164-
attr_accessor :sort_params
172+
attr_accessor :sort_params, :param_depth_limit
165173

166174
extend Forwardable
167175
def_delegators :'Faraday::Utils', :escape, :unescape
168176
end
169177

170178
# Useful default for OAuth and caching.
171179
@sort_params = true
180+
@param_depth_limit = 100
172181

173182
extend EncodeMethods
174183
extend DecodeMethods

spec/faraday/connection_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@
355355
url = conn.build_url(nil, b: 2, c: 3)
356356
expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3')
357357
end
358+
359+
it 'raises a controlled error when URL query params exceed the nested depth limit' do
360+
original_param_depth_limit = Faraday::NestedParamsEncoder.param_depth_limit
361+
begin
362+
Faraday::NestedParamsEncoder.param_depth_limit = 2
363+
expect { conn.build_url('/nigiri?a[b][c]=1') }.to raise_error(
364+
Faraday::Error,
365+
'exceeded nested parameter depth limit of 2'
366+
)
367+
ensure
368+
Faraday::NestedParamsEncoder.param_depth_limit = original_param_depth_limit
369+
end
370+
end
358371
end
359372

360373
describe '#build_request' do

spec/faraday/params_encoders/nested_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
RSpec.describe Faraday::NestedParamsEncoder do
66
it_behaves_like 'a params encoder'
77

8+
around do |example|
9+
original_param_depth_limit = described_class.param_depth_limit
10+
begin
11+
example.run
12+
ensure
13+
described_class.param_depth_limit = original_param_depth_limit
14+
end
15+
end
16+
817
it 'decodes arrays' do
918
query = 'a[1]=one&a[2]=two&a[3]=three'
1019
expected = { 'a' => %w[one two three] }
@@ -59,6 +68,27 @@
5968
expect(subject.decode(query)).to eq(expected)
6069
end
6170

71+
it 'allows nested params within the configured depth limit' do
72+
described_class.param_depth_limit = 3
73+
74+
expect(subject.decode('a[b][c]=1')).to eq({ 'a' => { 'b' => { 'c' => '1' } } })
75+
end
76+
77+
it 'raises a controlled error when nested params exceed the depth limit' do
78+
described_class.param_depth_limit = 2
79+
80+
expect { subject.decode('a[b][c]=1') }.to raise_error(
81+
Faraday::Error,
82+
'exceeded nested parameter depth limit of 2'
83+
)
84+
end
85+
86+
it 'allows disabling the nested params depth limit' do
87+
described_class.param_depth_limit = nil
88+
89+
expect(subject.decode('a[b][c][d]=1')).to eq({ 'a' => { 'b' => { 'c' => { 'd' => '1' } } } })
90+
end
91+
6292
it 'encodes rack compat' do
6393
params = { a: [{ one: '1', two: '2' }, '3', ''] }
6494
result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&')

0 commit comments

Comments
 (0)