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