rack 1.3.2 → 1.3.3

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.

@@ -355,6 +355,12 @@ run on port 11211) and memcache-client installed.
355
355
  * July 16, 2011: Sixteenth public release 1.3.2
356
356
  * Fix for Rails and rack-test, Rack::Utils#escape calls to_s
357
357
 
358
+ * September 16, 2011: Seventeenth public release 1.3.3
359
+ * Fix bug with broken query parameters in Rack::ShowExceptions
360
+ * Rack::Request#cookies no longer swallows exceptions on broken input
361
+ * Prevents XSS attacks enabled by bug in Ruby 1.8's regexp engine
362
+ * Rack::ConditionalGet handles broken If-Modified-Since helpers
363
+
358
364
  == Contact
359
365
 
360
366
  Please post bugs, suggestions and patches to
data/Rakefile CHANGED
@@ -64,13 +64,13 @@ file "SPEC" => 'lib/rack/lint.rb' do
64
64
  }
65
65
  end
66
66
 
67
- desc "Run all the fast tests"
67
+ desc "Run all the fast + platform agnostic tests"
68
68
  task :test => 'SPEC' do
69
69
  opts = ENV['TEST'] || '-a'
70
70
  specopts = ENV['TESTOPTS'] ||
71
- "-q -t '^(?!Rack::Adapter|Rack::Session::Memcache|Rack::Server)'"
71
+ "-q -t '^(?!Rack::Adapter|Rack::Session::Memcache|Rack::Server|Rack::Handler)'"
72
72
 
73
- sh "bacon -I./lib:./test -w #{opts} #{specopts}"
73
+ sh "bacon -I./lib:./test #{opts} #{specopts}"
74
74
  end
75
75
 
76
76
  desc "Run all the tests"
@@ -56,6 +56,7 @@ module Rack
56
56
 
57
57
  def modified_since?(modified_since, headers)
58
58
  last_modified = to_rfc2822(headers['Last-Modified']) and
59
+ modified_since and
59
60
  modified_since >= last_modified
60
61
  end
61
62
 
@@ -230,22 +230,22 @@ module Rack
230
230
  end
231
231
 
232
232
  def cookies
233
- return {} unless @env["HTTP_COOKIE"]
234
-
235
- if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
236
- @env["rack.request.cookie_hash"]
237
- else
238
- @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
239
- # According to RFC 2109:
240
- # If multiple cookies satisfy the criteria above, they are ordered in
241
- # the Cookie header such that those with more specific Path attributes
242
- # precede those with less specific. Ordering with respect to other
243
- # attributes (e.g., Domain) is unspecified.
244
- @env["rack.request.cookie_hash"] =
245
- Hash[*Utils.parse_query(@env["rack.request.cookie_string"], ';,').map {|k,v|
246
- [k, Array === v ? v.first : v]
247
- }.flatten]
248
- end
233
+ hash = @env["rack.request.cookie_hash"] ||= {}
234
+ string = @env["HTTP_COOKIE"]
235
+
236
+ hash.clear unless string
237
+ return hash if string == @env["rack.request.cookie_string"]
238
+
239
+ # According to RFC 2109:
240
+ # If multiple cookies satisfy the criteria above, they are ordered in
241
+ # the Cookie header such that those with more specific Path attributes
242
+ # precede those with less specific. Ordering with respect to other
243
+ # attributes (e.g., Domain) is unspecified.
244
+ Utils.parse_query(string, ';,').each { |k,v| hash[k] = Array === v ? v.first : v }
245
+ @env["rack.request.cookie_string"] = string
246
+ hash
247
+ rescue => error
248
+ raise error.class, "cannot parse Cookie header: #{error.message}"
249
249
  end
250
250
 
251
251
  def xhr?
@@ -80,7 +80,7 @@ module Rack
80
80
  #
81
81
  # X-Sendfile is supported under Apache 2.x using a separate module:
