rack-cors 0.4.1 → 2.0.1

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

Potentially problematic release.


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

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: f47b5b2ba34721795ddb1c65e70e989134655e7f47116dd977edee702a79f41f
4
+ data.tar.gz: 7c03dc701b00b7418ab4d733872fccc3522392f0f85014b8ca25045767d866a9
5
5
  SHA512:
6
- metadata.gz: 633c772b16e08fad8fb93a7d1bf5d62a398abb534a27ce0d44df843242c5e1077112e98b1eb7e2d3849da952fec1754cd2e8451195c1a2c86ac567d69652b859
7
- data.tar.gz: 8e0265d2d82db72ac68871dcf0eaf974809638d5a52514f99392b731fe09050ab07968c5e538a35d7bba2b7892cc62b3d6b7b401fe8a05c4648f5ad57a092abf
6
+ metadata.gz: d5a94b8f282fd5367e125f4b49e18ce5fb1c07581d89b2d50dc8cf4eb70e8404d97c78a6ccaf90f1e41f0f220bb09ff9dd07a4677559935cc35256674e6c512d
7
+ data.tar.gz: bee187e2dc53281d8b454df32b6a8fd50e05b66de4f25649d74f176abf29d52eb4cee5a2b2e602e087c5f5805b6dd55ab86a9e0692d3d82c5538e0c30e3c020b
@@ -0,0 +1,39 @@
1
+ name: ci
2
+
3
+ on:
4
+ - push
5
+ - pull_request
6
+
7
+ jobs:
8
+ test:
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby:
13
+ - "2.3"
14
+ - "2.4"
15
+ - "2.5"
16
+ - "2.6"
17
+ - "2.7"
18
+ - "3.0"
19
+ - "3.1"
20
+ - "3.2"
21
+ - truffleruby-head
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v3
25
+ - uses: ruby/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby }}
28
+ bundler-cache: true
29
+ - run: bundle exec rake test
30
+
31
+ rubocop:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v3
35
+ - uses: ruby/setup-ruby@v1
36
+ with:
37
+ ruby-version: 3.2.1
38
+ bundler-cache: true
39
+ - run: bundle exec rubocop
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
1
+ ---
2
+ AllCops:
3
+ Exclude:
4
+ - "examples/**/*"
5
+ - "vendor/**/*"
6
+
7
+ # Disables
8
+ Layout/LineLength:
9
+ Enabled: false
10
+ Style/Documentation:
11
+ Enabled: false
12
+ Metrics/ClassLength:
13
+ Enabled: false
14
+ Metrics/MethodLength:
15
+ Enabled: false
16
+ Metrics/BlockLength:
17
+ Enabled: false
18
+ Style/HashEachMethods:
19
+ Enabled: false
20
+ Style/HashTransformKeys:
21
+ Enabled: false
22
+ Style/HashTransformValues:
23
+ Enabled: false
24
+ Style/DoubleNegation:
25
+ Enabled: false
26
+ Metrics/CyclomaticComplexity:
27
+ Enabled: false
28
+ Metrics/PerceivedComplexity:
29
+ Enabled: false
30
+ Metrics/AbcSize:
31
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,99 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 2.0.1 - 2023-02-17
5
+ ### Changed
6
+ - Use Rack::Utils::HeaderHash when Rack 2.x is detected
7
+
8
+ ## 2.0.0 - 2023-02-14
9
+ ### Changed
10
+ - Refactored codebase
11
+ - Support declaring custom protocols in origin
12
+ - Lowercased header names as defined by Rack spec
13
+ - Fix issue with duplicate headers because of header name case
14
+
15
+ ## 1.1.1 - 2019-12-29
16
+ ### Changed
17
+ - Allow /<resource>/* to match /<resource>/ and /<resource> paths
18
+
19
+ ## 1.1.0 - 2019-11-19
20
+ ### Changed
21
+ - Use Rack::Utils.escape_path instead of Rack::Utils.escape
22
+ - Require Rack 2.0 for escape_path method
23
+ - Don't try to clean path if invalid.
24
+ - Return 400 (Bad Request) on preflights with invalid path
25
+
26
+ ## 1.0.6 - 2019-11-14
27
+ ### Changed
28
+ - Use Rack::Utils.escape to make compat with Rack 1.6.0
29
+
30
+ ## 1.0.5 - 2019-11-14
31
+ ### Changed
32
+ - Update Gem spec to require rack >= 1.6.0
33
+
34
+ ## 1.0.4 - 2019-11-13
35
+ ### Security
36
+ - Escape and resolve path before evaluating resource rules (thanks to Colby Morgan)
37
+
38
+ ## 1.0.3 - 2019-03-24
39
+ ### Changed
40
+ - Don't send 'Content-Type' header with pre-flight requests
41
+ - Allow ruby array for vary header config
42
+
43
+ ## 1.0.2 - 2017-10-22
44
+ ### Fixed
45
+ - Automatically allow simple headers when headers are set
46
+
47
+ ## 1.0.1 - 2017-07-18
48
+ ### Fixed
49
+ - Allow lambda origin configuration
50
+
51
+ ## 1.0.0 - 2017-07-15
52
+ ### Security
53
+ - Don't implicitly accept 'null' origins when 'file://' is specified
54
+ (https://github.com/cyu/rack-cors/pull/134)
55
+ - Ignore '' origins (https://github.com/cyu/rack-cors/issues/139)
56
+ - Default credentials option on resources to false
57
+ (https://github.com/cyu/rack-cors/issues/95)
58
+ - Don't allow credentials option to be true if '*' is specified is origin
59
+ (https://github.com/cyu/rack-cors/pull/142)
60
+ - Don't reflect Origin header when '*' is specified as origin
61
+ (https://github.com/cyu/rack-cors/pull/142)
62
+
63
+ ### Fixed
64
+ - Don't respond immediately on non-matching preflight requests instead of
65
+ sending them through the app (https://github.com/cyu/rack-cors/pull/106)
66
+
67
+ ## 0.4.1 - 2017-02-01
68
+ ### Fixed
69
+ - Return miss result in X-Rack-CORS instead of incorrectly returning
70
+ preflight-hit
71
+
72
+ ## 0.4.0 - 2015-04-15
73
+ ### Changed
74
+ - Don't set HTTP_ORIGIN with HTTP_X_ORIGIN if nil
75
+
76
+ ### Added
77
+ - Calculate vary headers for non-CORS resources
78
+ - Support custom vary headers for resource
79
+ - Support :if option for resource
80
+ - Support :any as a possible value for :methods option
81
+
82
+ ### Fixed
83
+ - Don't symbolize incoming HTTP request methods
84
+
85
+ ## 0.3.1 - 2014-12-27
86
+ ### Changed
87
+ - Changed the env key to rack.cors to avoid Rack::Lint warnings
88
+
89
+ ## 0.3.0 - 2014-10-19
90
+ ### Added
91
+ - Added support for defining a logger with a Proc
92
+ - Return a X-Rack-CORS header when in debug mode detailing how Rack::Cors
93
+ processed a request
94
+ - Added support for non HTTP/HTTPS origins when just a domain is specified
95
+
96
+ ### Changed
97
+ - Changed the log level of the fallback logger to DEBUG
98
+ - Print warning when attempting to use :any as an allowed method
99
+ - Treat incoming `Origin: null` headers as file://
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in rack-cors.gemspec
4
6
  gemspec
5
7
 
6
- gem 'pry-byebug'
8
+ gem 'pry-byebug', '~> 3.6.0'
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Rack CORS Middleware [![Build Status](https://travis-ci.org/cyu/rack-cors.svg?branch=master)](https://travis-ci.org/cyu/rack-cors)
1
+ # Rack CORS Middleware [![Build Status](https://github.com/cyu/rack-cors/actions/workflows/ci.yaml/badge.svg)](https://github.com/cyu/rack-cors/actions)
2
2
 
3
3
  `Rack::Cors` provides support for Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.
4
4
 
5
- The [CORS spec](http://www.w3.org/TR/cors/) allows web applications to make cross domain AJAX calls without using workarounds such as JSONP. See [Cross-domain Ajax with Cross-Origin Resource Sharing](http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/)
5
+ The [CORS spec](http://www.w3.org/TR/cors/) allows web applications to make cross domain AJAX calls without using workarounds such as JSONP. See [further explanations on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
6
6
 
7
7
  ## Installation
8
8
 
@@ -13,49 +13,43 @@ 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
 
20
20
  ## Configuration
21
21
 
22
22
  ### Rails Configuration
23
- Put something like the code below in `config/application.rb` of your Rails application. For example, this will allow GET, POST or OPTIONS requests from any origin on any resource.
23
+ For Rails, you'll need to add this middleware on application startup. A practical way to do this is with an initializer file. For example, the following will allow GET, POST, PATCH, or PUT requests from any origin on any resource:
24
24
 
25
25
  ```ruby
26
- module YourApp
27
- class Application < Rails::Application
28
-
29
- # ...
30
-
31
- # Rails 3/4
32
-
33
- config.middleware.insert_before 0, "Rack::Cors" do
34
- allow do
35
- origins '*'
36
- resource '*', :headers => :any, :methods => [:get, :post, :options]
37
- end
38
- end
39
-
40
- # Rails 5
41
-
42
- config.middleware.insert_before 0, Rack::Cors do
43
- allow do
44
- origins '*'
45
- resource '*', :headers => :any, :methods => [:get, :post, :options]
46
- end
47
- end
26
+ # config/initializers/cors.rb
48
27
 
28
+ Rails.application.config.middleware.insert_before 0, Rack::Cors do
29
+ allow do
30
+ origins '*'
31
+ resource '*', headers: :any, methods: [:get, :post, :patch, :put]
49
32
  end
50
33
  end
51
34
  ```
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.
35
+
36
+ NOTE: If you create application with `--api` option, configuration automatically generate in `config/initializers/cors.rb`.
37
+
38
+ 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). Basic setup examples for Rails 5 & Rails 6 can be found in the examples/ directory.
53
39
 
54
40
  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
41
 
42
+ *Note about Rails 6*: Rails 6 has support for blocking requests from unknown hosts, so origin domains will need to be added there as well.
43
+
44
+ ```ruby
45
+ Rails.application.config.hosts << "product.com"
46
+ ```
47
+
48
+ Read more about it here in the [Rails Guides](https://guides.rubyonrails.org/configuring.html#configuring-middleware)
49
+
56
50
  ### Rack Configuration
57
51
 
58
- NOTE: If you're running Rails, updating in `config/application.rb` should be enough. There is no need to update `config.ru` as well.
52
+ NOTE: If you're running Rails, adding `config/initializers/cors.rb` should be enough. There is no need to update `config.ru` as well.
59
53
 
60
54
  In `config.ru`, configure `Rack::Cors` by passing a block to the `use` command:
61
55
 
@@ -68,16 +62,22 @@ use Rack::Cors do
68
62
 
69
63
  resource '/file/list_all/', :headers => 'x-domain-token'
70
64
  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
65
+ methods: [:get, :post, :delete, :put, :patch, :options, :head],
66
+ headers: 'x-domain-token',
67
+ expose: ['Some-Custom-Response-Header'],
68
+ max_age: 600
75
69
  # headers to expose
76
70
  end
77
71
 
78
72
  allow do
79
73
  origins '*'
80
- resource '/public/*', :headers => :any, :methods => :get
74
+ resource '/public/*', headers: :any, methods: :get
75
+
76
+ # Only allow a request for a specific host
77
+ resource '/api/v1/*',
78
+ headers: :any,
79
+ methods: :get,
80
+ if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
81
81
  end
82
82
  end
83
83
  ```
@@ -91,20 +91,19 @@ end
91
91
  #### Origin
92
92
  Origins can be specified as a string, a regular expression, or as '\*' to allow all origins.
93
93
 
94
- **\*SECURITY NOTE:** Be careful when using regular expressions to not accidentally be too inclusive. For example, the expression `/https:\/\/example\.com/` will match the domain *example.com.randomdomainname.co.uk*. It is recommended that any regular expression be enclosed with start & end string anchors (`\A\z`).
94
+ **\*SECURITY NOTE:** Be careful when using regular expressions to not accidentally be too inclusive. For example, the expression `/https:\/\/example\.com/` will match the domain *example.com.randomdomainname.co.uk*. It is recommended that any regular expression be enclosed with start & end string anchors, like `\Ahttps:\/\/example\.com\z`.
95
95
 
96
96
  Additionally, origins can be specified dynamically via a block of the following form:
97
97
  ```ruby
98
98
  origins { |source, env| true || false }
99
99
  ```
100
100
 
101
- #### Resource
102
101
  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:
103
102
 
104
103
  * **methods** (string or array or `:any`): The HTTP methods allowed for the resource.
105
104
  * **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
105
  * **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.
106
+ * **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
107
  * **max_age** (number): Sets the `Access-Control-Max-Age` response header.
109
108
  * **if** (Proc): If the result of the proc is true, will process the request as a valid CORS request.
110
109
  * **vary** (string or array): A list of HTTP headers to add to the 'Vary' header.
@@ -112,24 +111,50 @@ A Resource path can be specified as exact string match (`/path/to/file.txt`) or
112
111
 
113
112
  ## Common Gotchas
114
113
 
115
- Incorrect positioning of `Rack::Cors` in the middleware stack can produce unexpected results. The Rails example above will put it above all middleware which should cover most issues.
114
+ ### Origin Matching
115
+
116
+ When specifying an origin, make sure that it does not have a trailing slash.
116
117
 
117
- Here are some common cases:
118
+ ### Testing Postman and/or cURL
118
119
 
119
- * **Serving static files.** Insert this middleware before `ActionDispatch::Static` so that static files are served with the proper CORS headers (see note below for a caveat). **NOTE:** that this might not work in production environments as static files are usually served from the web server (Nginx, Apache) and not the Rails container.
120
+ * Make sure you're passing in an `Origin:` header. That header is required to trigger a CORS response. Here's [a good SO post](https://stackoverflow.com/questions/12173990/how-can-you-debug-a-cors-request-with-curl) about using cURL for testing CORS.
121
+ * Make sure your origin does not have a trailing slash.
120
122
 
121
- * **Caching in the middleware.** Insert this middleware before `Rack::Cache` so that the proper CORS headers are written and not cached ones.
123
+ ### Positioning in the Middleware Stack
122
124
 
123
- * **Authentication via Warden** Warden will return immediately if a resource that requires authentication is accessed without authentication. If `Warden::Manager`is in the stack before `Rack::Cors`, it will return without the correct CORS headers being applied, resulting in a failed CORS request. Be sure to insert this middleware before 'Warden::Manager`.
125
+ Positioning of `Rack::Cors` in the middleware stack is very important. In the Rails example above we put it above all other middleware which, in our experience, provides the most consistent results.
124
126
 
125
- To determine where to put the CORS middleware in the Rack stack, run the following command:
127
+ Here are some scenarios where incorrect positioning have created issues:
128
+
129
+ * **Serving static files.** Insert before `ActionDispatch::Static` so that static files are served with the proper CORS headers. **NOTE:** this might not work in production as static files are usually served from the web server (Nginx, Apache) and not the Rails container.
130
+
131
+ * **Caching in the middleware.** Insert before `Rack::Cache` so that the proper CORS headers are written and not cached ones.
132
+
133
+ * **Authentication via Warden** Warden will return immediately if a resource that requires authentication is accessed without authentication. If `Warden::Manager`is in the stack before `Rack::Cors`, it will return without the correct CORS headers being applied, resulting in a failed CORS request.
134
+
135
+ You can run the following command to see what the middleware stack looks like:
126
136
 
127
137
  ```bash
128
138
  bundle exec rake middleware
129
139
  ```
130
140
 
131
- In many cases, the Rack stack will be different running in production environments. For example, the `ActionDispatch::Static` middleware will not be part of the stack if `config.serve_static_assets = false`. You can run the following command to see what your middleware stack looks like in production:
141
+ Note that the middleware stack is different in production. For example, the `ActionDispatch::Static` middleware will not be part of the stack if `config.serve_static_assets = false`. You can run this to see what your middleware stack looks like in production:
132
142
 
133
143
  ```bash
134
144
  RAILS_ENV=production bundle exec rake middleware
135
145
  ```
146
+
147
+ ### Serving static files
148
+
149
+ If you trying to serve CORS headers on static assets (like CSS, JS, Font files), keep in mind that static files are usually served directly from web servers and never runs through the Rails container (including the middleware stack where `Rack::Cors` resides).
150
+
151
+ In Heroku, you can serve static assets through the Rails container by setting `config.serve_static_assets = true` in `production.rb`.
152
+
153
+ ### Custom Protocols (chrome-extension://, ionic://, etc.)
154
+
155
+ Prior to 2.0.0, `http://`, `https://`, and `file://` are the only protocols supported in the `origins` list. If you wish to specify an origin that
156
+ has a custom protocol (`chrome-extension://`, `ionic://`, etc.) simply exclude the protocol. [See issue.](https://github.com/cyu/rack-cors/issues/100)
157
+
158
+ For example, instead of specifying `chrome-extension://aomjjhallfgjeglblehebfpbcfeobpga` specify `aomjjhallfgjeglblehebfpbcfeobpga` in `origins`.
159
+
160
+ As of 2.0.0 (currently in RC1), you can specify origins with a custom protocol.
data/Rakefile CHANGED
@@ -1,4 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
2
4
 
3
5
  require 'rake/testtask'
4
6
  Rake::TestTask.new(:test) do |test|
@@ -7,15 +9,14 @@ Rake::TestTask.new(:test) do |test|
7
9
  test.verbose = true
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
11
13
 
12
14
  require 'rdoc/task'
13
15
  Rake::RDocTask.new do |rdoc|
14
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
16
+ version = File.exist?('VERSION') ? File.read('VERSION') : ''
15
17
 
16
18
  rdoc.rdoc_dir = 'rdoc'
17
19
  rdoc.title = "rack-cors #{version}"
18
20
  rdoc.rdoc_files.include('README*')
19
21
  rdoc.rdoc_files.include('lib/**/*.rb')
