remote_ip_proxy_scrubber 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 825602125d5364b1bdc70746c8c0f35e9b87410b
4
+ data.tar.gz: 7d62c2bca5fb334c022cd01454e714107295a511
5
+ SHA512:
6
+ metadata.gz: 8dcc9c19faedf5f1530cb47709394680f66b9f7fde1dca783b899c152ea9d61d5c92a673ba35d9024239313b94611822068ff79a99dea748a5859063205fd9c5
7
+ data.tar.gz: 34a7c22cea7f23dd4f1d5a07b7eca37624ddf52cdffc33ebed7501838f94da46ffd50578c90e260cded104e2ac92af7f0e07d0831da0bd7d5cae43e3cff5a1b6
data/.gitignore ADDED
@@ -0,0 +1,39 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+ .ruby-version
36
+ .ruby-gemset
37
+
38
+ # This is a gem, so don't commit the Gemfile.lock
39
+ /Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ - 2.1.0
5
+ - 1.9.3
6
+ - 1.8.7
7
+ addons:
8
+ code_climate:
9
+ repo_token: b4467c40c37d79974e72fddf47335d9cd65596e3b2cf64aeac7594a6ba12f85e
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ if RUBY_VERSION > "1.9"
6
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
7
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Marcos Wright-Kuhns
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ [![Build Status](https://travis-ci.org/metavida/remote_ip_proxy_scrubber.svg?branch=master)](https://travis-ci.org/metavida/remote_ip_proxy_scrubber)
2
+ [![Code Climate](https://codeclimate.com/github/metavida/remote_ip_proxy_scrubber/badges/gpa.svg)](https://codeclimate.com/github/metavida/remote_ip_proxy_scrubber)
3
+ [![Test Coverage](https://codeclimate.com/github/metavida/remote_ip_proxy_scrubber/badges/coverage.svg)](https://codeclimate.com/github/metavida/remote_ip_proxy_scrubber)
4
+
5
+ # remote_ip Proxy Scrubber
6
+
7
+ A project that makes it as easy as possible to prevent Rails from logging IP addresses that belong to proxy devices in your HTTP request chain.
8
+
9
+ Because Rails has pretty dramatically changed how these sorts of IPs are filtered over the years, this project's ultimate goal is to make life easy on as many Rails versions as possible.
10
+
11
+ # Common Use-cases
12
+
13
+ * Using [CloudFlare in front of your app](https://www.cloudflare.com/ips)
14
+ * Using [Incapsula in front of your app](https://incapsula.zendesk.com/hc/en-us/articles/200627570-Restricting-direct-access-to-your-website-Incapsula-s-IP-addresses-)
15
+
16
+ # Usage
17
+
18
+ Let's say you've got proxy servers running outside of the local network where your Rails app is running. In this example, we'll say the IP addresses of these proxy servers are in these IP ranges: `17.0.0.4/30`, `17.17.0.8/30`
19
+
20
+ ## Fixing `request.remote_ip`
21
+
22
+ Without this gem, calls to `request.remote_ip` from your Rails app will return the IP addresses from your proxy servers. Adding the code, below, ensures that `request.remote_ip` will never return the IP addresses of your proxy servers, and assuming the servers that first process requests from your clients is adding an appropriate X-Forwarded-For header, `request.remote_ip` will return the real IP address of your clients!
23
+
24
+ ```ruby
25
+ # Add the following to config/application.rb or conifg/environments/*.rb
26
+
27
+ config.middleware.insert_before(Rails::Rack::Logger, RemoteIpProxyScrubber.filter_middleware, [
28
+ "17.0.0.4/30",
29
+ "17.17.0.8/30",
30
+ ])
31
+ ```
32
+
33
+ ## Fixing Rails logs
34
+
35
+ **Oddly enough**, even with `request.remote_ip` returning the correct value, Rails log will *still* contain IP addresses from your proxy servers. To fix this, you'll need to tell Rails to use a different logger.
36
+
37
+ ```ruby
38
+ # Add the following to config/application.rb or conifg/environments/*.rb
39
+
40
+ config.middleware.insert_before(Rails::Rack::Logger, RemoteIpProxyScrubber.patched_logger)
41
+ config.middleware.delete(Rails::Rack::Logger)
42
+ ```
43
+
44
+ # Questions? Contributions?
45
+
46
+ If this gem isn't working for you, feel free to open up an Issue, or a Pull Request if you've got a proposed solution! I maintain this project in my spare time, so your patience is appreciated.
47
+
48
+ # Credit
49
+
50
+ Thanks to [Haiku Learning](http://www.haikulearning.com) for sponsoring the initial development of this gem. We're scratching our own itch, but hopefully it's helpful for you too!
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ begin
2
+ require "bundler/gem_tasks"
3
+ rescue LoadError
4
+ puts "Bundler not available. Install it with: gem install bundler"
5
+ end
6
+
7
+ Dir[File.join(File.dirname(__FILE__), "lib/tasks/*.rake")].sort.each { |ext| load ext }
@@ -0,0 +1,55 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'remote_ip_proxy_scrubber/version'
4
+
5
+ module RemoteIpProxyScrubber
6
+ # Add the following to config/application.rb or conifg/environments/*
7
+ #
8
+ # config.middleware.insert_before(Rails::Rack::Logger, RemoteIpProxyScrubber.filter_middleware, [
9
+ # "216.73.93.70/31", # www.google.com
10
+ # "216.73.93.72/31", # www.google.com
11
+ # "17.0.0.0/8", # Apple
12
+ # ])
13
+ def filter_middleware
14
+ require 'remote_ip_proxy_scrubber/filter_proxy_ips'
15
+
16
+ RemoteIpProxyScrubber::Rails4::FilterProxyIPs
17
+ end
18
+ module_function :filter_middleware
19
+
20
+ # Returns a Class to be used a rack middleware,
21
+ # replacing the existing `Rails::Rack::Logger`.
22
+ #
23
+ # config.middleware.insert_before(Rails::Rack::Logger, RemoteIpProxyScrubber.patched_logger)
24
+ # config.middleware.delete(Rails::Rack::Logger)
25
+ def patched_logger
26
+ require 'remote_ip_proxy_scrubber/remote_ip_logger'
27
+
28
+ rails_version = self.rails_version
29
+ if rails_version >= Gem::Version.new('4.0.0')
30
+ RemoteIpProxyScrubber::Rails4::RemoteIPLogger
31
+ elsif rails_version >= Gem::Version.new('3.2.9')
32
+ RemoteIpProxyScrubber::Rails3_2_9::RemoteIPLogger
33
+ elsif rails_version >= Gem::Version.new('3.2.0')
34
+ RemoteIpProxyScrubber::Rails3_2_0::RemoteIPLogger
35
+ elsif rails_version >= Gem::Version.new('3.0.6')
36
+ RemoteIpProxyScrubber::Rails3_1::RemoteIPLogger
37
+ elsif rails_version >= Gem::Version.new('3.0.0')
38
+ RemoteIpProxyScrubber::Rails3_0::RemoteIPLogger
39
+ else
40
+ fail "Sorry, this gem doesn't know how to monkey-patch the Rails logger for Rails #{rails_version} yet."
41
+ end
42
+ end
43
+ module_function :patched_logger
44
+
45
+ def rails_version
46
+ rails_version = Rails.version rescue nil
47
+ rails_version ||= RAILS_GEM_VERSION rescue nil
48
+ if rails_version.nil?
49
+ fail "Unable to determine the current version of Rails"
50
+ end
51
+ Gem::Version.new(rails_version)
52
+ end
53
+ module_function :rails_version
54
+
55
+ end
@@ -0,0 +1,63 @@
1
+ require 'ipaddr'
2
+
3
+ module RemoteIpProxyScrubber
4
+ module Rails4
5
+ class FilterProxyIPs
6
+ X_FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'
7
+
8
+ attr_reader :proxies
9
+
10
+ def initialize(app, *proxy_matchers)
11
+ @app = app
12
+
13
+ @proxies = []
14
+
15
+ proxy_matchers.flatten.each do |matcher|
16
+ @proxies << case matcher
17
+ when Regexp, IPAddr
18
+ matcher
19
+ when String
20
+ IPAddr.new(matcher)
21
+ else
22
+ raise ArgumentError.new("Expected String, IPAddr or Regexp but found #{matcher.class} #{matcher.inspect}")
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ def call(env)
29
+ @env = env
30
+
31
+ ips = ips_from(X_FORWARDED_FOR)
32
+ @env[X_FORWARDED_FOR] = filter_proxies(ips).join(', ')
33
+
34
+ @app.call(@env)
35
+ end
36
+
37
+ protected
38
+
39
+ # Shamelssly copied from Rails 4.2
40
+ # https://github.com/rails/rails/blob/v4.2.0/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L149-L168
41
+ def ips_from(header)
42
+ # Split the comma-separated list into an array of strings
43
+ ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
44
+ ips.select do |ip|
45
+ begin
46
+ # Only return IPs that are valid according to the IPAddr#new method
47
+ range = IPAddr.new(ip).to_range
48
+ # we want to make sure nobody is sneaking a netmask in
49
+ range.begin == range.end
50
+ rescue ArgumentError
51
+ nil
52
+ end
53
+ end
54
+ end
55
+
56
+ def filter_proxies(ips)
57
+ ips.reject do |ip|
58
+ @proxies.any? { |proxy| proxy === ip }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,87 @@
1
+ # Simplify monkey-patching Rails logging
2
+ # so that it logs the request.remote_ip, not just the ip
3
+ module RemoteIpProxyScrubber
4
+ # Rails 3.3.0 through 4.x
5
+ # define the log format in Rails::Rack::Logger#started_request_message
6
+ # * https://github.com/rails/rails/blob/v4.2.1/railties/lib/rails/rack/logger.rb#L48-L55
7
+ # * https://github.com/rails/rails/blob/v4.0.0/railties/lib/rails/rack/logger.rb#L48-L55
8
+ module Rails4
9
+ class RemoteIPLogger < Rails::Rack::Logger
10
+ protected
11
+ def started_request_message(request)
12
+ 'Started %s "%s" for %s at %s' % [
13
+ request.request_method,
14
+ request.filtered_path,
15
+ request.remote_ip,
16
+ Time.now.to_default_s ]
17
+ end
18
+ end
19
+ end
20
+ # Rails 3.2.9 through 3.2.x
21
+ # use the same Rails::Rack::Logger#started_request_message
22
+ # * https://github.com/rails/rails/blob/v3.2.21/railties/lib/rails/rack/logger.rb#L37-L44
23
+ # * https://github.com/rails/rails/blob/v3.2.9/railties/lib/rails/rack/logger.rb#L37-L44
24
+ module Rails3_2_9
25
+ class RemoteIPLogger < ::RemoteIpProxyScrubber::Rails4::RemoteIPLogger
26
+ end
27
+ end
28
+
29
+ # Rails 3.2.0 through 3.2.8
30
+ # define the log format directly in in Rails::Rack::Logger#call_app
31
+ # * https://github.com/rails/rails/blob/v3.2.8/railties/lib/rails/rack/logger.rb#L22-L29
32
+ # * https://github.com/rails/rails/blob/v3.2.0/railties/lib/rails/rack/logger.rb#L22-L29
33
+ module Rails3_2_0
34
+ class RemoteIPLogger < Rails::Rack::Logger
35
+ protected
36
+ def call_app(env)
37
+ request = ActionDispatch::Request.new(env)
38
+ path = request.filtered_path
39
+ Rails.logger.info "\n\nStarted #{request.request_method} \"#{path}\" for #{request.remote_ip} at #{Time.now.to_default_s}"
40
+ @app.call(env)
41
+ ensure
42
+ ActiveSupport::LogSubscriber.flush_all!
43
+ end
44
+ end
45
+ end
46
+
47
+ # Rails 3.0.6 through 3.1.X
48
+ # use the Rails::Rack::Logger#before_dispatch method
49
+ # * https://github.com/rails/rails/blob/v3.1.12/railties/lib/rails/rack/logger.rb#L20-L26
50
+ # * https://github.com/rails/rails/blob/v3.1.0/railties/lib/rails/rack/logger.rb#L20-L26
51
+ # * https://github.com/rails/rails/blob/v3.0.20/railties/lib/rails/rack/logger.rb#L20-L26
52
+ # * https://github.com/rails/rails/blob/v3.0.6/railties/lib/rails/rack/logger.rb#L20-L26
53
+ module Rails3_1
54
+ class RemoteIPLogger < Rails::Rack::Logger
55
+ protected
56
+ def before_dispatch(env)
57
+ request = ActionDispatch::Request.new(env)
58
+ path = request.filtered_path
59
+
60
+ info "\n\nStarted #{request.request_method} \"#{path}\" " \
61
+ "for #{request.remote_ip} at #{Time.now.to_default_s}"
62
+ end
63
+ end
64
+ end
65
+
66
+ # Rails 3.0.0 through 3.0.5
67
+ # also use the Rails::Rack::Logger#before_dispatch method
68
+ # but use a slightly different implementation
69
+ # Rails 3.0.0 through 3.0.3 actually are even more slightly different
70
+ # but I don't expect the difference to affect our functionality
71
+ # * https://github.com/rails/rails/blob/v3.0.5/railties/lib/rails/rack/logger.rb#L20-L26
72
+ # * https://github.com/rails/rails/blob/v3.0.4/railties/lib/rails/rack/logger.rb#L20-L26
73
+ # * https://github.com/rails/rails/blob/v3.0.3/railties/lib/rails/rack/logger.rb#L20-L26
74
+ # * https://github.com/rails/rails/blob/v3.0.0/railties/lib/rails/rack/logger.rb#L20-L26
75
+ module Rails3_0
76
+ class RemoteIPLogger < Rails::Rack::Logger
77
+ protected
78
+ def before_dispatch(env)
79
+ request = ActionDispatch::Request.new(env)
80
+ path = request.fullpath
81
+
82
+ info "\n\nStarted #{request.request_method} \"#{path}\" " \
83
+ "for #{request.remote_ip} at #{Time.now.to_default_s}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,18 @@
1
+ module RemoteIpProxyScrubber
2
+ class Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
+
8
+ class << self
9
+ # A String representing the current version of the OEmbed gem.
10
+ def inspect
11
+ STRING
12
+ end
13
+ alias_method :to_s, :inspect
14
+ end
15
+ end
16
+
17
+ VERSION = Version::STRING
18
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:specs)
4
+
5
+ task :default => :specs
@@ -0,0 +1,42 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'remote_ip_proxy_scrubber/version'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = "remote_ip_proxy_scrubber"
10
+ s.version = RemoteIpProxyScrubber::Version.to_s
11
+
12
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
13
+ s.authors = ["Marcos Wright-Kuhns"]
14
+ s.date = "2015-03-20"
15
+ s.description = "Make it as easy as possible to prevent Rails from logging IP addresses that belong to proxy devices in your HTTP request chain."
16
+ s.email = "marcos@wrightkuhns.com"
17
+ s.homepage = "http://github.com/metavida/remote_ip_proxy_scrubber"
18
+ s.licenses = ["MIT"]
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = s.files.grep(%r{^(test|spec|features,integration_test)/})
22
+
23
+ s.require_paths = ["lib"]
24
+ s.rubygems_version = "2.4.6"
25
+ s.summary = "Help Rails ignore IPs for proxy devices"
26
+
27
+ if s.respond_to? :specification_version then
28
+ s.specification_version = 3
29
+
30
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
31
+ s.add_development_dependency(%q<rake>, [">= 0"])
32
+ s.add_development_dependency(%q<rspec>, ["~> 3.0"])
33
+ else
34
+ s.add_dependency(%q<rake>, [">= 0"])
35
+ s.add_dependency(%q<rspec>, ["~> 3.0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<rake>, [">= 0"])
39
+ s.add_dependency(%q<rspec>, ["~> 3.0"])
40
+ end
41
+ end
42
+
@@ -0,0 +1,171 @@
1
+ require File.dirname(__FILE__) + '/../lib/remote_ip_proxy_scrubber/filter_proxy_ips'
2
+ include SpecHelper
3
+
4
+
5
+ # Returns an Array of input argument combinations that should always be tested
6
+ def input_argmuent_variations
7
+ return @variations if @variations
8
+
9
+ @variations = []
10
+
11
+ single_args = [
12
+ '8.8.8.8', # single string
13
+ '8.8.8.0/24', # single string range
14
+ IPAddr.new('8.8.8.8'), # single IPAddr
15
+ IPAddr.new('8.8.8.0/24'), # single IPAddr range
16
+ /8.8.8.*/, # single Regexp
17
+ ]
18
+
19
+ single_args.each do |first_arg|
20
+ @variations << [first_arg]
21
+ single_args.each do |second_arg|
22
+ @variations << [first_arg, second_arg]
23
+ end
24
+ end
25
+
26
+ # Add a "crazy Array" variation
27
+ @variations << [[single_args[0], [single_args[1]]]]
28
+
29
+ @variations
30
+ end
31
+
32
+ describe RemoteIpProxyScrubber::Rails4::FilterProxyIPs do
33
+
34
+ describe ".initialize" do
35
+ # Test every possible variation of arguments
36
+ input_argmuent_variations.each do |args|
37
+ it "should set @proxies to an Array of Regexp and IPAddr, given #{args.inspect}" do
38
+ proxies = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(:app, *args).proxies
39
+ expect(proxies).to be_a(Array)
40
+ proxies.each do |proxy|
41
+ expect(proxy).to be_a(Regexp).or be_a(IPAddr)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ describe ".call" do
48
+ it "should remove a single IP with a simple String" do
49
+ # Given
50
+ app = double('app').as_null_object
51
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, '17.0.0.1')
52
+
53
+ # Then
54
+ env = double("env")
55
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '8.8.8.8, 17.0.0.1' }
56
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '8.8.8.8')
57
+
58
+ # When
59
+ proxy_filter.call(env)
60
+ end
61
+
62
+ it "should remove multiple IPs with a simple String" do
63
+ # Given
64
+ app = double('app').as_null_object
65
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, '17.0.0.1')
66
+
67
+ # Then
68
+ env = double("env")
69
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '8.8.8.8, 17.0.0.1, 17.0.0.2, 17.0.0.1' }
70
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '8.8.8.8, 17.0.0.2')
71
+
72
+ # When
73
+ proxy_filter.call(env)
74
+ end
75
+
76
+ it "should remove multiple IPs with a range String" do
77
+ # Given
78
+ app = double('app').as_null_object
79
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, '17.0.0.0/4')
80
+
81
+ # Then
82
+ env = double("env")
83
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '8.8.8.8, 17.0.0.1, 17.0.0.2, 127.0.0.1' }
84
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '8.8.8.8, 127.0.0.1')
85
+
86
+ # When
87
+ proxy_filter.call(env)
88
+ end
89
+
90
+ it "should remove multiple IPs with a simple IPAddr" do
91
+ # Given
92
+ app = double('app').as_null_object
93
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, IPAddr.new('17.0.0.1'))
94
+
95
+ # Then
96
+ env = double("env")
97
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '8.8.8.8, 17.0.0.1, 17.0.0.2, 17.0.0.1' }
98
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '8.8.8.8, 17.0.0.2')
99
+
100
+ # When
101
+ proxy_filter.call(env)
102
+ end
103
+
104
+ it "should remove multiple IPs with a range IPAddr" do
105
+ # Given
106
+ app = double('app').as_null_object
107
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, IPAddr.new('17.0.0.0/4'))
108
+
109
+ # Then
110
+ env = double("env")
111
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '8.8.8.8, 17.0.0.1, 17.0.0.2, 127.0.0.1' }
112
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '8.8.8.8, 127.0.0.1')
113
+
114
+ # When
115
+ proxy_filter.call(env)
116
+ end
117
+
118
+ it "should remove IPs with a Regexp" do
119
+ # Given
120
+ app = double('app').as_null_object
121
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app, /^17\./)
122
+
123
+ # Then
124
+ env = double("env")
125
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '170.0.0.1, 17.0.0.1, 9.8.7.6, 17.254.0.1' }
126
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '170.0.0.1, 9.8.7.6')
127
+
128
+ # When
129
+ proxy_filter.call(env)
130
+ end
131
+
132
+ it "should NOT remove IPs with no proxy_matches" do
133
+ # Given
134
+ app = double('app').as_null_object
135
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app)
136
+
137
+ # Then
138
+ env = double("env")
139
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { '170.0.0.1, 17.0.0.1, 9.8.7.6, 17.254.0.1' }
140
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '170.0.0.1, 17.0.0.1, 9.8.7.6, 17.254.0.1')
141
+
142
+ # When
143
+ proxy_filter.call(env)
144
+ end
145
+
146
+ it "should silently remove invalid IPs in the header" do
147
+ # Given
148
+ app = double('app').as_null_object
149
+ proxy_filter = RemoteIpProxyScrubber::Rails4::FilterProxyIPs.new(app)
150
+
151
+ invalid_ips = [
152
+ '127.0.0.500',
153
+ '17.0.0.1/4',
154
+ '17.0.0.1/500',
155
+ 'not an IP',
156
+ ]
157
+
158
+ # Then
159
+ env = double("env")
160
+ invalid_ips.each do |invalid|
161
+ allow(env).to receive(:[]).with('HTTP_X_FORWARDED_FOR') { "#{invalid}, 127.0.0.1" }
162
+ expect(env).to receive(:[]=).with('HTTP_X_FORWARDED_FOR', '127.0.0.1')
163
+
164
+ # When
165
+ proxy_filter.call(env)
166
+ end
167
+ end
168
+ end
169
+
170
+ end
171
+
@@ -0,0 +1,114 @@
1
+ require File.dirname(__FILE__) + '/../lib/remote_ip_proxy_scrubber'
2
+ include SpecHelper
3
+
4
+ # Define a few items that we'll stub out during out tests
5
+ class Rails
6
+ def self.version
7
+ raise 'this method must be stubbed in our tests'
8
+ end
9
+ module Rack
10
+ class Logger
11
+ end
12
+ end
13
+ end
14
+
15
+ # Backport the Rails methods we need for testing
16
+ class Hash
17
+ def reverse_merge!(other_hash)
18
+ replace(other_hash.merge(self))
19
+ end
20
+ end
21
+ class Array
22
+ def extract_options!
23
+ last.is_a?(::Hash) ? pop : {}
24
+ end
25
+ end
26
+
27
+ describe RemoteIpProxyScrubber do
28
+
29
+ describe ".patched_logger" do
30
+ rails_versions_to_test = {
31
+ :Rails4 => %w{4.0.0 4.1.10 4.3.3 5.0.0},
32
+ :Rails3_2_9 => %w{3.2.9 3.2.29 3.99.999},
33
+ :Rails3_2_0 => %w{3.2.0 3.2.8},
34
+ :Rails3_1 => %w{3.0.6 3.0.999 3.1.0 3.1.12 3.1.999},
35
+ :Rails3_0 => %w{3.0.0 3.0.5},
36
+ }
37
+
38
+ rails_versions_to_test.each do |expected_class, current_rails_versions|
39
+ expected_class_str = "RemoteIpProxyScrubber::#{expected_class}::RemoteIPLogger"
40
+ current_rails_versions.each do |rails_version|
41
+ it "should return #{expected_class_str} for Rails #{rails_version}" do
42
+ # Given
43
+ expect(Rails).to receive(:version) { rails_version }
44
+
45
+ # Then
46
+ expect(RemoteIpProxyScrubber.patched_logger).to \
47
+ be == expected_class_str.constantize
48
+ end
49
+ end
50
+ end
51
+
52
+ rails_versions_to_fail = %w{1.0.0 1.2.6 2.0.0 2.99.99 not.a.version}
53
+
54
+ rails_versions_to_fail.each do |rails_version|
55
+ it "should fail for Rails #{rails_version}" do
56
+ # Given
57
+ expect(Rails).to receive(:version) { rails_version }
58
+
59
+ # Then/When
60
+ expect {
61
+ RemoteIpProxyScrubber.patched_logger
62
+ }.to raise_error
63
+ end
64
+ end
65
+
66
+ it "should fail if there is no Rails version" do
67
+ # Given
68
+ expect(Rails).to receive(:version) { fail NoMethodError.new("No Rails") }
69
+
70
+ # Then/When
71
+ expect {
72
+ RemoteIpProxyScrubber.patched_logger
73
+ }.to raise_error
74
+ end
75
+
76
+ end
77
+
78
+ describe ".rails_version" do
79
+ it "should prefer Rails.version if available" do
80
+ # Given
81
+ expected_version = Gem::Version.new('0.0.1')
82
+ unexpected_version = Gem::Version.new('0.0.2')
83
+ expect(Rails).to receive(:version) { expected_version.to_s }
84
+ redefine_const(Object, :RAILS_GEM_VERSION, unexpected_version.to_s) do
85
+
86
+ # Then
87
+ expect(RemoteIpProxyScrubber.rails_version).to be == expected_version
88
+ end
89
+ end
90
+
91
+ it "should use RAILS_VERSION if Rails.version isn't available" do
92
+ # Given
93
+ expected_version = Gem::Version.new('0.0.1')
94
+ expect(Rails).to receive(:version) { fail NoMethodError.new("No Rails") }
95
+ redefine_const(Object, :RAILS_GEM_VERSION, expected_version.to_s) do
96
+
97
+ # Then
98
+ expect(RemoteIpProxyScrubber.rails_version).to be == expected_version
99
+ end
100
+ end
101
+
102
+ it "should fail if it can't figure out the version" do
103
+ # Given
104
+ expect(Rails).to receive(:version) { nil }
105
+ redefine_const(Object, :RAILS_GEM_VERSION, nil) do
106
+
107
+ # Then
108
+ expect {
109
+ RemoteIpProxyScrubber.rails_version
110
+ }.to raise_error
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,142 @@
1
+ # Conventionally, all specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
2
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
3
+ # this file to always be loaded, without a need to explicitly require it in any
4
+ # files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need
12
+ # it.
13
+ #
14
+ # The `.rspec` file also contains a few flags that are not defaults but that
15
+ # users commonly want.
16
+
17
+ begin
18
+ require "codeclimate-test-reporter"
19
+ CodeClimate::TestReporter.start
20
+ rescue LoadError # Don't fail hard if this gem isn't installed
21
+ end
22
+
23
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
24
+ RSpec.configure do |config|
25
+ # rspec-expectations config goes here. You can use an alternate
26
+ # assertion/expectation library such as wrong or the stdlib/minitest
27
+ # assertions if you prefer.
28
+ config.expect_with :rspec do |expectations|
29
+ # This option will default to `true` in RSpec 4. It makes the `description`
30
+ # and `failure_message` of custom matchers include text for helper methods
31
+ # defined using `chain`, e.g.:
32
+ # be_bigger_than(2).and_smaller_than(4).description
33
+ # # => "be bigger than 2 and smaller than 4"
34
+ # ...rather than:
35
+ # # => "be bigger than 2"
36
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
37
+ end
38
+
39
+ # rspec-mocks config goes here. You can use an alternate test double
40
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
41
+ config.mock_with :rspec do |mocks|
42
+ # Prevents you from mocking or stubbing a method that does not exist on
43
+ # a real object. This is generally recommended, and will default to
44
+ # `true` in RSpec 4.
45
+ mocks.verify_partial_doubles = true
46
+ end
47
+
48
+ # The settings below are suggested to provide a good initial experience
49
+ # with RSpec, but feel free to customize to your heart's content.
50
+ =begin
51
+ # These two settings work together to allow you to limit a spec run
52
+ # to individual examples or groups you care about by tagging them with
53
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
54
+ # get run.
55
+ config.filter_run :focus
56
+ config.run_all_when_everything_filtered = true
57
+
58
+ # Limits the available syntax to the non-monkey patched syntax that is
59
+ # recommended. For more details, see:
60
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
61
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
62
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
63
+ config.disable_monkey_patching!
64
+
65
+ # This setting enables warnings. It's recommended, but in some cases may
66
+ # be too noisy due to issues in dependencies.
67
+ config.warnings = true
68
+
69
+ # Many RSpec users commonly either run the entire suite or an individual
70
+ # file, and it's useful to allow more verbose output when running an
71
+ # individual spec file.
72
+ if config.files_to_run.one?
73
+ # Use the documentation formatter for detailed output,
74
+ # unless a formatter has already been configured
75
+ # (e.g. via a command-line flag).
76
+ config.default_formatter = 'doc'
77
+ end
78
+
79
+ # Print the 10 slowest examples and example groups at the
80
+ # end of the spec run, to help surface which specs are running
81
+ # particularly slow.
82
+ config.profile_examples = 10
83
+
84
+ # Run specs in random order to surface order dependencies. If you find an
85
+ # order dependency and want to debug it, you can fix the order by providing
86
+ # the seed, which is printed after each run.
87
+ # --seed 1234
88
+ config.order = :random
89
+
90
+ # Seed global randomization in this process using the `--seed` CLI option.
91
+ # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # test failures related to randomization by passing the same `--seed` value
93
+ # as the one that triggered the failure.
94
+ Kernel.srand config.seed
95
+ =end
96
+ end
97
+
98
+ module SpecHelper
99
+ # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
100
+ #
101
+ # silence_warnings do
102
+ # value = noisy_call # no warning voiced
103
+ # end
104
+ #
105
+ # noisy_call # warning voiced
106
+ def silence_warnings
107
+ old_verbose, $VERBOSE = $VERBOSE, nil
108
+ yield
109
+ ensure
110
+ $VERBOSE = old_verbose
111
+ end
112
+
113
+ # Redefine a constant within this block. For Example:
114
+ # class Apache
115
+ # CONF_DIR = '/etc/httpd/conf'
116
+ # end
117
+ #
118
+ # puts Apache::CONF_DIR #=> '/etc/httpd/conf'
119
+ # redefine_const(Apache, :CONF_DIR, '/apache/conf') do
120
+ # puts Apache::CONF_DIR #=> '/apache/conf'
121
+ # end
122
+ # puts Apache::CONF_DIR #=> '/etc/httpd/conf'
123
+ def redefine_const(klass, name, value)
124
+ old_value = klass.const_get(name) rescue nil
125
+ silence_warnings { klass.const_set(name, value) }
126
+ yield
127
+ ensure
128
+ silence_warnings { klass.const_set(name, old_value) }
129
+ end
130
+ end
131
+
132
+ class String
133
+ if RUBY_VERSION >= '2.0.0'
134
+ def constantize
135
+ Kernel.const_get(self)
136
+ end
137
+ else
138
+ def constantize
139
+ Object.module_eval("::#{self}", __FILE__, __LINE__)
140
+ end
141
+ end
142
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remote_ip_proxy_scrubber
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcos Wright-Kuhns
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ description: Make it as easy as possible to prevent Rails from logging IP addresses
42
+ that belong to proxy devices in your HTTP request chain.
43
+ email: marcos@wrightkuhns.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - ".travis.yml"
51
+ - Gemfile
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - lib/remote_ip_proxy_scrubber.rb
56
+ - lib/remote_ip_proxy_scrubber/filter_proxy_ips.rb
57
+ - lib/remote_ip_proxy_scrubber/remote_ip_logger.rb
58
+ - lib/remote_ip_proxy_scrubber/version.rb
59
+ - lib/tasks/rspec.rake
60
+ - remote_ip_proxy_scrubber.gemspec
61
+ - spec/filter_proxy_ips_spec.rb
62
+ - spec/remote_ip_proxy_scrubber_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: http://github.com/metavida/remote_ip_proxy_scrubber
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.4.6
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Help Rails ignore IPs for proxy devices
88
+ test_files:
89
+ - spec/filter_proxy_ips_spec.rb
90
+ - spec/remote_ip_proxy_scrubber_spec.rb
91
+ - spec/spec_helper.rb