82
82
  #
83
- # http://tn123.ath.cx/mod_xsendfile/
83
+ # https://tn123.org/mod_xsendfile/
84
84
  #
85
85
  # Once the module is compiled and installed, you can enable it using
86
86
  # XSendFile config directive:
@@ -277,7 +277,7 @@ TEMPLATE = <<'HTML'
277
277
  <h2>Request information</h2>
278
278
 
279
279
  <h3 id="get-info">GET</h3>
280
- <% unless req.GET.empty? %>
280
+ <% if req.GET and not req.GET.empty? %>
281
281
  <table class="req">
282
282
  <thead>
283
283
  <tr>
@@ -299,7 +299,7 @@ TEMPLATE = <<'HTML'
299
299
  <% end %>
300
300
 
301
301
  <h3 id="post-info">POST</h3>
302
- <% unless req.POST.empty? %>
302
+ <% if req.POST and not req.POST.empty? %>
303
303
  <table class="req">
304
304
  <thead>
305
305
  <tr>
@@ -146,7 +146,13 @@ module Rack
146
146
  '"' => "&quot;",
147
147
  "/" => "&#x2F;"
148
148
  }
149
- ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
149
+ if //.respond_to?(:encoding)
150
+ ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
151
+ else
152
+ # On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise
153
+ # TODO doesn't apply to jruby, so a better condition above might be preferable?
154
+ ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
155
+ end
150
156
 
151
157
  # Escape ampersands, brackets and quotes to their HTML/XML entities.
152
158
  def escape_html(string)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack"
3
- s.version = "1.3.2"
3
+ s.version = "1.3.3"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.summary = "a modular Ruby webserver interface"
6
6
 
@@ -83,4 +83,16 @@ describe Rack::ConditionalGet do
83
83
  response.body.should.equal 'TEST'
84
84
  end
85
85
 
