rack 1.5.3 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7aa32fd28459ce2c2e164f12fb33c2599493a01
4
- data.tar.gz: 3335d110a0d071412aae13ac81072f5ddbf59806
3
+ metadata.gz: d55818e96ace1f944f520fbe49b80f007f6c32a8
4
+ data.tar.gz: 26648f0efcd058816df3d6eb69c126d33fab4067
5
5
  SHA512:
6
- metadata.gz: efa5a3a838fca1bafcd280c7aa149ccbb0b96bc9b5137eb4b98d22da87f065c283aa2f84241223ec156771e5fc6c450c88ce1a78bd9bece75d33f4947097fc5c
7
- data.tar.gz: 1ccd7cb881db9bb604337d2f00b25d784b263eaba81e9f357d9335413adbe98385621026200716288b96e86c412cacf79d3bf689318905d58687c6fd5b628297
6
+ metadata.gz: 54ec13d366e64034c71a6ea552b6a92d06c67c5737a7d7bcad36b24517ed9bf86e4a44895208ae268de2f973882972b6299bf73e55427a9145751868bab27b97
7
+ data.tar.gz: 4a19a800a7315faa9d809520cbaa94dd8ae5d3c9d7e6e2b082a9de88f24071a43e37974204cd653192964e1370fd0647ef85d17d61592ed2e8ff8235d611d509
@@ -528,6 +528,8 @@ run on port 11211) and memcache-client installed.
528
528
  * Fix a race condition that could result in overwritten pidfiles
529
529
  * Various documentation additions
530
530
 
531
+ * Prevent extremely deep parameters from being parsed. CVE-2015-3225
532
+
531
533
  == Contact
532
534
 
533
535
  Please post bugs, suggestions and patches to
@@ -20,7 +20,7 @@ module Rack
20
20
 
21
21
  # Return the Rack release as a dotted string.
22
22
  def self.release
23
- "1.5.3"
23
+ "1.5.4"
24
24
  end
25
25
 
26
26
  autoload :Builder, "rack/builder"
@@ -2,6 +2,8 @@ require 'rack/utils'
2
2
 
3
3
  module Rack
4
4
  module Multipart
5
+ class MultipartLimitError < Errno::EMFILE; end
6
+
5
7
  class Parser
6
8
  BUFSIZE = 16384
7
9
 
@@ -14,7 +16,13 @@ module Rack
14
16
 
15
17
  fast_forward_to_first_boundary
16
18
 
19
+ opened_files = 0
17
20
  loop do
21
+ if Utils.multipart_part_limit > 0
22
+ raise MultipartLimitError, 'Maximum file multiparts in content reached' if opened_files >= Utils.multipart_part_limit
23
+ opened_files += 1
24
+ end
25
+
18
26
  head, filename, content_type, name, body =
19
27
  get_current_head_and_filename_and_content_type_and_name_and_body
20
28
 
@@ -52,12 +52,23 @@ module Rack
52
52
 
53
53
  class << self
54
54
  attr_accessor :key_space_limit
55
+ attr_accessor :param_depth_limit
56
+ attr_accessor :multipart_part_limit
55
57
  end
56
58
 
57
59
  # The default number of bytes to allow parameter keys to take up.
58
60
  # This helps prevent a rogue client from flooding a Request.
59
61
  self.key_space_limit = 65536
60
62
 
63
+ # Default depth at which the parameter parser will raise an exception for
64
+ # being too deep. This helps prevent SystemStackErrors
65
+ self.param_depth_limit = 100
66
+ #
67
+ # The maximum number of parts a request can contain. Accepting to many part
68
+ # can lead to the server running out of file handles.
69
+ # Set to `0` for no limit.
70
+ self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
71
+
61
72
  # Stolen from Mongrel, with some small modifications:
62
73
  # Parses a query string by breaking it up at the '&'
63
74
  # and ';' characters. You can also use this to parse
@@ -100,7 +111,9 @@ module Rack
100
111
  end
101
112
  module_function :parse_nested_query
102
113
 
103
- def normalize_params(params, name, v = nil)
114
+ def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit)
115
+ raise RangeError if depth <= 0
116
+
104
117
  name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
105
118
  k = $1 || ''
106
119
  after = $' || ''
@@ -118,14 +131,14 @@ module Rack
118
131
  params[k] ||= []
119
132
  raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
120
133
  if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
