rack-cors 0.4.1 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: aaa518c3408420a39bd3c41bd822cdda6309beb2
4
- data.tar.gz: 8fa4c9d5141e4d6c4df8600175f44ba6803ebe9f
2
+ SHA256:
3
+ metadata.gz: a12cdfc5aca2abf0cf86fb1ca217619fa6b40cad19721118016e064554f46ba0
4
+ data.tar.gz: 2874199b748909fdfd3e8ec601bd8620bc0235e60c66226259a79ff2404dbaf8
5
5
  SHA512:
6
- metadata.gz: 633c772b16e08fad8fb93a7d1bf5d62a398abb534a27ce0d44df843242c5e1077112e98b1eb7e2d3849da952fec1754cd2e8451195c1a2c86ac567d69652b859
7
- data.tar.gz: 8e0265d2d82db72ac68871dcf0eaf974809638d5a52514f99392b731fe09050ab07968c5e538a35d7bba2b7892cc62b3d6b7b401fe8a05c4648f5ad57a092abf
6
+ metadata.gz: 2b71fe191ad396ab85e8c1966e979fa3516ee768bae6ed93fd1d43644eada8a455dbab00990ef22440ee7f82dab16a37b283897403d4eba674547bda1f0b86f5
7
+ data.tar.gz: a31481b3f6d9d45bdc522c444e923438f7f513a57796bf2cf6eaaa665d87f7479bf5f1e5f5ea8d380ce7194f0d3690823e1a681e55c17317cab29bf87b7a7303
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.2.5
5
- - 2.3.0
6
- - 2.3.1
4
+ - 2.2
5
+ - 2.3
6
+ - 2.4
7
+ - 2.5
8
+ - 2.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 1.0.5 - 2019-11-14
5
+ ### Changed
6
+ - Update Gem spec to require rack >= 1.6.0
7
+
8
+ ## 1.0.4 - 2019-11-13
9
+ ### Security
10
+ - Escape and resolve path before evaluating resource rules (thanks to Colby Morgan)
11
+
12
+ ## 1.0.3 - 2019-03-24
13
+ ### Changed
14
+ - Don't send 'Content-Type' header with pre-flight requests
15
+ - Allow ruby array for vary header config
16
+
17
+ ## 1.0.2 - 2017-10-22
18
+ ### Fixed
19
+ - Automatically allow simple headers when headers are set
20
+
21
+ ## 1.0.1 - 2017-07-18
22
+ ### Fixed
23
+ - Allow lambda origin configuration
24
+
25
+ ## 1.0.0 - 2017-07-15
26
+ ### Security
27
+ - Don't implicitly accept 'null' origins when 'file://' is specified
28
+ (https://github.com/cyu/rack-cors/pull/134)
29
+ - Ignore '' origins (https://github.com/cyu/rack-cors/issues/139)
30
+ - Default credentials option on resources to false
31
+ (https://github.com/cyu/rack-cors/issues/95)
32
+ - Don't allow credentials option to be true if '*' is specified is origin
33
+ (https://github.com/cyu/rack-cors/pull/142)
34
+ - Don't reflect Origin header when '*' is specified as origin
35
+ (https://github.com/cyu/rack-cors/pull/142)
36
+
37
+ ### Fixed
38
+ - Don't respond immediately on non-matching preflight requests instead of
39
+ sending them through the app (https://github.com/cyu/rack-cors/pull/106)
40
+
41
+ ## 0.4.1 - 2017-02-01
42
+ ### Fixed
43
+ - Return miss result in X-Rack-CORS instead of incorrectly returning
44
+ preflight-hit
45
+
46
+ ## 0.4.0 - 2015-04-15
47
+ ### Changed
48
+ - Don't set HTTP_ORIGIN with HTTP_X_ORIGIN if nil
49
+
50
+ ### Added
51
+ - Calculate vary headers for non-CORS resources
52
+ - Support custom vary headers for resource
53
+ - Support :if option for resource
54
+ - Support :any as a possible value for :methods option
55
+
56
+ ### Fixed
57
+ - Don't symbolize incoming HTTP request methods
58
+
59
+ ## 0.3.1 - 2014-12-27
60
+ ### Changed
61
+ - Changed the env key to rack.cors to avoid Rack::Lint warnings
62
+
63
+ ## 0.3.0 - 2014-10-19
64
+ ### Added
65
+ - Added support for defining a logger with a Proc
66
+ - Return a X-Rack-CORS header when in debug mode detailing how Rack::Cors
67
+ processed a request
68
+ - Added support for non HTTP/HTTPS origins when just a domain is specified
69
+
70
+ ### Changed
71
+ - Changed the log level of the fallback logger to DEBUG
72
+ - Print warning when attempting to use :any as an allowed method
73
+ - Treat incoming `Origin: null` headers as file://
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in rack-cors.gemspec
4
4
  gemspec
5
5
 
6
- gem 'pry-byebug'
6
+ gem 'pry-byebug', '~> 3.6.0'
data/README.md CHANGED
@@ -13,7 +13,7 @@ Install the gem:
13
13
  Or in your Gemfile:
14
14
 
15
15
  ```ruby
16
- gem 'rack-cors', :require => 'rack/cors'
16
+ gem 'rack-cors'
17
17
  ```
18
18
 
19
19
 
@@ -25,31 +25,30 @@ Put something like the code below in `config/application.rb` of your Rails appli
25
25
  ```ruby
26
26
  module YourApp
27
27
  class Application < Rails::Application
28
-
29
28
  # ...
30
29
 
31
- # Rails 3/4
30
+ # Rails 5
32
31
 
33
- config.middleware.insert_before 0, "Rack::Cors" do
32
+ config.middleware.insert_before 0, Rack::Cors do
34
33
  allow do
35
34
  origins '*'
36
- resource '*', :headers => :any, :methods => [:get, :post, :options]
35
+ resource '*', headers: :any, methods: [:get, :post, :options]
37
36
  end
38
37
  end
39
-
40
- # Rails 5
41
38
 
42
- config.middleware.insert_before 0, Rack::Cors do
39
+ # Rails 3/4
40
+
41
+ config.middleware.insert_before 0, "Rack::Cors" do
43
42
  allow do
44
43
  origins '*'
45
- resource '*', :headers => :any, :methods => [:get, :post, :options]
44
+ resource '*', headers: :any, methods: [:get, :post, :options]
46
45
  end
47
46
  end
48
-
49
47
  end
50
48
  end
51
49
  ```
52
- Refer to [rails 3 example](https://github.com/cyu/rack-cors/tree/master/examples/rails3) and [rails 4 example](https://github.com/cyu/rack-cors/tree/master/examples/rails4) for more details.
50
+
51
+ We use `insert_before` to make sure `Rack::Cors` runs at the beginning of the stack to make sure it isn't interfered with by other middleware (see `Rack::Cache` note in **Common Gotchas** section). Check out the [rails 4 example](https://github.com/cyu/rack-cors/tree/master/examples/rails4) and [rails 3 example](https://github.com/cyu/rack-cors/tree/master/examples/rails3).
53
52
 
54
53
  See The [Rails Guide to Rack](http://guides.rubyonrails.org/rails_on_rack.html) for more details on rack middlewares or watch the [railscast](http://railscasts.com/episodes/151-rack-middleware).
55
54
 
@@ -68,16 +67,22 @@ use Rack::Cors do
68
67
 
69
68
  resource '/file/list_all/', :headers => 'x-domain-token'
70
69
  resource '/file/at/*',
71
- :methods => [:get, :post, :delete, :put, :patch, :options, :head],
72
- :headers => 'x-domain-token',
73
- :expose => ['Some-Custom-Response-Header'],
74
- :max_age => 600
70
+ methods: [:get, :post, :delete, :put, :patch, :options, :head],
71
+ headers: 'x-domain-token',
72
+ expose: ['Some-Custom-Response-Header'],
73
+ max_age: 600
75
74
  # headers to expose
76
75
  end
77
76
 
78
77
  allow do
79
78
  origins '*'
80
- resource '/public/*', :headers => :any, :methods => :get
79
+ resource '/public/*', headers: :any, methods: :get
80
+
81
+ # Only allow a request for a specific host
82
+ resource '/api/v1/*',
83
+ headers: :any,
84
+ methods: :get,
85
+ if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
81
86
  end
82
87
  end
83
88
  ```
@@ -98,13 +103,12 @@ Additionally, origins can be specified dynamically via a block of the following
98
103
  origins { |source, env| true || false }
99
104
  ```
100
105
 
101
- #### Resource
102
- A Resource path can be specified as exact string match (`/path/to/file.txt`) or with a '\*' wildcard (`/all/files/in/*`). A resource can take the following options:
106
+ A Resource path can be specified as exact string match (`/path/to/file.txt`) or with a '\*' wildcard (`/all/files/in/*`). To include all of a directory's files and the files in its subdirectories, use this form: `/assets/**/*`. A resource can take the following options:
103
107
 
104
108
  * **methods** (string or array or `:any`): The HTTP methods allowed for the resource.
105
109
  * **headers** (string or array or `:any`): The HTTP headers that will be allowed in the CORS resource request. Use `:any` to allow for any headers in the actual request.
106
110
  * **expose** (string or array): The HTTP headers in the resource response can be exposed to the client.
107
- * **credentials** (boolean): Sets the `Access-Control-Allow-Credentials` response header.
111
+ * **credentials** (boolean, default: `false`): Sets the `Access-Control-Allow-Credentials` response header. **Note:** If a wildcard (`*`) origin is specified, this option cannot be set to `true`. Read this [security article](http://web-in-security.blogspot.de/2017/07/cors-misconfigurations-on-large-scale.html) for more information.
108
112
  * **max_age** (number): Sets the `Access-Control-Max-Age` response header.
109
113
  * **if** (Proc): If the result of the proc is true, will process the request as a valid CORS request.
110
114
  * **vary** (string or array): A list of HTTP headers to add to the 'Vary' header.
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Cors
3
- VERSION = "0.4.1"
3
+ VERSION = "1.0.5"
4
4
  end
5
5
  end
data/lib/rack/cors.rb CHANGED
@@ -2,13 +2,28 @@ require 'logger'
2
2
 
3
3
  module Rack
4
4
  class Cors
5
- ENV_KEY = 'rack.cors'.freeze
5
+ HTTP_ORIGIN = 'HTTP_ORIGIN'.freeze
6
+ HTTP_X_ORIGIN = 'HTTP_X_ORIGIN'.freeze
6
7
 
7
- ORIGIN_HEADER_KEY = 'HTTP_ORIGIN'.freeze
8
- ORIGIN_X_HEADER_KEY = 'HTTP_X_ORIGIN'.freeze
9
- PATH_INFO_HEADER_KEY = 'PATH_INFO'.freeze
10
- VARY_HEADER_KEY = 'Vary'.freeze
11
- DEFAULT_VARY_HEADERS = ['Origin'].freeze
8
+ HTTP_ACCESS_CONTROL_REQUEST_METHOD = 'HTTP_ACCESS_CONTROL_REQUEST_METHOD'.freeze
9
+ HTTP_ACCESS_CONTROL_REQUEST_HEADERS = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'.freeze
10
+
11
+ PATH_INFO = 'PATH_INFO'.freeze
12
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
13
+
14
+ RACK_LOGGER = 'rack.logger'.freeze
15
+ RACK_CORS =
16
+ # retaining the old key for backwards compatibility
17
+ ENV_KEY = 'rack.cors'.freeze
18
+
19
+ OPTIONS = 'OPTIONS'.freeze
20
+ VARY = 'Vary'.freeze
21
+
22
+ DEFAULT_VARY_HEADERS = ['Origin'].freeze
23
+
24
+ # All CORS routes need to accept CORS simple headers at all times
25
+ # {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers}
26
+ CORS_SIMPLE_HEADERS = ['accept', 'accept-language', 'content-language', 'content-type'].freeze
12
27
 
13
28
  def initialize(app, opts={}, &block)
14
29
  @app = app
@@ -47,36 +62,38 @@ module Rack
47
62
  end
48
63
 
49
64
  def call(env)
50
- env[ORIGIN_HEADER_KEY] ||= env[ORIGIN_X_HEADER_KEY] if env[ORIGIN_X_HEADER_KEY]
65
+ env[HTTP_ORIGIN] ||= env[HTTP_X_ORIGIN] if env[HTTP_X_ORIGIN]
66
+
67
+ path = evaluate_path(env)
51
68
 
52
69
  add_headers = nil
53
- if env[ORIGIN_HEADER_KEY]
70
+ if env[HTTP_ORIGIN]
54
71
  debug(env) do
55
72
  [ 'Incoming Headers:',
56
- " Origin: #{env[ORIGIN_HEADER_KEY]}",
57
- " Access-Control-Request-Method: #{env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']}",
58
- " Access-Control-Request-Headers: #{env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
73
+ " Origin: #{env[HTTP_ORIGIN]}",
74
+ " Path-Info: #{path}",
75
+ " Access-Control-Request-Method: #{env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]}",
76
+ " Access-Control-Request-Headers: #{env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]}"
59
77
  ].join("\n")
60
78
  end
61
- if env['REQUEST_METHOD'] == 'OPTIONS' and env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
62
- if headers = process_preflight(env)
63
- debug(env) do
64
- "Preflight Headers:\n" +
65
- headers.collect{|kv| " #{kv.join(': ')}"}.join("\n")
66
- end
67
- return [200, headers, []]
79
+ if env[REQUEST_METHOD] == OPTIONS and env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
80
+ headers = process_preflight(env, path)
81
+ debug(env) do
82
+ "Preflight Headers:\n" +
83
+ headers.collect{|kv| " #{kv.join(': ')}"}.join("\n")
68
84
  end
85
+ return [200, headers, []]
69
86
  else
70
- add_headers = process_cors(env)
87
+ add_headers = process_cors(env, path)
71
88
  end
72
89
  else
73
90
  Result.miss(env, Result::MISS_NO_ORIGIN)
74
91
  end
75
92
 
76
93
  # This call must be done BEFORE calling the app because for some reason
77
- # env[PATH_INFO_HEADER_KEY] gets changed after that and it won't match.
78
- # (At least in rails 4.1.6)
79
- vary_resource = resource_for_path(env[PATH_INFO_HEADER_KEY])
94
+ # env[PATH_INFO] gets changed after that and it won't match. (At least
95
+ # in rails 4.1.6)
96
+ vary_resource = resource_for_path(path)
80
97
 
81
98
  status, headers, body = @app.call env
82
99
 
@@ -95,16 +112,16 @@ module Rack
95
112
  # response to be different depending on the Origin header value.
96
113
  # Better explained here: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
97
114
  if vary_resource
98
- vary = headers[VARY_HEADER_KEY]
115
+ vary = headers[VARY]
99
116
  cors_vary_headers = if vary_resource.vary_headers && vary_resource.vary_headers.any?
100
117
  vary_resource.vary_headers
101
118
  else
102
119
  DEFAULT_VARY_HEADERS
103
120
  end
104
- headers[VARY_HEADER_KEY] = ((vary ? vary.split(/,\s*/) : []) + cors_vary_headers).uniq.join(', ')
121
+ headers[VARY] = ((vary ? ([vary].flatten.map { |v| v.split(/,\s*/) }.flatten) : []) + cors_vary_headers).uniq.join(', ')
105
122
  end
106
123
 
107
- if debug? && result = env[ENV_KEY]
124
+ if debug? && result = env[RACK_CORS]
108
125
  result.append_header(headers)
109
126
  end
110
127
 
@@ -122,36 +139,41 @@ module Rack
122
139
  @logger_proc = nil
123
140
  logger_proc.call
124
141
 
125
- elsif defined?(Rails) && Rails.logger
142
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
126
143
  Rails.logger
127
144
 
128
- elsif env['rack.logger']
129
- env['rack.logger']
145
+ elsif env[RACK_LOGGER]
146
+ env[RACK_LOGGER]
130
147
 
131
148
  else
132
149
  ::Logger.new(STDOUT).tap { |logger| logger.level = ::Logger::Severity::DEBUG }
133
150
  end
134
151
  end
135
152
 
153
+ def evaluate_path(env)
154
+ path = env[PATH_INFO]
155
+ path = Rack::Utils.clean_path_info(Rack::Utils.unescape_path(path)) if path
156
+ path
157
+ end
158
+
136
159
  def all_resources
137
160
  @all_resources ||= []
138
161
  end
139
162
 
140
- def process_preflight(env)
141
- resource, error = match_resource(env)
142
- if resource
143
- Result.preflight_hit(env)
144
- preflight = resource.process_preflight(env)
145
- preflight
163
+ def process_preflight(env, path)
164
+ result = Result.preflight(env)
146
165
 
147
- else
148
- Result.preflight_miss(env, error)
149
- nil
166
+ resource, error = match_resource(path, env)
167
+ unless resource
168
+ result.miss(error)
169
+ return {}
150
170
  end
171
+
172
+ return resource.process_preflight(env, result)
151
173
  end
152
174
 
153
- def process_cors(env)
154
- resource, error = match_resource(env)
175
+ def process_cors(env, path)
176
+ resource, error = match_resource(path, env)
155
177
  if resource
156
178
  Result.hit(env)
157
179
  cors = resource.to_headers(env)
@@ -172,9 +194,8 @@ module Rack
172
194
  nil
173
195
  end
174
196
 
175
- def match_resource(env)
176
- path = env[PATH_INFO_HEADER_KEY]
177
- origin = env[ORIGIN_HEADER_KEY]
197
+ def match_resource(path, env)
198
+ origin = env[HTTP_ORIGIN]
178
199
 
179
200
  origin_matched = false
180
201
  all_resources.each do |r|
@@ -195,6 +216,10 @@ module Rack
195
216
  MISS_NO_ORIGIN = 'no-origin'.freeze
196
217
  MISS_NO_PATH = 'no-path'.freeze
197
218
 
219
+ MISS_NO_METHOD = 'no-method'.freeze
220
+ MISS_DENY_METHOD = 'deny-method'.freeze
221
+ MISS_DENY_HEADER = 'deny-header'.freeze
222
+
198
223
  attr_accessor :preflight, :hit, :miss_reason
199
224
 
200
225
  def hit?
@@ -205,11 +230,16 @@ module Rack
205
230
  !!preflight
206
231
  end
207
232
 
233
+ def miss(reason)
234
+ self.hit = false
235
+ self.miss_reason = reason
236
+ end
237
+
208
238
  def self.hit(env)
209
239
  r = Result.new
210
240
  r.preflight = false
211
241
  r.hit = true
212
- env[ENV_KEY] = r
242
+ env[RACK_CORS] = r
213
243
  end
214
244
 
215
245
  def self.miss(env, reason)
@@ -217,23 +247,15 @@ module Rack
217
247
  r.preflight = false
218
248
  r.hit = false
219
249
  r.miss_reason = reason
220
- env[ENV_KEY] = r
250
+ env[RACK_CORS] = r
221
251
  end
222
252
 
223
- def self.preflight_hit(env)
253
+ def self.preflight(env)
224
254
  r = Result.new
225
255
  r.preflight = true
226
- r.hit = true
227
- env[ENV_KEY] = r
256
+ env[RACK_CORS] = r
228
257
  end
229
258
 
230
- def self.preflight_miss(env, reason)
231
- r = Result.new
232
- r.preflight = true
233
- r.hit = false
234
- r.miss_reason = reason
235
- env[ENV_KEY] = r
236
- end
237
259
 
238
260
  def append_header(headers)
239
261
  headers[HEADER_KEY] = if hit?
@@ -248,6 +270,9 @@ module Rack
248
270
  end
249
271
 
250
272
  class Resources
273
+
274
+ attr_reader :resources
275
+
251
276
  def initialize
252
277
  @origins = []
253
278
  @resources = []
@@ -255,9 +280,10 @@ module Rack
255
280
  end
256
281
 
257
282
  def origins(*args, &blk)
258
- @origins = args.flatten.collect do |n|
283
+ @origins = args.flatten.reject{ |s| s == '' }.map do |n|
259
284
  case n
260
- when Regexp,
285
+ when Proc,
286
+ Regexp,
261
287
  /^https?:\/\//,
262
288
  'file://' then n
263
289
  when '*' then @public_resources = true; n
@@ -278,13 +304,11 @@ module Rack
278
304
  def allow_origin?(source,env = {})
279
305
  return true if public_resources?
280
306
 
281
- effective_source = (source == 'null' ? 'file://' : source)
282
-
283
307
  return !! @origins.detect do |origin|
284
308
  if origin.is_a?(Proc)
285
309
  origin.call(source,env)
286
310
  else
287
- origin === effective_source
311
+ origin === source
288
312
  end
289
313
  end
290
314
  end
@@ -300,12 +324,21 @@ module Rack
300
324
  end
301
325
 
302
326
  class Resource
327
+ class CorsMisconfigurationError < StandardError
328
+ def message
329
+ "Allowing credentials for wildcard origins is insecure."\
330
+ " Please specify more restrictive origins or set 'credentials' to false in your CORS configuration."
331
+ end
332
+ end
333
+
303
334
  attr_accessor :path, :methods, :headers, :expose, :max_age, :credentials, :pattern, :if_proc, :vary_headers
304
335
 
305
336
  def initialize(public_resource, path, opts={})
337
+ raise CorsMisconfigurationError if public_resource && opts[:credentials] == true
338
+
306
339
  self.path = path
307
- self.credentials = opts[:credentials].nil? ? true : opts[:credentials]
308
- self.max_age = opts[:max_age] || 1728000
340
+ self.credentials = public_resource ? false : (opts[:credentials] == true)
341
+ self.max_age = opts[:max_age] || 7200
309
342
  self.pattern = compile(path)
310
343
  self.if_proc = opts[:if]
311
344
  self.vary_headers = opts[:vary] && [opts[:vary]].flatten
@@ -323,7 +356,7 @@ module Rack
323
356
  else
324
357
  ensure_enum(opts[:methods]) || [:get]
325
358
  end.map{|e| e.to_s }
326
-
359
+
327
360
  self.expose = opts[:expose] ? [opts[:expose]].flatten : nil
328
361
  end
329
362
 
@@ -335,14 +368,29 @@ module Rack
335
368
  matches_path?(path) && (if_proc.nil? || if_proc.call(env))
336
369
  end
337
370
 
338
- def process_preflight(env)
339
- return nil if invalid_method_request?(env) || invalid_headers_request?(env)
340
- {'Content-Type' => 'text/plain'}.merge(to_preflight_headers(env))
371
+ def process_preflight(env, result)
372
+ headers = {}
373
+
374
+ request_method = env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
375
+ if request_method.nil?
376
+ result.miss(Result::MISS_NO_METHOD) and return headers
377
+ end
378
+ if !methods.include?(request_method.downcase)
379
+ result.miss(Result::MISS_DENY_METHOD) and return headers
380
+ end
381
+
382
+ request_headers = env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
383
+ if request_headers && !allow_headers?(request_headers)
384
+ result.miss(Result::MISS_DENY_HEADER) and return headers
385
+ end
386
+
387
+ result.hit = true
388
+ headers.merge(to_preflight_headers(env))
341
389
  end
342
390
 
343
391
  def to_headers(env)
344
392
  h = {
345
- 'Access-Control-Allow-Origin' => origin_for_response_header(env[ORIGIN_HEADER_KEY]),
393
+ 'Access-Control-Allow-Origin' => origin_for_response_header(env[HTTP_ORIGIN]),
346
394
  'Access-Control-Allow-Methods' => methods.collect{|m| m.to_s.upcase}.join(', '),
347
395
  'Access-Control-Expose-Headers' => expose.nil? ? '' : expose.join(', '),
348
396
  'Access-Control-Max-Age' => max_age.to_s }
@@ -356,33 +404,27 @@ module Rack
356
404
  end
357
405
 
358
406
  def origin_for_response_header(origin)
359
- return '*' if public_resource? && !credentials
407
+ return '*' if public_resource?
360
408
  origin
361
409
  end
362
410
 
363
411
  def to_preflight_headers(env)
364
412
  h = to_headers(env)
365
- if env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
366
- h.merge!('Access-Control-Allow-Headers' => env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])
413
+ if env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
414
+ h.merge!('Access-Control-Allow-Headers' => env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS])
367
415
  end
368
416
  h
369
417
  end
370
418
 
371
- def invalid_method_request?(env)
372
- request_method = env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
373
- request_method.nil? || !methods.include?(request_method.downcase)
374
- end
375
-
376
- def invalid_headers_request?(env)
377
- request_headers = env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
378
- request_headers && !allow_headers?(request_headers)
379
- end
380
-
381
419
  def allow_headers?(request_headers)
382
- return false if headers.nil?
383
- headers == :any || begin
384
- request_headers = request_headers.split(/,\s*/) if request_headers.kind_of?(String)
385
- request_headers.all?{|h| headers.include?(h.downcase)}
420
+ headers = self.headers || []
421
+ if headers == :any
422
+ return true
423
+ end
424
+ request_headers = request_headers.split(/,\s*/) if request_headers.kind_of?(String)
425
+ request_headers.all? do |header|
426
+ header = header.downcase
427
+ CORS_SIMPLE_HEADERS.include?(header) || headers.include?(header)
386
428
  end
387
429
  end
388
430
 
data/rack-cors.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Rack::Cors::VERSION
9
9
  spec.authors = ["Calvin Yu"]
10
10
  spec.email = ["me@sourcebender.com"]
11
- spec.description = %q{Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: https://github.com/cyu/rack-cors}
11
+ spec.description = %q{Middleware that will make Rack-based apps CORS compatible. Fork the project here: https://github.com/cyu/rack-cors}
12
12
  spec.summary = %q{Middleware for enabling Cross-Origin Resource Sharing in Rack apps}
13
13
  spec.homepage = "https://github.com/cyu/rack-cors"
14
14
  spec.license = "MIT"
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "minitest", ">= 5.3.0"
24
- spec.add_development_dependency "mocha", ">= 0.14.0"
25
- spec.add_development_dependency "rack-test", ">= 0"
21
+ spec.add_dependency "rack", ">= 1.6.0"
22
+ spec.add_development_dependency "bundler", ">= 1.16.0", '< 3'
23
+ spec.add_development_dependency "rake", "~> 12.3.0"
24
+ spec.add_development_dependency "minitest", "~> 5.11.0"
25
+ spec.add_development_dependency "mocha", "~> 1.6.0"
26
+ spec.add_development_dependency "rack-test", "~> 1.1.0"
26
27
  end
@@ -1,4 +1,4 @@
1
- CORS_SERVER = 'cors-server:3000'
1
+ CORS_SERVER = '127.0.0.1.xip.io:9292'
2
2
 
3
3
  describe 'CORS', ->
4
4
 
@@ -12,6 +12,11 @@ describe 'CORS', ->
12
12
  expect(data).to.eql('Hello world')
13
13
  done()
14
14
 
15
+ it 'should allow PATCH access to dynamic resource', (done) ->
16
+ $.ajax("http://#{CORS_SERVER}/", type: 'PATCH').done (data, textStatus, jqXHR) ->
17
+ expect(data).to.eql('Hello world')
18
+ done()
19
+
15
20
  it 'should allow HEAD access to dynamic resource', (done) ->
16
21
  $.ajax("http://#{CORS_SERVER}/", type: 'HEAD').done (data, textStatus, jqXHR) ->
17
22
  expect(jqXHR.status).to.eql(200)