rack-canonical-host 0.1.0 → 0.2.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/.rspec +2 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +10 -1
- data/README.md +7 -16
- data/lib/rack/canonical_host.rb +16 -9
- data/lib/rack/canonical_host/redirect.rb +34 -25
- data/lib/rack/canonical_host/version.rb +1 -1
- data/rack-canonical-host.gemspec +3 -2
- data/spec/rack/canonical_host_spec.rb +35 -33
- data/spec/spec_helper.rb +17 -2
- data/spec/support/matchers/be_redirect.rb +2 -2
- metadata +36 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6bc6bda17ed0ec22ca85d722327dd5d1bcd53d3
|
4
|
+
data.tar.gz: d1b695e4c981811aec5d8885c0e59a1e6e7e2a3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3d53af79ae673e177030f52176c93d7ba6e44aaf8c85bdbfa36f54404daa10fa7fc30ccac7ef7684562f8ced86e01e5b2d4a1c05f9e6ed83b88d60934ae714f
|
7
|
+
data.tar.gz: b1a74f4130768e4112aec5dcb1eb2bb07a16358eef146517e12af84370098582ac036621a2a529b1c2208cb185960ac7e7cd4d574c72f3c957cbeebb3e5a33c5
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.2.0 (2016-03-28)
|
4
|
+
|
5
|
+
* Normalize redirect URL to avoid XSS vulnerability ([Thomas Maurer][tma])
|
6
|
+
* Remove `:force_ssl` option in favor of using [rack-ssl][rack-ssl]
|
7
|
+
([Nathaniel Bibler][nbibler])
|
8
|
+
|
9
|
+
[rack-ssl]: http://rubygems.org/gems/rack-ssl
|
10
|
+
|
3
11
|
## 0.1.0 (2014-04-16)
|
4
12
|
|
5
13
|
* Add `:force_ssl` option ([Jeff Carbonella][jcarbo])
|
@@ -45,11 +53,12 @@
|
|
45
53
|
|
46
54
|
* Initial release ([Tyler Hunt][tylerhunt])
|
47
55
|
|
48
|
-
[jcarbo]: http://github.com/jcarbo
|
49
56
|
[finack]: http://github.com/finack
|
50
57
|
[firedev]: http://github.com/firedev
|
58
|
+
[jcarbo]: http://github.com/jcarbo
|
51
59
|
[jellybob]: http://github.com/jellybob
|
52
60
|
[jschuur]: http://github.com/jschuur
|
53
61
|
[nbibler]: http://github.com/nbibler
|
54
62
|
[rubymaverick]: http://github.com/ericallam
|
63
|
+
[tma]: http://github.com/tma
|
55
64
|
[tylerhunt]: http://github.com/tylerhunt
|
data/README.md
CHANGED
@@ -4,11 +4,10 @@ Rack middleware that lets you define a single host name as the canonical host
|
|
4
4
|
for your application. Requests for other host names will then be redirected to
|
5
5
|
the canonical host.
|
6
6
|
|
7
|
-
[](http://rubygems.org/gems/rack-canonical-host)
|
8
|
+
[](https://travis-ci.org/tylerhunt/rack-canonical-host)
|
9
|
+
[](https://codeclimate.com/github/tylerhunt/rack-canonical-host)
|
10
|
+
[](https://gemnasium.com/tylerhunt/rack-canonical-host)
|
12
11
|
|
13
12
|
## Installation
|
14
13
|
|
@@ -96,16 +95,6 @@ use Rack::CanonicalHost, 'example.com', if: /.*\.example\.com/
|
|
96
95
|
use Rack::CanonicalHost, 'example.ru', if: /.*\.example\.ru/
|
97
96
|
```
|
98
97
|
|
99
|
-
If you'd like to enforce the use of HTTPS, use the `:force_ssl` option:
|
100
|
-
|
101
|
-
``` ruby
|
102
|
-
use Rack::CanonicalHost, 'example.com', force_ssl: true
|
103
|
-
```
|
104
|
-
|
105
|
-
In this case, requests like `http://example.com` will be redirected to
|
106
|
-
`https://example.com`.
|
107
|
-
|
108
|
-
|
109
98
|
## Contributing
|
110
99
|
|
111
100
|
1. Fork it
|
@@ -120,6 +109,7 @@ In this case, requests like `http://example.com` will be redirected to
|
|
120
109
|
Thanks to the following people who have contributed patches or helpful
|
121
110
|
suggestions:
|
122
111
|
|
112
|
+
* [Thomas Maurer](https://github.com/tma)
|
123
113
|
* [Jeff Carbonella](https://github.com/jcarbo)
|
124
114
|
* [Joost Schuur](https://github.com/jellybob)
|
125
115
|
* [Jon Wood](https://github.com/jellybob)
|
@@ -131,5 +121,6 @@ suggestions:
|
|
131
121
|
|
132
122
|
## Copyright
|
133
123
|
|
134
|
-
Copyright © 2009-
|
124
|
+
Copyright © 2009-2016 Tyler Hunt.
|
125
|
+
|
135
126
|
Released under the terms of the MIT license. See LICENSE for details.
|
data/lib/rack/canonical_host.rb
CHANGED
@@ -5,27 +5,34 @@ require 'rack/canonical_host/version'
|
|
5
5
|
module Rack
|
6
6
|
class CanonicalHost
|
7
7
|
def initialize(app, host=nil, options={}, &block)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
self.app = app
|
9
|
+
self.host = host
|
10
|
+
self.options = options
|
11
|
+
self.block = block
|
12
12
|
end
|
13
13
|
|
14
14
|
def call(env)
|
15
|
-
host =
|
16
|
-
redirect = Redirect.new(env, host,
|
15
|
+
host = evaluate_host(env)
|
16
|
+
redirect = Redirect.new(env, host, options)
|
17
17
|
|
18
18
|
if redirect.canonical?
|
19
|
-
|
19
|
+
app.call(env)
|
20
20
|
else
|
21
21
|
redirect.response
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
protected
|
26
|
+
|
27
|
+
attr_accessor :app
|
28
|
+
attr_accessor :host
|
29
|
+
attr_accessor :options
|
30
|
+
attr_accessor :block
|
31
|
+
|
25
32
|
private
|
26
33
|
|
27
|
-
def
|
28
|
-
|
34
|
+
def evaluate_host(env)
|
35
|
+
block and block.call(env) or host
|
29
36
|
end
|
30
37
|
end
|
31
38
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'addressable/uri'
|
2
|
+
require 'rack'
|
2
3
|
|
3
4
|
module Rack
|
4
5
|
class CanonicalHost
|
@@ -15,56 +16,64 @@ module Rack
|
|
15
16
|
HTML
|
16
17
|
|
17
18
|
def initialize(env, host, options={})
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@if = Array(options[:if])
|
19
|
+
self.env = env
|
20
|
+
self.host = host
|
21
|
+
self.ignore = Array(options[:ignore])
|
22
|
+
self.conditions = Array(options[:if])
|
23
23
|
end
|
24
24
|
|
25
25
|
def canonical?
|
26
|
-
|
26
|
+
return true unless enabled?
|
27
|
+
known? || ignored?
|
27
28
|
end
|
28
29
|
|
29
30
|
def response
|
30
|
-
headers = { 'Location' => new_url, 'Content-Type' => 'text/html' }
|
31
31
|
[301, headers, [HTML_TEMPLATE % new_url]]
|
32
32
|
end
|
33
33
|
|
34
|
+
protected
|
35
|
+
|
36
|
+
attr_accessor :env
|
37
|
+
attr_accessor :host
|
38
|
+
attr_accessor :ignore
|
39
|
+
attr_accessor :conditions
|
40
|
+
|
34
41
|
private
|
35
42
|
|
36
|
-
def
|
37
|
-
|
43
|
+
def any_match?(patterns, string)
|
44
|
+
patterns.any? { |pattern| string[pattern] }
|
38
45
|
end
|
39
46
|
|
40
|
-
def
|
41
|
-
|
47
|
+
def headers
|
48
|
+
{
|
49
|
+
'Location' => new_url,
|
50
|
+
'Content-Type' => 'text/html',
|
51
|
+
}
|
42
52
|
end
|
43
53
|
|
44
|
-
def
|
45
|
-
|
54
|
+
def enabled?
|
55
|
+
return true if conditions.empty?
|
56
|
+
|
57
|
+
conditions.include?(request_uri.host) ||
|
58
|
+
any_match?(conditions, request_uri.host)
|
46
59
|
end
|
47
60
|
|
48
|
-
def
|
49
|
-
|
50
|
-
@if.include?( request_uri.host ) || any_regexp_match?( @if, request_uri.host )
|
61
|
+
def ignored?
|
62
|
+
ignore.include?(request_uri.host)
|
51
63
|
end
|
52
|
-
private :conditions_match?
|
53
64
|
|
54
|
-
def
|
55
|
-
|
65
|
+
def known?
|
66
|
+
host.nil? || request_uri.host == host
|
56
67
|
end
|
57
|
-
private :any_regexp_match?
|
58
68
|
|
59
69
|
def new_url
|
60
|
-
request_uri.
|
61
|
-
|
62
|
-
|
63
|
-
}.to_s
|
70
|
+
uri = request_uri.dup
|
71
|
+
uri.host = host
|
72
|
+
uri.normalize.to_s
|
64
73
|
end
|
65
74
|
|
66
75
|
def request_uri
|
67
|
-
Addressable::URI.parse(Rack::Request.new(
|
76
|
+
@request_uri ||= Addressable::URI.parse(Rack::Request.new(env).url)
|
68
77
|
end
|
69
78
|
end
|
70
79
|
end
|
data/rack-canonical-host.gemspec
CHANGED
@@ -7,9 +7,10 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.homepage = 'http://github.com/tylerhunt/rack-canonical-host'
|
8
8
|
gem.author = 'Tyler Hunt'
|
9
9
|
|
10
|
-
gem.add_dependency 'addressable'
|
10
|
+
gem.add_dependency 'addressable', '> 0', '< 3'
|
11
11
|
gem.add_dependency 'rack', '~> 1.0'
|
12
|
-
gem.add_development_dependency '
|
12
|
+
gem.add_development_dependency 'rake'
|
13
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
13
14
|
|
14
15
|
gem.files = `git ls-files`.split($\)
|
15
16
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rack::CanonicalHost do
|
4
|
-
let(:response) { [200, { 'Content-Type' => 'text/plain' }, 'OK'] }
|
4
|
+
let(:response) { [200, { 'Content-Type' => 'text/plain' }, ['OK']] }
|
5
5
|
let(:inner_app) { lambda { |env| response } }
|
6
6
|
|
7
|
-
def build_app(host=nil, options={}, inner_app=inner_app, &block)
|
7
|
+
def build_app(host=nil, options={}, inner_app=inner_app(), &block)
|
8
8
|
Rack::Builder.new do
|
9
9
|
use Rack::Lint
|
10
10
|
use Rack::CanonicalHost, host, options, &block
|
@@ -19,7 +19,7 @@ describe Rack::CanonicalHost do
|
|
19
19
|
it { should_not be_redirect }
|
20
20
|
|
21
21
|
it 'calls the inner app' do
|
22
|
-
inner_app.
|
22
|
+
expect(inner_app).to receive(:call).with(env).and_return(response)
|
23
23
|
subject
|
24
24
|
end
|
25
25
|
end
|
@@ -32,7 +32,7 @@ describe Rack::CanonicalHost do
|
|
32
32
|
it { should be_redirect.via(301).to('http://example.com/full/path') }
|
33
33
|
|
34
34
|
it 'does not call the inner app' do
|
35
|
-
inner_app.
|
35
|
+
expect(inner_app).to_not receive(:call)
|
36
36
|
subject
|
37
37
|
end
|
38
38
|
end
|
@@ -54,6 +54,23 @@ describe Rack::CanonicalHost do
|
|
54
54
|
include_context 'a matching request'
|
55
55
|
end
|
56
56
|
|
57
|
+
context 'with an X-Forwarded-Host' do
|
58
|
+
let(:app) { build_app('example.com') }
|
59
|
+
let(:url) { 'http://proxied.url/full/path' }
|
60
|
+
|
61
|
+
context 'which matches the canonical host' do
|
62
|
+
let(:env) { Rack::MockRequest.env_for(url, 'HTTP_X_FORWARDED_HOST' => 'example.com:80') }
|
63
|
+
|
64
|
+
include_context 'a matching request'
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'which does not match the canonical host' do
|
68
|
+
let(:env) { Rack::MockRequest.env_for(url, 'HTTP_X_FORWARDED_HOST' => 'www.example.com:80') }
|
69
|
+
|
70
|
+
include_context 'a non-matching request'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
57
74
|
context 'without any options' do
|
58
75
|
let(:app) { build_app('example.com') }
|
59
76
|
|
@@ -79,7 +96,7 @@ describe Rack::CanonicalHost do
|
|
79
96
|
it { should_not be_redirect }
|
80
97
|
|
81
98
|
it 'calls the inner app' do
|
82
|
-
inner_app.
|
99
|
+
expect(inner_app).to receive(:call).with(env).and_return(response)
|
83
100
|
subject
|
84
101
|
end
|
85
102
|
end
|
@@ -117,34 +134,6 @@ describe Rack::CanonicalHost do
|
|
117
134
|
|
118
135
|
end
|
119
136
|
|
120
|
-
context 'with :force_ssl option' do
|
121
|
-
let(:app) { build_app('example.com', :force_ssl => true) }
|
122
|
-
|
123
|
-
context 'with a non-ssl request' do
|
124
|
-
let(:url) { 'http://example.com/full/path' }
|
125
|
-
it { should be_redirect.to('https://example.com/full/path') }
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'with an ssl request' do
|
129
|
-
let(:url) { 'https://example.com/full/path' }
|
130
|
-
it { should_not be_redirect }
|
131
|
-
end
|
132
|
-
|
133
|
-
context 'when host is not set' do
|
134
|
-
let(:app) { build_app(nil, :force_ssl => true) }
|
135
|
-
let(:url) { 'http://example.com/full/path' }
|
136
|
-
|
137
|
-
it { should be_redirect.to('https://example.com/full/path') }
|
138
|
-
end
|
139
|
-
|
140
|
-
context 'when :forse_ssl is false' do
|
141
|
-
let(:app) { build_app('example.com', :force_ssl => false) }
|
142
|
-
let(:url) { 'http://example.com/full/path' }
|
143
|
-
|
144
|
-
it { should_not be_redirect }
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
137
|
context 'with a block' do
|
149
138
|
let(:app) { build_app { 'example.com' } }
|
150
139
|
|
@@ -156,5 +145,18 @@ describe Rack::CanonicalHost do
|
|
156
145
|
include_context 'matching and non-matching requests'
|
157
146
|
end
|
158
147
|
end
|
148
|
+
|
149
|
+
context 'with URL containing JavaScript XSS' do
|
150
|
+
let(:url) { 'http://subdomain.example.net/full/path' }
|
151
|
+
let(:env) do
|
152
|
+
Rack::MockRequest.env_for(url).tap do |env|
|
153
|
+
env['QUERY_STRING'] = '"><script>alert(73541);</script>'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:app) { build_app('example.com') }
|
158
|
+
|
159
|
+
it { should be_redirect.to('http://example.com/full/path?%22%3E%3Cscript%3Ealert(73541)%3B%3C/script%3E') }
|
160
|
+
end
|
159
161
|
end
|
160
162
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,22 @@ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each do |file|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
RSpec.configure do |config|
|
8
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
-
config.run_all_when_everything_filtered = true
|
10
8
|
config.filter_run :focus
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
|
11
|
+
config.order = :random
|
12
|
+
Kernel.srand config.seed
|
13
|
+
|
14
|
+
if config.files_to_run.one?
|
15
|
+
config.default_formatter = 'doc'
|
16
|
+
end
|
17
|
+
|
18
|
+
config.expect_with :rspec do |expectations|
|
19
|
+
expectations.syntax = :expect
|
20
|
+
end
|
21
|
+
|
22
|
+
config.mock_with :rspec do |mocks|
|
23
|
+
mocks.syntax = :expect
|
24
|
+
mocks.verify_partial_doubles = true
|
25
|
+
end
|
11
26
|
end
|
@@ -34,11 +34,11 @@ module BeRedirect
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
37
|
+
def failure_message
|
38
38
|
"Expected response to #{description}"
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def failure_message_when_negated
|
42
42
|
"Did not expect response to #{description}"
|
43
43
|
end
|
44
44
|
|
metadata
CHANGED
@@ -1,66 +1,86 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-canonical-host
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tyler Hunt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ">"
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rack
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
|
-
- - ~>
|
37
|
+
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
39
|
version: '1.0'
|
34
40
|
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
|
-
- - ~>
|
44
|
+
- - "~>"
|
39
45
|
- !ruby/object:Gem::Version
|
40
46
|
version: '1.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
41
61
|
- !ruby/object:Gem::Dependency
|
42
62
|
name: rspec
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
44
64
|
requirements:
|
45
|
-
- - ~>
|
65
|
+
- - "~>"
|
46
66
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
67
|
+
version: '3.0'
|
48
68
|
type: :development
|
49
69
|
prerelease: false
|
50
70
|
version_requirements: !ruby/object:Gem::Requirement
|
51
71
|
requirements:
|
52
|
-
- - ~>
|
72
|
+
- - "~>"
|
53
73
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
74
|
+
version: '3.0'
|
55
75
|
description:
|
56
76
|
email:
|
57
77
|
executables: []
|
58
78
|
extensions: []
|
59
79
|
extra_rdoc_files: []
|
60
80
|
files:
|
61
|
-
- .gitignore
|
62
|
-
- .rspec
|
63
|
-
- .travis.yml
|
81
|
+
- ".gitignore"
|
82
|
+
- ".rspec"
|
83
|
+
- ".travis.yml"
|
64
84
|
- CHANGELOG.md
|
65
85
|
- Gemfile
|
66
86
|
- LICENSE
|
@@ -83,17 +103,17 @@ require_paths:
|
|
83
103
|
- lib
|
84
104
|
required_ruby_version: !ruby/object:Gem::Requirement
|
85
105
|
requirements:
|
86
|
-
- -
|
106
|
+
- - ">="
|
87
107
|
- !ruby/object:Gem::Version
|
88
108
|
version: '0'
|
89
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
110
|
requirements:
|
91
|
-
- -
|
111
|
+
- - ">="
|
92
112
|
- !ruby/object:Gem::Version
|
93
113
|
version: '0'
|
94
114
|
requirements: []
|
95
115
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.
|
116
|
+
rubygems_version: 2.4.5
|
97
117
|
signing_key:
|
98
118
|
specification_version: 4
|
99
119
|
summary: Rack middleware for defining a canonical host name.
|