rack-canonical-host 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.