20
22
  end
21
-
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class Cors
5
+ class Resource
6
+ # All CORS routes need to accept CORS simple headers at all times
7
+ # {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers}
8
+ CORS_SIMPLE_HEADERS = %w[accept accept-language content-language content-type].freeze
9
+
10
+ attr_accessor :path, :methods, :headers, :expose, :max_age, :credentials, :pattern, :if_proc, :vary_headers
11
+
12
+ def initialize(public_resource, path, opts = {})
13
+ raise CorsMisconfigurationError if public_resource && opts[:credentials] == true
14
+
15
+ self.path = path
16
+ self.credentials = public_resource ? false : (opts[:credentials] == true)
17
+ self.max_age = opts[:max_age] || 7200
18
+ self.pattern = compile(path)
19
+ self.if_proc = opts[:if]
20
+ self.vary_headers = opts[:vary] && [opts[:vary]].flatten
21
+ @public_resource = public_resource
22
+
23
+ self.headers = case opts[:headers]
24
+ when :any then :any
25
+ when nil then nil
26
+ else
27
+ [opts[:headers]].flatten.collect(&:downcase)
28
+ end
29
+
30
+ self.methods = case opts[:methods]
31
+ when :any then %i[get head post put patch delete options]
32
+ else
33
+ ensure_enum(opts[:methods]) || [:get]
34
+ end.map(&:to_s)
35
+
36
+ self.expose = opts[:expose] ? [opts[:expose]].flatten : nil
37
+ end
38
+
39
+ def matches_path?(path)
40
+ pattern =~ path
41
+ end
42
+
43
+ def match?(path, env)
44
+ matches_path?(path) && (if_proc.nil? || if_proc.call(env))
45
+ end
46
+
47
+ def process_preflight(env, result)
48
+ headers = {}
49
+
50
+ request_method = env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_METHOD]
51
+ result.miss(Result::MISS_NO_METHOD) && (return headers) if request_method.nil?
52
+ result.miss(Result::MISS_DENY_METHOD) && (return headers) unless methods.include?(request_method.downcase)
53
+
54
+ request_headers = env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
55
+ result.miss(Result::MISS_DENY_HEADER) && (return headers) if request_headers && !allow_headers?(request_headers)
56
+
57
+ result.hit = true
58
+ headers.merge(to_preflight_headers(env))
59
+ end
60
+
61
+ def to_headers(env)
62
+ h = {
63
+ 'access-control-allow-origin' => origin_for_response_header(env[Rack::Cors::HTTP_ORIGIN]),
64
+ 'access-control-allow-methods' => methods.collect { |m| m.to_s.upcase }.join(', '),
65
+ 'access-control-expose-headers' => expose.nil? ? '' : expose.join(', '),
66
+ 'access-control-max-age' => max_age.to_s
67
+ }
68
+ h['access-control-allow-credentials'] = 'true' if credentials
69
+ header_proc.call(h)
70
+ end
71
+
72
+ protected
73
+
74
+ def public_resource?
75
+ @public_resource
76
+ end
77
+
78
+ def origin_for_response_header(origin)
79
+ return '*' if public_resource?
80
+
81
+ origin
82
+ end
83
+
84
+ def to_preflight_headers(env)
85
+ h = to_headers(env)
86
+ h.merge!('access-control-allow-headers' => env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]) if env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
87
+ h
88
+ end
89
+
90
+ def allow_headers?(request_headers)
91
+ headers = self.headers || []
92
+ return true if headers == :any
93
+
94
+ request_headers = request_headers.split(/,\s*/) if request_headers.is_a?(String)
95
+ request_headers.all? do |header|
96
+ header = header.downcase
97
+ CORS_SIMPLE_HEADERS.include?(header) || headers.include?(header)
98
+ end
99
+ end
100
+
101
+ def ensure_enum(var)
102
+ return nil if var.nil?
103
+
104
+ [var].flatten
105
+ end
106
+
107
+ def compile(path)
108
+ if path.respond_to? :to_str
109
+ special_chars = %w[. + ( )]
110
+ pattern =
111
+ path.to_str.gsub(%r{((:\w+)|/\*|[\*#{special_chars.join}])}) do |match|
112
+ case match
113
+ when '/*'
114
+ '\\/?(.*?)'
115
+ when '*'
116
+ '(.*?)'
117
+ when *special_chars
118
+ Regexp.escape(match)
119
+ else
120
+ '([^/?&#]+)'
121
+ end
122
+ end
123
+ /^#{pattern}$/
124
+ elsif path.respond_to? :match
125
+ path
126
+ else
127
+ raise TypeError, path
128
+ end
129
+ end
130
+
131
+ def header_proc
132
+ @header_proc ||= begin
133
+ if defined?(Rack::Headers)
134
+ ->(h) { h }
135
+ else
136
+ ->(h) { Rack::Utils::HeaderHash.new(h) }
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class Cors
5
+ class Resource
6
+ class CorsMisconfigurationError < StandardError
7
+ def message
8
+ 'Allowing credentials for wildcard origins is insecure.' \
9
+ " Please specify more restrictive origins or set 'credentials' to false in your CORS configuration."
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resources/cors_misconfiguration_error'
4
+
5
+ module Rack
6
+ class Cors
7
+ class Resources
8
+ attr_reader :resources
9
+
10
+ def initialize
11
+ @origins = []
12
+ @resources = []
13
+ @public_resources = false
14
+ end
15
+
16
+ def origins(*args, &blk)
17
+ @origins = args.flatten.reject { |s| s == '' }.map do |n|
18
+ case n
19
+ when Proc, Regexp, %r{^[a-z][a-z0-9.+-]*://}
20
+ n
21
+ when '*'
22
+ @public_resources = true
23
+ n
24
+ else
25
+ Regexp.compile("^[a-z][a-z0-9.+-]*:\\\/\\\/#{Regexp.quote(n)}$")
26
+ end
27
+ end.flatten
28
+ @origins.push(blk) if blk
29
+ end
30
+
31
+ def resource(path, opts = {})
32
+ @resources << Resource.new(public_resources?, path, opts)
33
+ end
34
+
35
+ def public_resources?
36
+ @public_resources
37
+ end
38
+
39
+ def allow_origin?(source, env = {})
40
+ return true if public_resources?
41
+
42
+ !!@origins.detect do |origin|
43
+ if origin.is_a?(Proc)
44
+ origin.call(source, env)
45
+ elsif origin.is_a?(Regexp)
46
+ source =~ origin
47
+ else
48
+ source == origin
49
+ end
50
+ end
51
+ end
52
+
53
+ def match_resource(path, env)
54
+ @resources.detect { |r| r.match?(path, env) }
55
+ end
56
+
57
+ def resource_for_path(path)
58
+ @resources.detect { |r| r.matches_path?(path) }
59
+ end
60
+ end
61
+ end
62
+ end