rack 2.0.0.alpha → 2.0.0.rc1
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 +4 -4
- data/COPYING +1 -1
- data/HISTORY.md +13 -4
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +1 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/common_logger.rb +1 -1
- data/lib/rack/directory.rb +20 -2
- data/lib/rack/etag.rb +2 -2
- data/lib/rack/file.rb +10 -1
- data/lib/rack/handler.rb +1 -1
- data/lib/rack/mock.rb +1 -1
- data/lib/rack/multipart/generator.rb +1 -1
- data/lib/rack/multipart/parser.rb +2 -3
- data/lib/rack/query_parser.rb +15 -8
- data/lib/rack/reloader.rb +2 -1
- data/lib/rack/request.rb +28 -18
- data/lib/rack/session/abstract/id.rb +2 -0
- data/lib/rack/static.rb +1 -1
- data/lib/rack/urlmap.rb +4 -2
- data/lib/rack/utils.rb +20 -2
- data/test/cgi/lighttpd.errors +8 -0
- data/test/helper.rb +4 -1
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/spec_directory.rb +15 -0
- data/test/spec_etag.rb +2 -2
- data/test/spec_file.rb +11 -0
- data/test/spec_multipart.rb +8 -0
- data/test/spec_request.rb +5 -0
- data/test/spec_response.rb +55 -5
- data/test/spec_session_abstract_session_hash.rb +28 -0
- data/test/spec_utils.rb +13 -2
- data/test/spec_webrick.rb +14 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54be55d492853c85486a9d94bfa3122be91a375c
|
4
|
+
data.tar.gz: b1f919ccb928bf607b66efb2dc494d350c4e67f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 969ed8dd45442d71e9448dcc7951d997429cbe3a8857694c76cd0c8137ea22ba831f54dc1c24d65f2f2b35ce44c4a1d43bba182b98d93d86fc5917a671cc1486
|
7
|
+
data.tar.gz: cf0ede4ad49a3e2bb23f0136532c32d6fb799b5cb050bffa909727478bf1cca6c6d7704f574cb9a9c9e657e7b5f6430e5e1e8485ac1c747d5d6a14a8d4b81a2d
|
data/COPYING
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2007-
|
1
|
+
Copyright (c) 2007-2016 Christian Neukirchen <purl.org/net/chneukirchen>
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to
|
data/HISTORY.md
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
Sun Dec 4 18:48:03 2015 Jeremy Daer <jeremydaer@gmail.com>
|
2
2
|
|
3
|
-
*
|
4
|
-
third-party requests, closing the door on many
|
5
|
-
|
6
|
-
|
3
|
+
* First-party "SameSite" cookies. Browsers omit SameSite cookies
|
4
|
+
from third-party requests, closing the door on many CSRF attacks.
|
5
|
+
|
6
|
+
Pass `same_site: true` (or `:strict`) to enable:
|
7
|
+
response.set_cookie 'foo', value: 'bar', same_site: true
|
8
|
+
or `same_site: :lax` to use Lax enforcement:
|
9
|
+
response.set_cookie 'foo', value: 'bar', same_site: :lax
|
10
|
+
|
11
|
+
Based on version 7 of the Same-site Cookies internet draft:
|
12
|
+
https://tools.ietf.org/html/draft-west-first-party-cookies-07
|
13
|
+
|
14
|
+
Thanks to Ben Toews (@mastahyeti) and Bob Long (@bobjflong) for
|
15
|
+
updating to drafts 5 and 7.
|
7
16
|
|
8
17
|
Tue Nov 3 16:17:26 2015 Aaron Patterson <tenderlove@ruby-lang.org>
|
9
18
|
|
data/example/protectedlobster.rb
CHANGED
data/example/protectedlobster.ru
CHANGED
data/lib/rack.rb
CHANGED
@@ -17,7 +17,7 @@ module Rack
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.split_header_value(str)
|
20
|
-
str.scan(
|
20
|
+
str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
|
21
21
|
end
|
22
22
|
|
23
23
|
def initialize
|
@@ -38,7 +38,7 @@ module Rack
|
|
38
38
|
|
39
39
|
def to_s
|
40
40
|
map do |k, v|
|
41
|
-
"#{k}="
|
41
|
+
"#{k}=" << (UNQUOTED.include?(k) ? v.to_s : quote(v))
|
42
42
|
end.join(', ')
|
43
43
|
end
|
44
44
|
|
@@ -50,4 +50,3 @@ module Rack
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
data/lib/rack/common_logger.rb
CHANGED
@@ -48,7 +48,7 @@ module Rack
|
|
48
48
|
now.strftime("%d/%b/%Y:%H:%M:%S %z"),
|
49
49
|
env[REQUEST_METHOD],
|
50
50
|
env[PATH_INFO],
|
51
|
-
env[QUERY_STRING].empty? ? "" : "
|
51
|
+
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
52
52
|
env[HTTP_VERSION],
|
53
53
|
status.to_s[0..3],
|
54
54
|
length,
|
data/lib/rack/directory.rb
CHANGED
@@ -59,13 +59,21 @@ table { width:100%%; }
|
|
59
59
|
def initialize(root, app=nil)
|
60
60
|
@root = ::File.expand_path(root)
|
61
61
|
@app = app || Rack::File.new(@root)
|
62
|
+
@head = Rack::Head.new(lambda { |env| get env })
|
62
63
|
end
|
63
64
|
|
64
65
|
def call(env)
|
66
|
+
# strip body if this is a HEAD call
|
67
|
+
@head.call env
|
68
|
+
end
|
69
|
+
|
70
|
+
def get(env)
|
65
71
|
script_name = env[SCRIPT_NAME]
|
66
72
|
path_info = Utils.unescape_path(env[PATH_INFO])
|
67
73
|
|
68
|
-
if
|
74
|
+
if bad_request = check_bad_request(path_info)
|
75
|
+
bad_request
|
76
|
+
elsif forbidden = check_forbidden(path_info)
|
69
77
|
forbidden
|
70
78
|
else
|
71
79
|
path = ::File.join(@root, path_info)
|
@@ -73,6 +81,16 @@ table { width:100%%; }
|
|
73
81
|
end
|
74
82
|
end
|
75
83
|
|
84
|
+
def check_bad_request(path_info)
|
85
|
+
return if Utils.valid_path?(path_info)
|
86
|
+
|
87
|
+
body = "Bad Request\n"
|
88
|
+
size = body.bytesize
|
89
|
+
return [400, {CONTENT_TYPE => "text/plain",
|
90
|
+
CONTENT_LENGTH => size.to_s,
|
91
|
+
"X-Cascade" => "pass"}, [body]]
|
92
|
+
end
|
93
|
+
|
76
94
|
def check_forbidden(path_info)
|
77
95
|
return unless path_info.include? ".."
|
78
96
|
|
@@ -155,7 +173,7 @@ table { width:100%%; }
|
|
155
173
|
return format % (int.to_f / size) if int >= size
|
156
174
|
end
|
157
175
|
|
158
|
-
int
|
176
|
+
"#{int}B"
|
159
177
|
end
|
160
178
|
end
|
161
179
|
end
|
data/lib/rack/etag.rb
CHANGED
@@ -65,10 +65,10 @@ module Rack
|
|
65
65
|
|
66
66
|
body.each do |part|
|
67
67
|
parts << part
|
68
|
-
(digest ||= Digest::
|
68
|
+
(digest ||= Digest::SHA256.new) << part unless part.empty?
|
69
69
|
end
|
70
70
|
|
71
|
-
[digest && digest.hexdigest, parts]
|
71
|
+
[digest && digest.hexdigest.byteslice(0, 32), parts]
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/lib/rack/file.rb
CHANGED
@@ -2,6 +2,7 @@ require 'time'
|
|
2
2
|
require 'rack/utils'
|
3
3
|
require 'rack/mime'
|
4
4
|
require 'rack/request'
|
5
|
+
require 'rack/head'
|
5
6
|
|
6
7
|
module Rack
|
7
8
|
# Rack::File serves files below the +root+ directory given, according to the
|
@@ -22,17 +23,24 @@ module Rack
|
|
22
23
|
@root = root
|
23
24
|
@headers = headers
|
24
25
|
@default_mime = default_mime
|
26
|
+
@head = Rack::Head.new(lambda { |env| get env })
|
25
27
|
end
|
26
28
|
|
27
29
|
def call(env)
|
30
|
+
# HEAD requests drop the response body, including 4xx error messages.
|
31
|
+
@head.call env
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(env)
|
28
35
|
request = Rack::Request.new env
|
29
36
|
unless ALLOWED_VERBS.include? request.request_method
|
30
37
|
return fail(405, "Method Not Allowed", {'Allow' => ALLOW_HEADER})
|
31
38
|
end
|
32
39
|
|
33
40
|
path_info = Utils.unescape_path request.path_info
|
34
|
-
|
41
|
+
return fail(400, "Bad Request") unless Utils.valid_path?(path_info)
|
35
42
|
|
43
|
+
clean_path_info = Utils.clean_path_info(path_info)
|
36
44
|
path = ::File.join(@root, clean_path_info)
|
37
45
|
|
38
46
|
available = begin
|
@@ -131,6 +139,7 @@ module Rack
|
|
131
139
|
|
132
140
|
def fail(status, body, headers = {})
|
133
141
|
body += "\n"
|
142
|
+
|
134
143
|
[
|
135
144
|
status,
|
136
145
|
{
|
data/lib/rack/handler.rb
CHANGED
data/lib/rack/mock.rb
CHANGED
@@ -128,7 +128,7 @@ module Rack
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
empty_str =
|
131
|
+
empty_str = String.new.force_encoding(Encoding::ASCII_8BIT)
|
132
132
|
opts[:input] ||= empty_str
|
133
133
|
if String === opts[:input]
|
134
134
|
rack_input = StringIO.new(opts[:input])
|
@@ -26,7 +26,7 @@ module Rack
|
|
26
26
|
str = if left < size
|
27
27
|
@io.read left
|
28
28
|
else
|
29
|
-
|
29
|
+
@io.read size
|
30
30
|
end
|
31
31
|
|
32
32
|
if str
|
@@ -100,8 +100,6 @@ module Rack
|
|
100
100
|
# Generic multipart cases, not coming from a form
|
101
101
|
data = {:type => content_type,
|
102
102
|
:name => name, :tempfile => body, :head => head}
|
103
|
-
elsif !filename && data.empty?
|
104
|
-
return
|
105
103
|
end
|
106
104
|
|
107
105
|
yield data
|
@@ -347,6 +345,7 @@ module Rack
|
|
347
345
|
k,v = param.split('=', 2)
|
348
346
|
k.strip!
|
349
347
|
v.strip!
|
348
|
+
v = v[1..-2] if v[0] == '"' && v[-1] == '"'
|
350
349
|
encoding = Encoding.find v if k == CHARSET
|
351
350
|
end
|
352
351
|
end
|
data/lib/rack/query_parser.rb
CHANGED
@@ -79,16 +79,22 @@ module Rack
|
|
79
79
|
raise RangeError if depth <= 0
|
80
80
|
|
81
81
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
82
|
-
k = $1 || ''
|
83
|
-
after = $' || ''
|
82
|
+
k = $1 || ''.freeze
|
83
|
+
after = $' || ''.freeze
|
84
84
|
|
85
|
-
|
85
|
+
if k.empty?
|
86
|
+
if !v.nil? && name == "[]".freeze
|
87
|
+
return Array(v)
|
88
|
+
else
|
89
|
+
return
|
90
|
+
end
|
91
|
+
end
|
86
92
|
|
87
|
-
if after ==
|
93
|
+
if after == ''.freeze
|
88
94
|
params[k] = v
|
89
|
-
elsif after == "["
|
95
|
+
elsif after == "[".freeze
|
90
96
|
params[name] = v
|
91
|
-
elsif after == "[]"
|
97
|
+
elsif after == "[]".freeze
|
92
98
|
params[k] ||= []
|
93
99
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
94
100
|
params[k] << v
|
@@ -96,7 +102,8 @@ module Rack
|
|
96
102
|
child_key = $1
|
97
103
|
params[k] ||= []
|
98
104
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
99
|
-
|
105
|
+
first_key = child_key.gsub(/[\[\]]/, ' ').split.first
|
106
|
+
if params_hash_type?(params[k].last) && !params[k].last.key?(first_key)
|
100
107
|
normalize_params(params[k].last, child_key, v, depth - 1)
|
101
108
|
else
|
102
109
|
params[k] << normalize_params(make_params, child_key, v, depth - 1)
|
@@ -107,7 +114,7 @@ module Rack
|
|
107
114
|
params[k] = normalize_params(params[k], after, v, depth - 1)
|
108
115
|
end
|
109
116
|
|
110
|
-
|
117
|
+
params
|
111
118
|
end
|
112
119
|
|
113
120
|
def make_params
|
data/lib/rack/reloader.rb
CHANGED
@@ -26,6 +26,7 @@ module Rack
|
|
26
26
|
@last = (Time.now - cooldown)
|
27
27
|
@cache = {}
|
28
28
|
@mtimes = {}
|
29
|
+
@reload_mutex = Mutex.new
|
29
30
|
|
30
31
|
extend backend
|
31
32
|
end
|
@@ -33,7 +34,7 @@ module Rack
|
|
33
34
|
def call(env)
|
34
35
|
if @cooldown and Time.now > @last + @cooldown
|
35
36
|
if Thread.list.size > 1
|
36
|
-
|
37
|
+
@reload_mutex.synchronize{ reload! }
|
37
38
|
else
|
38
39
|
reload!
|
39
40
|
end
|
data/lib/rack/request.rb
CHANGED
@@ -16,23 +16,6 @@ module Rack
|
|
16
16
|
super(env)
|
17
17
|
end
|
18
18
|
|
19
|
-
# shortcut for <tt>request.params[key]</tt>
|
20
|
-
def [](key)
|
21
|
-
params[key.to_s]
|
22
|
-
end
|
23
|
-
|
24
|
-
# shortcut for <tt>request.params[key] = value</tt>
|
25
|
-
#
|
26
|
-
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
27
|
-
def []=(key, value)
|
28
|
-
params[key.to_s] = value
|
29
|
-
end
|
30
|
-
|
31
|
-
# like Hash#values_at
|
32
|
-
def values_at(*keys)
|
33
|
-
keys.map{|key| params[key] }
|
34
|
-
end
|
35
|
-
|
36
19
|
def params
|
37
20
|
@params ||= super
|
38
21
|
end
|
@@ -160,7 +143,7 @@ module Rack
|
|
160
143
|
|
161
144
|
def session
|
162
145
|
fetch_header(RACK_SESSION) do |k|
|
163
|
-
set_header RACK_SESSION,
|
146
|
+
set_header RACK_SESSION, default_session
|
164
147
|
end
|
165
148
|
end
|
166
149
|
|
@@ -437,8 +420,35 @@ module Rack
|
|
437
420
|
ip =~ /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i
|
438
421
|
end
|
439
422
|
|
423
|
+
# shortcut for <tt>request.params[key]</tt>
|
424
|
+
def [](key)
|
425
|
+
if $verbose
|
426
|
+
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead")
|
427
|
+
end
|
428
|
+
|
429
|
+
params[key.to_s]
|
430
|
+
end
|
431
|
+
|
432
|
+
# shortcut for <tt>request.params[key] = value</tt>
|
433
|
+
#
|
434
|
+
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
435
|
+
def []=(key, value)
|
436
|
+
if $verbose
|
437
|
+
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead")
|
438
|
+
end
|
439
|
+
|
440
|
+
params[key.to_s] = value
|
441
|
+
end
|
442
|
+
|
443
|
+
# like Hash#values_at
|
444
|
+
def values_at(*keys)
|
445
|
+
keys.map { |key| params[key] }
|
446
|
+
end
|
447
|
+
|
440
448
|
private
|
441
449
|
|
450
|
+
def default_session; {}; end
|
451
|
+
|
442
452
|
def parse_http_accept_header(header)
|
443
453
|
header.to_s.split(/\s*,\s*/).map do |part|
|
444
454
|
attribute, parameters = part.split(/\s*;\s*/, 2)
|
data/lib/rack/static.rb
CHANGED
@@ -93,7 +93,7 @@ module Rack
|
|
93
93
|
# HTTP Headers
|
94
94
|
@header_rules = options[:header_rules] || []
|
95
95
|
# Allow for legacy :cache_control option while prioritizing global header_rules setting
|
96
|
-
@header_rules.
|
96
|
+
@header_rules.unshift([:all, {CACHE_CONTROL => options[:cache_control]}]) if options[:cache_control]
|
97
97
|
|
98
98
|
@file_server = Rack::File.new(root)
|
99
99
|
end
|
data/lib/rack/urlmap.rb
CHANGED
@@ -47,11 +47,13 @@ module Rack
|
|
47
47
|
server_name = env[SERVER_NAME]
|
48
48
|
server_port = env[SERVER_PORT]
|
49
49
|
|
50
|
+
is_same_server = casecmp?(http_host, server_name) ||
|
51
|
+
casecmp?(http_host, "#{server_name}:#{server_port}")
|
52
|
+
|
50
53
|
@mapping.each do |host, location, match, app|
|
51
54
|
unless casecmp?(http_host, host) \
|
52
55
|
|| casecmp?(server_name, host) \
|
53
|
-
|| (!host &&
|
54
|
-
casecmp?(http_host, "#{server_name}:#{server_port}")))
|
56
|
+
|| (!host && is_same_server)
|
55
57
|
next
|
56
58
|
end
|
57
59
|
|
data/lib/rack/utils.rb
CHANGED
@@ -248,13 +248,23 @@ module Rack
|
|
248
248
|
rfc2822(value[:expires].clone.gmtime) if value[:expires]
|
249
249
|
secure = "; secure" if value[:secure]
|
250
250
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
251
|
-
|
251
|
+
same_site =
|
252
|
+
case value[:same_site]
|
253
|
+
when false, nil
|
254
|
+
nil
|
255
|
+
when :lax, 'Lax', :Lax
|
256
|
+
'; SameSite=Lax'.freeze
|
257
|
+
when true, :strict, 'Strict', :Strict
|
258
|
+
'; SameSite=Strict'.freeze
|
259
|
+
else
|
260
|
+
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
261
|
+
end
|
252
262
|
value = value[:value]
|
253
263
|
end
|
254
264
|
value = [value] unless Array === value
|
255
265
|
|
256
266
|
cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
|
257
|
-
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{
|
267
|
+
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
|
258
268
|
|
259
269
|
case header
|
260
270
|
when nil, ''
|
@@ -551,6 +561,7 @@ module Rack
|
|
551
561
|
428 => 'Precondition Required',
|
552
562
|
429 => 'Too Many Requests',
|
553
563
|
431 => 'Request Header Fields Too Large',
|
564
|
+
451 => 'Unavailable for Legal Reasons',
|
554
565
|
500 => 'Internal Server Error',
|
555
566
|
501 => 'Not Implemented',
|
556
567
|
502 => 'Bad Gateway',
|
@@ -598,5 +609,12 @@ module Rack
|
|
598
609
|
end
|
599
610
|
module_function :clean_path_info
|
600
611
|
|
612
|
+
NULL_BYTE = "\0".freeze
|
613
|
+
|
614
|
+
def valid_path?(path)
|
615
|
+
path.valid_encoding? && !path.include?(NULL_BYTE)
|
616
|
+
end
|
617
|
+
module_function :valid_path?
|
618
|
+
|
601
619
|
end
|
602
620
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
2016-05-04 12:00:25: (log.c.164) server started
|
2
|
+
2016-05-04 12:00:35: (server.c.1558) server stopped by UID = 502 PID = 80374
|
3
|
+
2016-05-04 12:13:42: (log.c.164) server started
|
4
|
+
2016-05-04 12:13:51: (server.c.1558) server stopped by UID = 502 PID = 82209
|
5
|
+
2016-05-04 12:16:30: (log.c.164) server started
|
6
|
+
2016-05-04 12:16:39: (server.c.1558) server stopped by UID = 502 PID = 82523
|
7
|
+
2016-05-04 12:18:17: (log.c.164) server started
|
8
|
+
2016-05-04 12:18:26: (server.c.1558) server stopped by UID = 502 PID = 82745
|
data/test/helper.rb
CHANGED
@@ -2,7 +2,10 @@ require 'minitest/autorun'
|
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
class TestCase < Minitest::Test
|
5
|
-
|
5
|
+
# Check for Lighttpd and launch it for tests if available.
|
6
|
+
`which lighttpd`
|
7
|
+
|
8
|
+
if $?.success?
|
6
9
|
begin
|
7
10
|
# Keep this first.
|
8
11
|
LIGHTTPD_PID = fork {
|
@@ -0,0 +1,11 @@
|
|
1
|
+
--AaB03x
|
2
|
+
Content-Type: text/plain; charset="utf-8"
|
3
|
+
Content-disposition: form-data; name="user_sid"
|
4
|
+
|
5
|
+
bbf14f82-d2aa-4c07-9fb8-ca6714a7ea97
|
6
|
+
--AaB03x
|
7
|
+
Content-Type: image/png; charset=UTF-8
|
8
|
+
Content-disposition: form-data; name="file";
|
9
|
+
filename="b67879ed-bfed-4491-a8cc-f99cca769f94.png"
|
10
|
+
|
11
|
+
--AaB03x
|
data/test/spec_directory.rb
CHANGED
@@ -63,6 +63,13 @@ describe Rack::Directory do
|
|
63
63
|
assert_match(res, /passed!/)
|
64
64
|
end
|
65
65
|
|
66
|
+
it "serve uri with URL encoded null byte (%00) in filenames" do
|
67
|
+
res = Rack::MockRequest.new(Rack::Lint.new(app))
|
68
|
+
.get("/cgi/test%00")
|
69
|
+
|
70
|
+
res.must_be :bad_request?
|
71
|
+
end
|
72
|
+
|
66
73
|
it "not allow directory traversal" do
|
67
74
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
68
75
|
get("/cgi/../test")
|
@@ -130,4 +137,12 @@ describe Rack::Directory do
|
|
130
137
|
res = mr.get("/script-path/cgi/test+directory/test+file")
|
131
138
|
res.must_be :ok?
|
132
139
|
end
|
140
|
+
|
141
|
+
it "return error when file not found for head request" do
|
142
|
+
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
143
|
+
head("/cgi/missing")
|
144
|
+
|
145
|
+
res.must_be :not_found?
|
146
|
+
res.body.must_be :empty?
|
147
|
+
end
|
133
148
|
end
|
data/test/spec_etag.rb
CHANGED
@@ -22,13 +22,13 @@ describe Rack::ETag do
|
|
22
22
|
it "set ETag if none is set if status is 200" do
|
23
23
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
24
24
|
response = etag(app).call(request)
|
25
|
-
response[1]['ETag'].must_equal "W/\"
|
25
|
+
response[1]['ETag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
|
26
26
|
end
|
27
27
|
|
28
28
|
it "set ETag if none is set if status is 201" do
|
29
29
|
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
30
30
|
response = etag(app).call(request)
|
31
|
-
response[1]['ETag'].must_equal "W/\"
|
31
|
+
response[1]['ETag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
|
32
32
|
end
|
33
33
|
|
34
34
|
it "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do
|
data/test/spec_file.rb
CHANGED
@@ -68,6 +68,11 @@ describe Rack::File do
|
|
68
68
|
assert_match(res, /ruby/)
|
69
69
|
end
|
70
70
|
|
71
|
+
it "serve uri with URL encoded null byte (%00) in filenames" do
|
72
|
+
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test%00")
|
73
|
+
res.must_be :bad_request?
|
74
|
+
end
|
75
|
+
|
71
76
|
it "allow safe directory traversal" do
|
72
77
|
req = Rack::MockRequest.new(file(DOCROOT))
|
73
78
|
|
@@ -237,4 +242,10 @@ describe Rack::File do
|
|
237
242
|
res['Content-Type'].must_equal nil
|
238
243
|
end
|
239
244
|
|
245
|
+
it "return error when file not found for head request" do
|
246
|
+
res = Rack::MockRequest.new(file(DOCROOT)).head("/cgi/missing")
|
247
|
+
res.must_be :not_found?
|
248
|
+
res.body.must_be :empty?
|
249
|
+
end
|
250
|
+
|
240
251
|
end
|
data/test/spec_multipart.rb
CHANGED
@@ -72,6 +72,13 @@ describe Rack::Multipart do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
it "handles quoted encodings" do
|
76
|
+
# See #905
|
77
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:unity3d_wwwform))
|
78
|
+
params = Rack::Multipart.parse_multipart(env)
|
79
|
+
params['user_sid'].encoding.must_equal Encoding::UTF_8
|
80
|
+
end
|
81
|
+
|
75
82
|
it "raise RangeError if the key space is exhausted" do
|
76
83
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
77
84
|
|
@@ -88,6 +95,7 @@ describe Rack::Multipart do
|
|
88
95
|
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
89
96
|
params = Rack::Multipart.parse_multipart(env)
|
90
97
|
params['profile']['bio'].must_include 'hello'
|
98
|
+
params['profile'].keys.must_include 'public_email'
|
91
99
|
end
|
92
100
|
|
93
101
|
it "reject insanely long boundaries" do
|
data/test/spec_request.rb
CHANGED
@@ -1305,6 +1305,11 @@ EOF
|
|
1305
1305
|
req.trusted_proxy?("2001:470:1f0b:18f8::1").must_equal nil
|
1306
1306
|
end
|
1307
1307
|
|
1308
|
+
it "sets the default session to an empty hash" do
|
1309
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
1310
|
+
assert_equal Hash.new, req.session
|
1311
|
+
end
|
1312
|
+
|
1308
1313
|
class MyRequest < Rack::Request
|
1309
1314
|
def params
|
1310
1315
|
{:foo => "bar"}
|
data/test/spec_response.rb
CHANGED
@@ -115,16 +115,66 @@ describe Rack::Response do
|
|
115
115
|
response["Set-Cookie"].must_equal "foo=bar"
|
116
116
|
end
|
117
117
|
|
118
|
-
it "can set
|
118
|
+
it "can set SameSite cookies with symbol value :lax" do
|
119
119
|
response = Rack::Response.new
|
120
|
-
response.set_cookie "foo", {:value => "bar", :
|
121
|
-
response["Set-Cookie"].must_equal "foo=bar;
|
120
|
+
response.set_cookie "foo", {:value => "bar", :same_site => :lax}
|
121
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
|
122
|
+
end
|
123
|
+
|
124
|
+
it "can set SameSite cookies with symbol value :Lax" do
|
125
|
+
response = Rack::Response.new
|
126
|
+
response.set_cookie "foo", {:value => "bar", :same_site => :lax}
|
127
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "can set SameSite cookies with string value 'Lax'" do
|
131
|
+
response = Rack::Response.new
|
132
|
+
response.set_cookie "foo", {:value => "bar", :same_site => "Lax"}
|
133
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
|
134
|
+
end
|
135
|
+
|
136
|
+
it "can set SameSite cookies with boolean value true" do
|
137
|
+
response = Rack::Response.new
|
138
|
+
response.set_cookie "foo", {:value => "bar", :same_site => true}
|
139
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
|
140
|
+
end
|
141
|
+
|
142
|
+
it "can set SameSite cookies with symbol value :strict" do
|
143
|
+
response = Rack::Response.new
|
144
|
+
response.set_cookie "foo", {:value => "bar", :same_site => :strict}
|
145
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
|
146
|
+
end
|
147
|
+
|
148
|
+
it "can set SameSite cookies with symbol value :Strict" do
|
149
|
+
response = Rack::Response.new
|
150
|
+
response.set_cookie "foo", {:value => "bar", :same_site => :Strict}
|
151
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "can set SameSite cookies with string value 'Strict'" do
|
155
|
+
response = Rack::Response.new
|
156
|
+
response.set_cookie "foo", {:value => "bar", :same_site => "Strict"}
|
157
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
|
158
|
+
end
|
159
|
+
|
160
|
+
it "validates the SameSite option value" do
|
161
|
+
response = Rack::Response.new
|
162
|
+
lambda {
|
163
|
+
response.set_cookie "foo", {:value => "bar", :same_site => "Foo"}
|
164
|
+
}.must_raise(ArgumentError).
|
165
|
+
message.must_match(/Invalid SameSite value: "Foo"/)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "can set SameSite cookies with symbol value" do
|
169
|
+
response = Rack::Response.new
|
170
|
+
response.set_cookie "foo", {:value => "bar", :same_site => :Strict}
|
171
|
+
response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
|
122
172
|
end
|
123
173
|
|
124
174
|
[ nil, false ].each do |non_truthy|
|
125
|
-
it "omits
|
175
|
+
it "omits SameSite attribute given a #{non_truthy.inspect} value" do
|
126
176
|
response = Rack::Response.new
|
127
|
-
response.set_cookie "foo", {:value => "bar", :
|
177
|
+
response.set_cookie "foo", {:value => "bar", :same_site => non_truthy}
|
128
178
|
response["Set-Cookie"].must_equal "foo=bar"
|
129
179
|
end
|
130
180
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rack/session/abstract/id'
|
3
|
+
|
4
|
+
describe Rack::Session::Abstract::SessionHash do
|
5
|
+
attr_reader :hash
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
store = Class.new do
|
10
|
+
def load_session(req)
|
11
|
+
["id", {foo: :bar, baz: :qux}]
|
12
|
+
end
|
13
|
+
def session_exists?(req)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@hash = Rack::Session::Abstract::SessionHash.new(store.new, nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns keys" do
|
21
|
+
assert_equal ["foo", "baz"], hash.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns values" do
|
25
|
+
assert_equal [:bar, :qux], hash.values
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/spec_utils.rb
CHANGED
@@ -206,6 +206,14 @@ describe Rack::Utils do
|
|
206
206
|
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
|
207
207
|
must_equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
|
208
208
|
|
209
|
+
Rack::Utils.parse_nested_query("x[][y]=1&x[][z][w]=a&x[][y]=2&x[][z][w]=b").
|
210
|
+
must_equal "x" => [{"y" => "1", "z" => {"w" => "a"}}, {"y" => "2", "z" => {"w" => "b"}}]
|
211
|
+
Rack::Utils.parse_nested_query("x[][z][w]=a&x[][y]=1&x[][z][w]=b&x[][y]=2").
|
212
|
+
must_equal "x" => [{"y" => "1", "z" => {"w" => "a"}}, {"y" => "2", "z" => {"w" => "b"}}]
|
213
|
+
|
214
|
+
Rack::Utils.parse_nested_query("data[books][][data][page]=1&data[books][][data][page]=2").
|
215
|
+
must_equal "data" => { "books" => [{ "data" => { "page" => "1"}}, { "data" => { "page" => "2"}}] }
|
216
|
+
|
209
217
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
|
210
218
|
must_raise(Rack::Utils::ParameterTypeError).
|
211
219
|
message.must_equal "expected Hash (got String) for param `y'"
|
@@ -300,13 +308,15 @@ describe Rack::Utils do
|
|
300
308
|
must_equal 'x[y][][z]=1&x[y][][z]=2'
|
301
309
|
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1', 'w' => 'a' }, { 'z' => '2', 'w' => '3' }] }).
|
302
310
|
must_equal 'x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3'
|
311
|
+
Rack::Utils.build_nested_query({"foo" => ["1", ["2"]]}).
|
312
|
+
must_equal 'foo[]=1&foo[][]=2'
|
303
313
|
|
304
314
|
lambda { Rack::Utils.build_nested_query("foo=bar") }.
|
305
315
|
must_raise(ArgumentError).
|
306
316
|
message.must_equal "value must be a Hash"
|
307
317
|
end
|
308
318
|
|
309
|
-
|
319
|
+
it 'performs the inverse function of #parse_nested_query' do
|
310
320
|
[{"foo" => nil, "bar" => ""},
|
311
321
|
{"foo" => "bar", "baz" => ""},
|
312
322
|
{"foo" => ["1", "2"]},
|
@@ -323,7 +333,8 @@ describe Rack::Utils do
|
|
323
333
|
{"x" => {"y" => [{"v" => {"w" => "1"}}]}},
|
324
334
|
{"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
|
325
335
|
{"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
|
326
|
-
{"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
|
336
|
+
{"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}},
|
337
|
+
{"foo" => ["1", ["2"]]},
|
327
338
|
].each { |params|
|
328
339
|
qs = Rack::Utils.build_nested_query(params)
|
329
340
|
Rack::Utils.parse_nested_query(qs).must_equal params
|
data/test/spec_webrick.rb
CHANGED
@@ -17,6 +17,19 @@ describe Rack::Handler::WEBrick do
|
|
17
17
|
Rack::Lint.new(TestRequest.new)
|
18
18
|
@thread = Thread.new { @server.start }
|
19
19
|
trap(:INT) { @server.shutdown }
|
20
|
+
@status_thread = Thread.new do
|
21
|
+
seconds = 10
|
22
|
+
wait_time = 0.1
|
23
|
+
until is_running? || seconds <= 0
|
24
|
+
seconds -= wait_time
|
25
|
+
sleep wait_time
|
26
|
+
end
|
27
|
+
raise "Server never reached status 'Running'" unless is_running?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_running?
|
32
|
+
@server.status == :Running
|
20
33
|
end
|
21
34
|
|
22
35
|
it "respond" do
|
@@ -188,6 +201,7 @@ describe Rack::Handler::WEBrick do
|
|
188
201
|
end
|
189
202
|
|
190
203
|
after do
|
204
|
+
@status_thread.join
|
191
205
|
@server.shutdown
|
192
206
|
@thread.join
|
193
207
|
end
|
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: 2.0.0.
|
4
|
+
version: 2.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Neukirchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -185,6 +185,7 @@ files:
|
|
185
185
|
- test/cgi/assets/javascripts/app.js
|
186
186
|
- test/cgi/assets/stylesheets/app.css
|
187
187
|
- test/cgi/lighttpd.conf
|
188
|
+
- test/cgi/lighttpd.errors
|
188
189
|
- test/cgi/rackup_stub.rb
|
189
190
|
- test/cgi/sample_rackup.ru
|
190
191
|
- test/cgi/test
|
@@ -221,6 +222,7 @@ files:
|
|
221
222
|
- test/multipart/semicolon
|
222
223
|
- test/multipart/text
|
223
224
|
- test/multipart/three_files_three_fields
|
225
|
+
- test/multipart/unity3d_wwwform
|
224
226
|
- test/multipart/webkit
|
225
227
|
- test/rackup/config.ru
|
226
228
|
- test/registering_handler/rack/handler/registering_myself.rb
|
@@ -262,6 +264,7 @@ files:
|
|
262
264
|
- test/spec_sendfile.rb
|
263
265
|
- test/spec_server.rb
|
264
266
|
- test/spec_session_abstract_id.rb
|
267
|
+
- test/spec_session_abstract_session_hash.rb
|
265
268
|
- test/spec_session_cookie.rb
|
266
269
|
- test/spec_session_memcache.rb
|
267
270
|
- test/spec_session_pool.rb
|
@@ -300,7 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
300
303
|
version: 1.3.1
|
301
304
|
requirements: []
|
302
305
|
rubyforge_project:
|
303
|
-
rubygems_version: 2.
|
306
|
+
rubygems_version: 2.6.4
|
304
307
|
signing_key:
|
305
308
|
specification_version: 4
|
306
309
|
summary: a modular Ruby webserver interface
|
@@ -343,6 +346,7 @@ test_files:
|
|
343
346
|
- test/spec_sendfile.rb
|
344
347
|
- test/spec_server.rb
|
345
348
|
- test/spec_session_abstract_id.rb
|
349
|
+
- test/spec_session_abstract_session_hash.rb
|
346
350
|
- test/spec_session_cookie.rb
|
347
351
|
- test/spec_session_memcache.rb
|
348
352
|
- test/spec_session_pool.rb
|