121
- normalize_params(params[k].last, child_key, v)
134
+ normalize_params(params[k].last, child_key, v, depth - 1)
122
135
  else
123
- params[k] << normalize_params(params.class.new, child_key, v)
136
+ params[k] << normalize_params(params.class.new, child_key, v, depth - 1)
124
137
  end
125
138
  else
126
139
  params[k] ||= params.class.new
127
140
  raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
128
- params[k] = normalize_params(params[k], after, v)
141
+ params[k] = normalize_params(params[k], after, v, depth - 1)
129
142
  end
130
143
 
131
144
  return params
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack"
3
- s.version = "1.5.3"
3
+ s.version = "1.5.4"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.summary = "a modular Ruby webserver interface"
6
6
  s.license = "MIT"
@@ -364,22 +364,29 @@ Content-Type: image/jpeg\r
364
364
  end
365
365
 
366
366
  it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
367
- data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
368
- options = {
369
- "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
370
- "CONTENT_LENGTH" => data.length.to_s,
371
- :input => StringIO.new(data)
372
- }
373
- env = Rack::MockRequest.env_for("/", options)
374
- params = Rack::Multipart.parse_multipart(env)
375
-
376
- params.should.not.equal nil
377
- params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
378
- params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
379
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
380
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
381
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
382
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
367
+ begin
368
+ previous_limit = Rack::Utils.multipart_part_limit
369
+ Rack::Utils.multipart_part_limit = 256
370
+
371
+ data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
372
+ options = {
373
+ "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
374
+ "CONTENT_LENGTH" => data.length.to_s,
375
+ :input => StringIO.new(data)
376
+ }
377
+ env = Rack::MockRequest.env_for("/", options)
378
+ params = Rack::Multipart.parse_multipart(env)
379
+
380
+ params.should.not.equal nil
381
+ params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
382
+ params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
383
+ params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
384
+ params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
385
+ params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
386
+ params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
387
+ ensure
388
+ Rack::Utils.multipart_part_limit = previous_limit
389
+ end
383
390
  end
384
391
 
385
392
  should "return nil if no UploadedFiles were used" do
@@ -2,6 +2,7 @@ require 'stringio'
2
2
  require 'cgi'
3
3
  require 'rack/request'
4
4
  require 'rack/mock'
5
+ require 'securerandom'
5
6
 
6
7
  describe Rack::Request do
7
8
  should "wrap the rack variables" do
@@ -718,6 +719,22 @@ EOF
718
719
  f[:tempfile].size.should.equal 76
719
720
  end
720
721
 
722
+ should "MultipartLimitError when request has too many multipart parts if limit set" do
723
+ begin
724
+ data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
725
+ data += "--AaB03x--\r"
726
+
727
+ options = {
728
+ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
729
+ "CONTENT_LENGTH" => data.length.to_s,
730
+ :input => StringIO.new(data)
731
+ }
732
+
733
+ request = Rack::Request.new Rack::MockRequest.env_for("/", options)
734
+ lambda { request.POST }.should.raise(Rack::Multipart::MultipartLimitError)
735
+ end
736
+ end
737
+
721
738
  should "parse big multipart form data" do
722
739
  input = <<EOF
723
740
  --AaB03x\r
@@ -123,6 +123,18 @@ describe Rack::Utils do
123
123
  Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
124
124
  end
125
125
 
126
+ should "raise an exception if the params are too deep" do
127
+ len = Rack::Utils.param_depth_limit
128
+
129
+ lambda {
130
+ Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar")
131
+ }.should.raise(RangeError)
132
+
133
+ lambda {
134
+ Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
135
+ }.should.not.raise
136
+ end
137
+
126
138
  should "parse nested query strings correctly" do
127
139
  Rack::Utils.parse_nested_query("foo").
128
140
  should.equal "foo" => nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.3
4
+ version: 1.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Neukirchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-06 00:00:00.000000000 Z
11
+ date: 2015-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bacon
@@ -247,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
247
  version: '0'
248
248
  requirements: []
249
249
  rubyforge_project: rack
250
- rubygems_version: 2.4.6
250
+ rubygems_version: 2.4.5
251
251
  signing_key:
252
252
  specification_version: 4
253
253
  summary: a modular Ruby webserver interface
@@ -299,4 +299,3 @@ test_files:
299
299
  - test/spec_urlmap.rb
300
300
  - test/spec_utils.rb
301
301
  - test/spec_webrick.rb
302
- has_rdoc: