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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35d3203952564f0ba3368443e122d7469a26e1d3
4
- data.tar.gz: b818b81e58a6fe8a720dbc27387b783d48d94ced
3
+ metadata.gz: e6bc6bda17ed0ec22ca85d722327dd5d1bcd53d3
4
+ data.tar.gz: d1b695e4c981811aec5d8885c0e59a1e6e7e2a3f
5
5
  SHA512:
6
- metadata.gz: c3a7af383aaf322c84ca2bdfc6994963ca3cb149b787cd6dec803e103d9e23857f8db48a204bb2cd77ab40740b925c5c183ac544f028bb836ed4af35ca4d5dd6
7
- data.tar.gz: 270717db4dbf29ba4a5356fd682e01a5faef0dba3847b231394e6ffd4dc7bdb2ab659a73ad7e74a4edb94b190af190dfe9ef66adf506cf0e27dc2b83e0c67b40
6
+ metadata.gz: b3d53af79ae673e177030f52176c93d7ba6e44aaf8c85bdbfa36f54404daa10fa7fc30ccac7ef7684562f8ced86e01e5b2d4a1c05f9e6ed83b88d60934ae714f
7
+ data.tar.gz: b1a74f4130768e4112aec5dcb1eb2bb07a16358eef146517e12af84370098582ac036621a2a529b1c2208cb185960ac7e7cd4d574c72f3c957cbeebb3e5a33c5
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
- --format progress
2
+ --warnings
3
+ --require spec_helper
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
+ cache: bundler
2
3
  script: bundle exec rspec
4
+ sudo: false
3
5
  rvm:
4
6
  - 1.9.3
5
7
  - 2.0.0
@@ -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
- [![Build Status][travis-image]][travis]
8
-
9
- [travis]: http://travis-ci.org/tylerhunt/rack-canonical-host
10
- [travis-image]: https://secure.travis-ci.org/tylerhunt/rack-canonical-host.png
11
-
7
+ [![Gem Version](https://img.shields.io/gem/v/rack-canonical-host.svg)](http://rubygems.org/gems/rack-canonical-host)
8
+ [![Build Status](https://img.shields.io/travis/tylerhunt/rack-canonical-host/master.svg)](https://travis-ci.org/tylerhunt/rack-canonical-host)
9
+ [![Code Climate](https://img.shields.io/codeclimate/github/tylerhunt/rack-canonical-host.svg)](https://codeclimate.com/github/tylerhunt/rack-canonical-host)
10
+ [![Dependency Status](https://gemnasium.com/tylerhunt/rack-canonical-host.svg)](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-2012 Tyler Hunt.
124
+ Copyright © 2009-2016 Tyler Hunt.
125
+
135
126
  Released under the terms of the MIT license. See LICENSE for details.
@@ -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
- @app = app
9
- @host = host
10
- @options = options
11
- @block = block
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 = host(env)
16
- redirect = Redirect.new(env, host, @options)
15
+ host = evaluate_host(env)
16
+ redirect = Redirect.new(env, host, options)
17
17
 
18
18
  if redirect.canonical?
19
- @app.call(env)
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 host(env)
28
- @block ? @block.call(env) || @host : @host
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
- @env = env
19
- @host = host
20
- @force_ssl = options[:force_ssl]
21
- @ignore = Array(options[:ignore])
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
- (known? && ssl?) || ignored? || !conditions_match?
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 known?
37
- @host.nil? || request_uri.host == @host
43
+ def any_match?(patterns, string)
44
+ patterns.any? { |pattern| string[pattern] }
38
45
  end
39
46
 
40
- def ssl?
41
- !@force_ssl || request_uri.scheme == "https"
47
+ def headers
48
+ {
49
+ 'Location' => new_url,
50
+ 'Content-Type' => 'text/html',
51
+ }
42
52
  end
43
53
 
44
- def ignored?
45
- @ignore && @ignore.include?(request_uri.host)
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 conditions_match?
49
- return true unless @if.size > 0
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 any_regexp_match?( regexp_array, string )
55
- regexp_array.any?{ |r| string[r] }
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.tap { |uri|
61
- uri.host = @host if @host
62
- uri.scheme = "https" if @force_ssl
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(@env).url)
76
+ @request_uri ||= Addressable::URI.parse(Rack::Request.new(env).url)
68
77
  end
69
78
  end
70
79
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class CanonicalHost
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -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 'rspec', '~> 2.0'
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.should_receive(:call).with(env).and_return(response)
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.should_not_receive(:call)
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.should_receive(:call).with(env).and_return(response)
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
@@ -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 failure_message_for_should
37
+ def failure_message
38
38
  "Expected response to #{description}"
39
39
  end
40
40
 
41
- def failure_message_for_should_not
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.1.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: 2014-04-17 00:00:00.000000000 Z
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: '2.0'
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: '2.0'
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.0.14
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.