86
+ should "not affect requests with malformed HTTP_IF_NONE_MATCH" do
87
+ bad_timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
88
+ app = Rack::ConditionalGet.new(lambda { |env|
89
+ [200,{'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] })
90
+
91
+ response = Rack::MockRequest.new(app).
92
+ get("/", 'HTTP_IF_MODIFIED_SINCE' => bad_timestamp)
93
+
94
+ response.status.should.equal 200
95
+ response.body.should.equal 'TEST'
96
+ end
97
+
86
98
  end
@@ -175,7 +175,8 @@ describe Rack::MockRequest do
175
175
  env["QUERY_STRING"].should.equal ""
176
176
  env["PATH_INFO"].should.equal "/foo"
177
177
  env["CONTENT_TYPE"].should.equal "multipart/form-data; boundary=AaB03x"
178
- env["mock.postdata"].length.should.equal 206
178
+ # The gsub accounts for differences in YAMLs affect on the data.
179
+ env["mock.postdata"].gsub("\r", "").length.should.equal 206
179
180
  end
180
181
 
181
182
  should "behave valid according to the Rack spec" do
@@ -351,6 +351,19 @@ describe Rack::Request do
351
351
  req.cookies.should.equal({})
352
352
  end
353
353
 
354
+ should "always return the same hash object" do
355
+ req = Rack::Request.new \
356
+ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
357
+ hash = req.cookies
358
+ req.env.delete("HTTP_COOKIE")
359
+ req.cookies.should.equal(hash)
360
+ end
361
+
362
+ should "raise any errors on every request" do
363
+ req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
364
+ 2.times { proc { req.cookies }.should.raise(ArgumentError) }
365
+ end
366
+
354
367
  should "parse cookies according to RFC 2109" do
355
368
  req = Rack::Request.new \
356
369
  Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
@@ -3,6 +3,14 @@ require 'rack/utils'
3
3
  require 'rack/mock'
4
4
 
5
5
  describe Rack::Utils do
6
+ def kcodeu
7
+ one8 = RUBY_VERSION.to_f < 1.9
8
+ default_kcode, $KCODE = $KCODE, 'U' if one8
9
+ yield
10
+ ensure
11
+ $KCODE = default_kcode if one8
12
+ end
13
+
6
14
  should "escape correctly" do
7
15
  Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
8
16
  Rack::Utils.escape("a space").should.equal "a+space"
@@ -21,32 +29,28 @@ describe Rack::Utils do
21
29
 
22
30
  if RUBY_VERSION[/^\d+\.\d+/] == '1.8'
23
31
  should "escape correctly for multibyte characters if $KCODE is set to 'U'" do
24
- default_kcode, $KCODE = $KCODE, 'U'
25
-
26
- matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
27
- matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
28
- Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
29
- matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
30
- matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
31
- Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
32
-
33
- $KCODE = default_kcode
32
+ kcodeu do
33
+ matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
34
+ matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
35
+ Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
36
+ matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
37
+ matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
38
+ Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
39
+ end
34
40
  end
35
41
 
36
42
  should "unescape multibyte characters correctly if $KCODE is set to 'U'" do
37
- default_kcode, $KCODE = $KCODE, 'U'
38
- Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
43
+ kcodeu do
44
+ Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
39
45
  "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
40
- $KCODE = default_kcode
46
+ end
41
47
  end
42
48
  end
43
49
 
44
50
  should "escape objects that responds to to_s" do
45
- default_kcode, $KCODE = $KCODE, 'U'
46
-
47
- Rack::Utils.escape(:id).should.equal "id"
48
-
49
- $KCODE = default_kcode
51
+ kcodeu do
52
+ Rack::Utils.escape(:id).should.equal "id"
53
+ end
50
54
  end
51
55
 
52
56
  if "".respond_to?(:encode)
@@ -219,7 +223,7 @@ describe Rack::Utils do
219
223
  message.should.equal "value must be a Hash"
220
224
  end
221
225
 
222
- should "should escape html entities [&><'\"/]" do
226
+ should "escape html entities [&><'\"/]" do
223
227
  Rack::Utils.escape_html("foo").should.equal "foo"
224
228
  Rack::Utils.escape_html("f&o").should.equal "f&amp;o"
225
229
  Rack::Utils.escape_html("f<o").should.equal "f&lt;o"
@@ -230,6 +234,27 @@ describe Rack::Utils do
230
234
  Rack::Utils.escape_html("<foo></foo>").should.equal "&lt;foo&gt;&lt;&#x2F;foo&gt;"
231
235
  end
232
236
 
237
+ should "escape html entities even on MRI when it's bugged" do
238
+ test_escape = lambda do
239
+ kcodeu do
240
+ Rack::Utils.escape_html("\300<").should.equal "\300&lt;"
241
+ end
242
+ end
243
+
244
+ if RUBY_VERSION.to_f < 1.9
245
+ test_escape.call
246
+ else
247
+ test_escape.should.raise(ArgumentError)
248
+ end
249
+ end
250
+
251
+ if "".respond_to?(:encode)
252
+ should "escape html entities in unicode strings" do
253
+ # the following will cause warnings if the regex is poorly encoded:
254
+ Rack::Utils.escape_html("☃").should.equal "☃"
255
+ end
256
+ end
257
+
233
258
  should "figure out which encodings are acceptable" do
234
259
  helper = lambda do |a, b|
235
260
  Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 2
10
- version: 1.3.2
9
+ - 3
10
+ version: 1.3.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Christian Neukirchen
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-26 00:00:00 Z
18
+ date: 2011-09-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bacon
@@ -301,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
301
  requirements: []
302
302
 
303
303
  rubyforge_project: rack
304
- rubygems_version: 1.8.5
304
+ rubygems_version: 1.8.10
305
305
  signing_key:
306
306
  specification_version: 3
307
307
  summary: a modular Ruby webserver interface