rack-cors 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +10 -10
- data/lib/rack/cors/resource.rb +1 -1
- data/lib/rack/cors/version.rb +1 -1
- metadata +34 -47
- data/.github/workflows/ci.yaml +0 -39
- data/.rubocop.yml +0 -31
- data/Gemfile +0 -8
- data/Rakefile +0 -22
- data/rack-cors.gemspec +0 -30
- data/test/.rubocop.yml +0 -8
- data/test/cors/expect.js +0 -1286
- data/test/cors/mocha.css +0 -250
- data/test/cors/mocha.js +0 -5373
- data/test/cors/runner.html +0 -20
- data/test/cors/test.cors.coffee +0 -49
- data/test/cors/test.cors.js +0 -79
- data/test/unit/cors_test.rb +0 -540
- data/test/unit/dsl_test.rb +0 -70
- data/test/unit/insecure.ru +0 -10
- data/test/unit/non_http.ru +0 -10
- data/test/unit/test.ru +0 -66
data/test/cors/runner.html
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
<html>
|
2
|
-
<head>
|
3
|
-
<meta charset="utf-8">
|
4
|
-
<title>Mocha Tests</title>
|
5
|
-
<link rel="stylesheet" href="mocha.css" />
|
6
|
-
</head>
|
7
|
-
<body>
|
8
|
-
<div id="mocha"></div>
|
9
|
-
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
|
10
|
-
<script src="expect.js"></script>
|
11
|
-
<script src="mocha.js"></script>
|
12
|
-
<script>mocha.setup('bdd')</script>
|
13
|
-
<script src="test.cors.js"></script>
|
14
|
-
<script>
|
15
|
-
mocha.checkLeaks();
|
16
|
-
mocha.globals(['jQuery']);
|
17
|
-
mocha.run();
|
18
|
-
</script>
|
19
|
-
</body>
|
20
|
-
</html>
|
data/test/cors/test.cors.coffee
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
CORS_SERVER = '127.0.0.1.xip.io:3000'
|
2
|
-
|
3
|
-
mocha.setup({ignoreLeaks: true});
|
4
|
-
|
5
|
-
describe 'CORS', ->
|
6
|
-
|
7
|
-
it 'should allow access to dynamic resource', (done) ->
|
8
|
-
$.get "http://#{CORS_SERVER}/", (data, status, xhr) ->
|
9
|
-
expect(data).to.eql('Hello world')
|
10
|
-
done()
|
11
|
-
|
12
|
-
it 'should allow PUT access to dynamic resource', (done) ->
|
13
|
-
$.ajax("http://#{CORS_SERVER}/", type: 'PUT').done (data, textStatus, jqXHR) ->
|
14
|
-
expect(data).to.eql('Hello world')
|
15
|
-
done()
|
16
|
-
|
17
|
-
it 'should allow PATCH access to dynamic resource', (done) ->
|
18
|
-
$.ajax("http://#{CORS_SERVER}/", type: 'PATCH').done (data, textStatus, jqXHR) ->
|
19
|
-
expect(data).to.eql('Hello world')
|
20
|
-
done()
|
21
|
-
|
22
|
-
it 'should allow HEAD access to dynamic resource', (done) ->
|
23
|
-
$.ajax("http://#{CORS_SERVER}/", type: 'HEAD').done (data, textStatus, jqXHR) ->
|
24
|
-
expect(jqXHR.status).to.eql(200)
|
25
|
-
done()
|
26
|
-
|
27
|
-
it 'should allow DELETE access to dynamic resource', (done) ->
|
28
|
-
$.ajax("http://#{CORS_SERVER}/", type: 'DELETE').done (data, textStatus, jqXHR) ->
|
29
|
-
expect(data).to.eql('Hello world')
|
30
|
-
done()
|
31
|
-
|
32
|
-
it 'should allow OPTIONS access to dynamic resource', (done) ->
|
33
|
-
$.ajax("http://#{CORS_SERVER}/", type: 'OPTIONS').done (data, textStatus, jqXHR) ->
|
34
|
-
expect(jqXHR.status).to.eql(200)
|
35
|
-
done()
|
36
|
-
|
37
|
-
it 'should allow access to static resource', (done) ->
|
38
|
-
$.get "http://#{CORS_SERVER}/static.txt", (data, status, xhr) ->
|
39
|
-
expect($.trim(data)).to.eql("Hello world")
|
40
|
-
done()
|
41
|
-
|
42
|
-
it 'should allow post resource', (done) ->
|
43
|
-
$.ajax
|
44
|
-
type: 'POST'
|
45
|
-
url: "http://#{CORS_SERVER}/cors"
|
46
|
-
beforeSend: (xhr) -> xhr.setRequestHeader('X-Requested-With', 'XMLHTTPRequest')
|
47
|
-
success:(data, status, xhr) ->
|
48
|
-
expect($.trim(data)).to.eql("OK!")
|
49
|
-
done()
|
data/test/cors/test.cors.js
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
// Generated by CoffeeScript 2.3.1
|
2
|
-
(function() {
|
3
|
-
var CORS_SERVER;
|
4
|
-
|
5
|
-
CORS_SERVER = '127.0.0.1.xip.io:3000';
|
6
|
-
|
7
|
-
mocha.setup({
|
8
|
-
ignoreLeaks: true
|
9
|
-
});
|
10
|
-
|
11
|
-
describe('CORS', function() {
|
12
|
-
it('should allow access to dynamic resource', function(done) {
|
13
|
-
return $.get(`http://${CORS_SERVER}/`, function(data, status, xhr) {
|
14
|
-
expect(data).to.eql('Hello world');
|
15
|
-
return done();
|
16
|
-
});
|
17
|
-
});
|
18
|
-
it('should allow PUT access to dynamic resource', function(done) {
|
19
|
-
return $.ajax(`http://${CORS_SERVER}/`, {
|
20
|
-
type: 'PUT'
|
21
|
-
}).done(function(data, textStatus, jqXHR) {
|
22
|
-
expect(data).to.eql('Hello world');
|
23
|
-
return done();
|
24
|
-
});
|
25
|
-
});
|
26
|
-
it('should allow PATCH access to dynamic resource', function(done) {
|
27
|
-
return $.ajax(`http://${CORS_SERVER}/`, {
|
28
|
-
type: 'PATCH'
|
29
|
-
}).done(function(data, textStatus, jqXHR) {
|
30
|
-
expect(data).to.eql('Hello world');
|
31
|
-
return done();
|
32
|
-
});
|
33
|
-
});
|
34
|
-
it('should allow HEAD access to dynamic resource', function(done) {
|
35
|
-
return $.ajax(`http://${CORS_SERVER}/`, {
|
36
|
-
type: 'HEAD'
|
37
|
-
}).done(function(data, textStatus, jqXHR) {
|
38
|
-
expect(jqXHR.status).to.eql(200);
|
39
|
-
return done();
|
40
|
-
});
|
41
|
-
});
|
42
|
-
it('should allow DELETE access to dynamic resource', function(done) {
|
43
|
-
return $.ajax(`http://${CORS_SERVER}/`, {
|
44
|
-
type: 'DELETE'
|
45
|
-
}).done(function(data, textStatus, jqXHR) {
|
46
|
-
expect(data).to.eql('Hello world');
|
47
|
-
return done();
|
48
|
-
});
|
49
|
-
});
|
50
|
-
it('should allow OPTIONS access to dynamic resource', function(done) {
|
51
|
-
return $.ajax(`http://${CORS_SERVER}/`, {
|
52
|
-
type: 'OPTIONS'
|
53
|
-
}).done(function(data, textStatus, jqXHR) {
|
54
|
-
expect(jqXHR.status).to.eql(200);
|
55
|
-
return done();
|
56
|
-
});
|
57
|
-
});
|
58
|
-
it('should allow access to static resource', function(done) {
|
59
|
-
return $.get(`http://${CORS_SERVER}/static.txt`, function(data, status, xhr) {
|
60
|
-
expect($.trim(data)).to.eql("Hello world");
|
61
|
-
return done();
|
62
|
-
});
|
63
|
-
});
|
64
|
-
return it('should allow post resource', function(done) {
|
65
|
-
return $.ajax({
|
66
|
-
type: 'POST',
|
67
|
-
url: `http://${CORS_SERVER}/cors`,
|
68
|
-
beforeSend: function(xhr) {
|
69
|
-
return xhr.setRequestHeader('X-Requested-With', 'XMLHTTPRequest');
|
70
|
-
},
|
71
|
-
success: function(data, status, xhr) {
|
72
|
-
expect($.trim(data)).to.eql("OK!");
|
73
|
-
return done();
|
74
|
-
}
|
75
|
-
});
|
76
|
-
});
|
77
|
-
});
|
78
|
-
|
79
|
-
}).call(this);
|
data/test/unit/cors_test.rb
DELETED
@@ -1,540 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'minitest/autorun'
|
4
|
-
require 'rack/test'
|
5
|
-
require 'mocha/setup'
|
6
|
-
require 'rack/cors'
|
7
|
-
require 'ostruct'
|
8
|
-
|
9
|
-
Rack::Test::Session.class_eval do
|
10
|
-
unless defined? :options
|
11
|
-
def options(uri, params = {}, env = {}, &block)
|
12
|
-
env = env_for(uri, env.merge(method: 'OPTIONS', params: params))
|
13
|
-
process_request(uri, env, &block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
Rack::Test::Methods.class_eval do
|
19
|
-
def_delegator :current_session, :options
|
20
|
-
end
|
21
|
-
|
22
|
-
module MiniTest::Assertions
|
23
|
-
def assert_cors_success(response)
|
24
|
-
assert !response.headers['Access-Control-Allow-Origin'].nil?, 'Expected a successful CORS response'
|
25
|
-
end
|
26
|
-
|
27
|
-
def assert_not_cors_success(response)
|
28
|
-
assert response.headers['Access-Control-Allow-Origin'].nil?, 'Expected a failed CORS response'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class CaptureResult
|
33
|
-
def initialize(app, options = {})
|
34
|
-
@app = app
|
35
|
-
@result_holder = options[:holder]
|
36
|
-
end
|
37
|
-
|
38
|
-
def call(env)
|
39
|
-
response = @app.call(env)
|
40
|
-
@result_holder.cors_result = env[Rack::Cors::RACK_CORS]
|
41
|
-
response
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class FakeProxy
|
46
|
-
def initialize(app, _options = {})
|
47
|
-
@app = app
|
48
|
-
end
|
49
|
-
|
50
|
-
def call(env)
|
51
|
-
status, headers, body = @app.call(env)
|
52
|
-
headers['vary'] = %w[Origin User-Agent]
|
53
|
-
[status, headers, body]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
Rack::MockResponse.infect_an_assertion :assert_cors_success, :must_render_cors_success, :only_one_argument
|
58
|
-
Rack::MockResponse.infect_an_assertion :assert_not_cors_success, :wont_render_cors_success, :only_one_argument
|
59
|
-
|
60
|
-
describe Rack::Cors do
|
61
|
-
include Rack::Test::Methods
|
62
|
-
|
63
|
-
attr_accessor :cors_result
|
64
|
-
|
65
|
-
def load_app(name, options = {})
|
66
|
-
test = self
|
67
|
-
Rack::Builder.new do
|
68
|
-
use CaptureResult, holder: test
|
69
|
-
eval File.read(File.dirname(__FILE__) + "/#{name}.ru")
|
70
|
-
use FakeProxy if options[:proxy]
|
71
|
-
map('/') do
|
72
|
-
run(lambda do |_env|
|
73
|
-
[200, { 'content-type' => 'text/html' }, ['success']]
|
74
|
-
end)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
let(:app) { load_app('test') }
|
80
|
-
|
81
|
-
it 'should support simple CORS request' do
|
82
|
-
successful_cors_request
|
83
|
-
_(cors_result).must_be :hit
|
84
|
-
end
|
85
|
-
|
86
|
-
it "should not return CORS headers if Origin header isn't present" do
|
87
|
-
get '/'
|
88
|
-
_(last_response).wont_render_cors_success
|
89
|
-
_(cors_result).wont_be :hit
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'should support OPTIONS CORS request' do
|
93
|
-
successful_cors_request '/options', method: :options
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'should support regex origins configuration' do
|
97
|
-
successful_cors_request origin: 'http://192.168.0.1:1234'
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'should support subdomain example' do
|
101
|
-
successful_cors_request origin: 'http://subdomain.example.com'
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'should support proc origins configuration' do
|
105
|
-
successful_cors_request '/proc-origin', origin: 'http://10.10.10.10:3000'
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'should support lambda origin configuration' do
|
109
|
-
successful_cors_request '/lambda-origin', origin: 'http://10.10.10.10:3000'
|
110
|
-
end
|
111
|
-
|
112
|
-
it 'should support proc origins configuration (inverse)' do
|
113
|
-
cors_request '/proc-origin', origin: 'http://bad.guy'
|
114
|
-
_(last_response).wont_render_cors_success
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'should not mix up path rules across origins' do
|
118
|
-
header 'Origin', 'http://10.10.10.10:3000'
|
119
|
-
get '/' # / is configured in a separate rule block
|
120
|
-
_(last_response).wont_render_cors_success
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'should support alternative X-Origin header' do
|
124
|
-
header 'X-Origin', 'http://localhost:3000'
|
125
|
-
get '/'
|
126
|
-
_(last_response).must_render_cors_success
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'should support expose header configuration' do
|
130
|
-
successful_cors_request '/expose_single_header'
|
131
|
-
_(last_response.headers['Access-Control-Expose-Headers']).must_equal 'expose-test'
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'should support expose multiple header configuration' do
|
135
|
-
successful_cors_request '/expose_multiple_headers'
|
136
|
-
_(last_response.headers['Access-Control-Expose-Headers']).must_equal 'expose-test-1, expose-test-2'
|
137
|
-
end
|
138
|
-
|
139
|
-
# Explanation: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
|
140
|
-
it "should add Vary header if resource matches even if Origin header isn't present" do
|
141
|
-
get '/'
|
142
|
-
_(last_response).wont_render_cors_success
|
143
|
-
_(last_response.headers['Vary']).must_equal 'Origin'
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'should add Vary header based on :vary option' do
|
147
|
-
successful_cors_request '/vary_test'
|
148
|
-
_(last_response.headers['Vary']).must_equal 'Origin, Host'
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'decode URL and resolve paths before resource matching' do
|
152
|
-
header 'Origin', 'http://localhost:3000'
|
153
|
-
get '/public/a/..%2F..%2Fprivate/stuff'
|
154
|
-
_(last_response).wont_render_cors_success
|
155
|
-
end
|
156
|
-
|
157
|
-
describe 'with upstream Vary headers' do
|
158
|
-
let(:app) { load_app('test', { proxy: true }) }
|
159
|
-
|
160
|
-
it 'should add to them' do
|
161
|
-
successful_cors_request '/vary_test'
|
162
|
-
_(last_response.headers['Vary']).must_equal 'Origin, User-Agent, Host'
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'should add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
|
167
|
-
successful_cors_request '/', origin: 'http://192.168.0.3:8080'
|
168
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal 'http://192.168.0.3:8080'
|
169
|
-
_(last_response.headers['Vary']).wont_be_nil
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'should add Vary header even if Access-Control-Allow-Origin header was added and it is generic (*)' do
|
173
|
-
successful_cors_request '/public_without_credentials', origin: 'http://192.168.1.3:8080'
|
174
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal '*'
|
175
|
-
_(last_response.headers['Vary']).must_equal 'Origin'
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'should support multi allow configurations for the same resource' do
|
179
|
-
successful_cors_request '/multi-allow-config', origin: 'http://mucho-grande.com'
|
180
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal 'http://mucho-grande.com'
|
181
|
-
_(last_response.headers['Vary']).must_equal 'Origin'
|
182
|
-
|
183
|
-
successful_cors_request '/multi-allow-config', origin: 'http://192.168.1.3:8080'
|
184
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal '*'
|
185
|
-
_(last_response.headers['Vary']).must_equal 'Origin'
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'should not return CORS headers on OPTIONS request if Access-Control-Allow-Origin is not present' do
|
189
|
-
options '/get-only'
|
190
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_be_nil
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'should not apply CORS headers if it does not match conditional on resource' do
|
194
|
-
header 'Origin', 'http://192.168.0.1:1234'
|
195
|
-
get '/conditional'
|
196
|
-
_(last_response).wont_render_cors_success
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'should apply CORS headers if it does match conditional on resource' do
|
200
|
-
header 'X-OK', '1'
|
201
|
-
successful_cors_request '/conditional', origin: 'http://192.168.0.1:1234'
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'should not allow everything if Origin is configured as blank string' do
|
205
|
-
cors_request '/blank-origin', origin: 'http://example.net'
|
206
|
-
_(last_response).wont_render_cors_success
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'should not allow credentials for public resources' do
|
210
|
-
successful_cors_request '/public'
|
211
|
-
_(last_response.headers['Access-Control-Allow-Credentials']).must_be_nil
|
212
|
-
end
|
213
|
-
|
214
|
-
describe 'logging' do
|
215
|
-
it 'should not log debug messages if debug option is false' do
|
216
|
-
app = mock
|
217
|
-
app.stubs(:call).returns(200, {}, [''])
|
218
|
-
|
219
|
-
logger = mock
|
220
|
-
logger.expects(:debug).never
|
221
|
-
|
222
|
-
cors = Rack::Cors.new(app, debug: false, logger: logger) {}
|
223
|
-
cors.send(:debug, {}, 'testing')
|
224
|
-
end
|
225
|
-
|
226
|
-
it 'should log debug messages if debug option is true' do
|
227
|
-
app = mock
|
228
|
-
app.stubs(:call).returns(200, {}, [''])
|
229
|
-
|
230
|
-
logger = mock
|
231
|
-
logger.expects(:debug)
|
232
|
-
|
233
|
-
cors = Rack::Cors.new(app, debug: true, logger: logger) {}
|
234
|
-
cors.send(:debug, {}, 'testing')
|
235
|
-
end
|
236
|
-
|
237
|
-
it 'should use rack.logger if available' do
|
238
|
-
app = mock
|
239
|
-
app.stubs(:call).returns([200, {}, ['']])
|
240
|
-
|
241
|
-
logger = mock
|
242
|
-
logger.expects(:debug).at_least_once
|
243
|
-
|
244
|
-
cors = Rack::Cors.new(app, debug: true) {}
|
245
|
-
cors.call({ 'rack.logger' => logger, 'HTTP_ORIGIN' => 'test.com' })
|
246
|
-
end
|
247
|
-
|
248
|
-
it 'should use logger proc' do
|
249
|
-
app = mock
|
250
|
-
app.stubs(:call).returns([200, {}, ['']])
|
251
|
-
|
252
|
-
logger = mock
|
253
|
-
logger.expects(:debug)
|
254
|
-
|
255
|
-
cors = Rack::Cors.new(app, debug: true, logger: proc { logger }) {}
|
256
|
-
cors.call({ 'HTTP_ORIGIN' => 'test.com' })
|
257
|
-
end
|
258
|
-
|
259
|
-
describe 'with Rails setup' do
|
260
|
-
after do
|
261
|
-
::Rails.logger = nil if defined?(::Rails)
|
262
|
-
end
|
263
|
-
|
264
|
-
it 'should use Rails.logger if available' do
|
265
|
-
app = mock
|
266
|
-
app.stubs(:call).returns([200, {}, ['']])
|
267
|
-
|
268
|
-
logger = mock
|
269
|
-
logger.expects(:debug)
|
270
|
-
|
271
|
-
::Rails = OpenStruct.new(logger: logger)
|
272
|
-
|
273
|
-
cors = Rack::Cors.new(app, debug: true) {}
|
274
|
-
cors.call({ 'HTTP_ORIGIN' => 'test.com' })
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
it 'should use Logger if none is set' do
|
279
|
-
app = mock
|
280
|
-
app.stubs(:call).returns([200, {}, ['']])
|
281
|
-
|
282
|
-
logger = mock
|
283
|
-
Logger.expects(:new).returns(logger)
|
284
|
-
logger.expects(:tap).returns(logger)
|
285
|
-
logger.expects(:debug)
|
286
|
-
|
287
|
-
cors = Rack::Cors.new(app, debug: true) {}
|
288
|
-
cors.call({ 'HTTP_ORIGIN' => 'test.com' })
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
describe 'preflight requests' do
|
293
|
-
it 'should fail if origin is invalid' do
|
294
|
-
preflight_request('http://allyourdataarebelongtous.com', '/')
|
295
|
-
_(last_response).wont_render_cors_success
|
296
|
-
_(cors_result).wont_be :hit
|
297
|
-
_(cors_result).must_be :preflight
|
298
|
-
end
|
299
|
-
|
300
|
-
it 'should fail if Access-Control-Request-Method is not allowed' do
|
301
|
-
preflight_request('http://localhost:3000', '/get-only', method: :post)
|
302
|
-
_(last_response).wont_render_cors_success
|
303
|
-
_(cors_result.miss_reason).must_equal Rack::Cors::Result::MISS_DENY_METHOD
|
304
|
-
_(cors_result).wont_be :hit
|
305
|
-
_(cors_result).must_be :preflight
|
306
|
-
end
|
307
|
-
|
308
|
-
it 'should fail if header is not allowed' do
|
309
|
-
preflight_request('http://localhost:3000', '/single_header', headers: 'Fooey')
|
310
|
-
_(last_response).wont_render_cors_success
|
311
|
-
_(cors_result.miss_reason).must_equal Rack::Cors::Result::MISS_DENY_HEADER
|
312
|
-
_(cors_result).wont_be :hit
|
313
|
-
_(cors_result).must_be :preflight
|
314
|
-
end
|
315
|
-
|
316
|
-
it 'should allow any header if headers = :any' do
|
317
|
-
preflight_request('http://localhost:3000', '/', headers: 'Fooey')
|
318
|
-
_(last_response).must_render_cors_success
|
319
|
-
end
|
320
|
-
|
321
|
-
it 'should allow any method if methods = :any' do
|
322
|
-
preflight_request('http://localhost:3000', '/', methods: :any)
|
323
|
-
_(last_response).must_render_cors_success
|
324
|
-
end
|
325
|
-
|
326
|
-
it 'allows PATCH method' do
|
327
|
-
preflight_request('http://localhost:3000', '/', methods: [:patch])
|
328
|
-
_(last_response).must_render_cors_success
|
329
|
-
end
|
330
|
-
|
331
|
-
it 'should allow header case insensitive match' do
|
332
|
-
preflight_request('http://localhost:3000', '/single_header', headers: 'X-Domain-Token')
|
333
|
-
_(last_response).must_render_cors_success
|
334
|
-
end
|
335
|
-
|
336
|
-
it 'should allow multiple headers match' do
|
337
|
-
# Webkit style
|
338
|
-
preflight_request('http://localhost:3000', '/two_headers', headers: 'X-Requested-With, X-Domain-Token')
|
339
|
-
_(last_response).must_render_cors_success
|
340
|
-
|
341
|
-
# Gecko style
|
342
|
-
preflight_request('http://localhost:3000', '/two_headers', headers: 'x-requested-with,x-domain-token')
|
343
|
-
_(last_response).must_render_cors_success
|
344
|
-
end
|
345
|
-
|
346
|
-
it "should allow '*' origins to allow any origin" do
|
347
|
-
preflight_request('http://locohost:3000', '/public')
|
348
|
-
_(last_response).must_render_cors_success
|
349
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal '*'
|
350
|
-
end
|
351
|
-
|
352
|
-
it "should allow '/<path>/' resource if match pattern is /<path>/*" do
|
353
|
-
preflight_request('http://localhost:3000', '/wildcard/')
|
354
|
-
_(last_response).must_render_cors_success
|
355
|
-
_(last_response.headers['Access-Control-Allow-Origin']).wont_equal nil
|
356
|
-
end
|
357
|
-
|
358
|
-
it "should allow '/<path>' resource if match pattern is /<path>/*" do
|
359
|
-
preflight_request('http://localhost:3000', '/wildcard')
|
360
|
-
_(last_response).must_render_cors_success
|
361
|
-
_(last_response.headers['Access-Control-Allow-Origin']).wont_equal nil
|
362
|
-
end
|
363
|
-
|
364
|
-
it "should allow '*' origin to allow any origin, and set '*' if no credentials required" do
|
365
|
-
preflight_request('http://locohost:3000', '/public_without_credentials')
|
366
|
-
_(last_response).must_render_cors_success
|
367
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal '*'
|
368
|
-
end
|
369
|
-
|
370
|
-
it 'should return "file://" as header with "file://" as origin' do
|
371
|
-
preflight_request('file://', '/')
|
372
|
-
_(last_response).must_render_cors_success
|
373
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal 'file://'
|
374
|
-
end
|
375
|
-
|
376
|
-
it 'supports custom protocols in origin' do
|
377
|
-
preflight_request('custom-protocol://abcdefg', '/')
|
378
|
-
_(last_response).must_render_cors_success
|
379
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal 'custom-protocol://abcdefg'
|
380
|
-
end
|
381
|
-
|
382
|
-
describe '' do
|
383
|
-
let(:app) do
|
384
|
-
test = self
|
385
|
-
Rack::Builder.new do
|
386
|
-
use CaptureResult, holder: test
|
387
|
-
use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
|
388
|
-
allow do
|
389
|
-
origins '*'
|
390
|
-
resource '/', methods: :post
|
391
|
-
end
|
392
|
-
end
|
393
|
-
map('/') do
|
394
|
-
run ->(_env) { [500, {}, ['FAIL!']] }
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
|
-
it 'should not send failed preflight requests thru the app' do
|
400
|
-
preflight_request('http://localhost', '/', method: :unsupported)
|
401
|
-
_(last_response).wont_render_cors_success
|
402
|
-
_(last_response.status).must_equal 200
|
403
|
-
_(cors_result).must_be :preflight
|
404
|
-
_(cors_result).wont_be :hit
|
405
|
-
_(cors_result.miss_reason).must_equal Rack::Cors::Result::MISS_DENY_METHOD
|
406
|
-
end
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
describe 'with insecure configuration' do
|
411
|
-
let(:app) { load_app('insecure') }
|
412
|
-
|
413
|
-
it 'should raise an error' do
|
414
|
-
_(proc { cors_request '/public' }).must_raise Rack::Cors::Resource::CorsMisconfigurationError
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
describe 'with non HTTP config' do
|
419
|
-
let(:app) { load_app('non_http') }
|
420
|
-
|
421
|
-
it 'should support non http/https origins' do
|
422
|
-
successful_cors_request '/public', origin: 'content://com.company.app'
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
describe 'Rack::Lint' do
|
427
|
-
def app
|
428
|
-
@app ||= Rack::Builder.new do
|
429
|
-
use Rack::Cors
|
430
|
-
use Rack::Lint
|
431
|
-
run ->(_env) { [200, { 'content-type' => 'text/html' }, ['hello']] }
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
it 'is lint-compliant with non-CORS request' do
|
436
|
-
get '/'
|
437
|
-
_(last_response.status).must_equal 200
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
describe 'with app overriding CORS header' do
|
442
|
-
let(:app) do
|
443
|
-
Rack::Builder.new do
|
444
|
-
use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
|
445
|
-
allow do
|
446
|
-
origins '*'
|
447
|
-
resource '/'
|
448
|
-
end
|
449
|
-
end
|
450
|
-
map('/') do
|
451
|
-
run ->(_env) { [200, { 'Access-Control-Allow-Origin' => 'http://foo.net' }, ['success']] }
|
452
|
-
end
|
453
|
-
end
|
454
|
-
end
|
455
|
-
|
456
|
-
it 'should return app header' do
|
457
|
-
successful_cors_request origin: 'http://example.net'
|
458
|
-
_(last_response.headers['Access-Control-Allow-Origin']).must_equal 'http://foo.net'
|
459
|
-
end
|
460
|
-
|
461
|
-
it 'should return original headers if in debug' do
|
462
|
-
successful_cors_request origin: 'http://example.net'
|
463
|
-
_(last_response.headers['X-Rack-CORS-Original-Access-Control-Allow-Origin']).must_equal '*'
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
describe 'with headers set to nil' do
|
468
|
-
let(:app) do
|
469
|
-
Rack::Builder.new do
|
470
|
-
use Rack::Cors do
|
471
|
-
allow do
|
472
|
-
origins '*'
|
473
|
-
resource '/', headers: nil
|
474
|
-
end
|
475
|
-
end
|
476
|
-
map('/') do
|
477
|
-
run ->(_env) { [200, { 'content-type' => 'text/html' }, ['hello']] }
|
478
|
-
end
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
it 'should succeed with CORS simple headers' do
|
483
|
-
preflight_request('http://localhost:3000', '/', headers: 'Accept')
|
484
|
-
_(last_response).must_render_cors_success
|
485
|
-
end
|
486
|
-
end
|
487
|
-
|
488
|
-
describe 'with custom allowed headers' do
|
489
|
-
let(:app) do
|
490
|
-
Rack::Builder.new do
|
491
|
-
use Rack::Cors do
|
492
|
-
allow do
|
493
|
-
origins '*'
|
494
|
-
resource '/', headers: []
|
495
|
-
end
|
496
|
-
end
|
497
|
-
map('/') do
|
498
|
-
run ->(_env) { [200, { 'content-type' => 'text/html' }, ['hello']] }
|
499
|
-
end
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
it 'should succeed with CORS simple headers' do
|
504
|
-
preflight_request('http://localhost:3000', '/', headers: 'Accept')
|
505
|
-
_(last_response).must_render_cors_success
|
506
|
-
preflight_request('http://localhost:3000', '/', headers: 'Accept-Language')
|
507
|
-
_(last_response).must_render_cors_success
|
508
|
-
preflight_request('http://localhost:3000', '/', headers: 'Content-Type')
|
509
|
-
_(last_response).must_render_cors_success
|
510
|
-
preflight_request('http://localhost:3000', '/', headers: 'Content-Language')
|
511
|
-
_(last_response).must_render_cors_success
|
512
|
-
end
|
513
|
-
end
|
514
|
-
|
515
|
-
protected
|
516
|
-
|
517
|
-
def cors_request(*args)
|
518
|
-
path = args.first.is_a?(String) ? args.first : '/'
|
519
|
-
|
520
|
-
opts = { method: :get, origin: 'http://localhost:3000' }
|
521
|
-
opts.merge! args.last if args.last.is_a?(Hash)
|
522
|
-
|
523
|
-
header 'origin', opts[:origin]
|
524
|
-
current_session.__send__ opts[:method], path, {}, test: self
|
525
|
-
end
|
526
|
-
|
527
|
-
def successful_cors_request(*args)
|
528
|
-
cors_request(*args)
|
529
|
-
_(last_response).must_render_cors_success
|
530
|
-
end
|
531
|
-
|
532
|
-
def preflight_request(origin, path, opts = {})
|
533
|
-
header 'Origin', origin
|
534
|
-
unless opts.key?(:method) && opts[:method].nil?
|
535
|
-
header 'Access-Control-Request-Method', opts[:method] ? opts[:method].to_s.upcase : 'GET'
|
536
|
-
end
|
537
|
-
header 'Access-Control-Request-Headers', opts[:headers] if opts[:headers]
|
538
|
-
options path
|
539
|
-
end
|
540
|
-
end
|