better_errors 0.6.0 → 0.7.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.

Potentially problematic release.


This version of better_errors might be problematic. Click here for more details.

@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0-rc2
data/README.md CHANGED
@@ -21,8 +21,6 @@ group :development do
21
21
  end
22
22
  ```
23
23
 
24
- **NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
25
-
26
24
  If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](http://twitter.com/banisterfiend) to your Gemfile:
27
25
 
28
26
  ```ruby
@@ -31,6 +29,28 @@ gem "binding_of_caller"
31
29
 
32
30
  This is an optional dependency however, and Better Errors will work without it.
33
31
 
32
+ ## Security
33
+
34
+ **NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
35
+
36
+ You will notice that the only machine that gets the Better Errors page is localhost, which means you get the default error page if you are developing on a remote host (or a virtually remote host, such as a Vagrant box). Obviously, the REPL is not something you want to expose to the public, but there may also be other pieces of sensitive information available in the backtrace.
37
+
38
+ To poke selective holes in this security mechanism, you can add a line like this to your startup (for example, on Rails it would be `config/environments/development.rb`)
39
+
40
+ ```ruby
41
+ BetterErrors::Middleware.allow_ip! ENV['TRUSTED_IP'] if ENV['TRUSTED_IP']
42
+ ```
43
+
44
+ Then run Rails like this:
45
+
46
+ ```shell
47
+ TRUSTED_IP=66.68.96.220 rails s
48
+ ```
49
+
50
+ Note that the `allow_ip!` is actually backed by a `Set`, so you can add more than one IP address or subnet.
51
+
52
+ **Tip:** You can find your apparent IP by hitting the old error page's "Show env dump" and looking at "REMOTE_ADDR".
53
+
34
54
  ## Usage
35
55
 
36
56
  If you're using Rails, there's nothing else you need to do.
data/Rakefile CHANGED
@@ -1 +1,4 @@
1
1
  require "bundler/gem_tasks"
2
+ task :default => :spec
3
+ task :test => :spec
4
+ task :spec do sh 'bundle exec rspec' end
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
17
17
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
18
  s.require_paths = ["lib"]
19
19
 
20
+ s.required_ruby_version = ">= 1.9.2"
21
+
20
22
  s.add_development_dependency "rake"
21
23
  s.add_development_dependency "rspec", "~> 2.12.0"
22
24
  s.add_development_dependency "binding_of_caller"
@@ -91,6 +91,8 @@ module BetterErrors
91
91
  self.editor = "subl://open?url=file://%{file}&line=%{line}"
92
92
  when :macvim, :mvim
93
93
  self.editor = proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" }
94
+ when :emacs
95
+ self.editor = "emacs://open?url=file://%{file}&line=%{line}"
94
96
  when String
95
97
  self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
96
98
  else
@@ -1,17 +1,18 @@
1
1
  require "json"
2
2
  require "ipaddr"
3
+ require "set"
3
4
 
4
5
  module BetterErrors
5
6
  # Better Errors' error handling middleware. Including this in your middleware
6
7
  # stack will show a Better Errors error page for exceptions raised below this
7
8
  # middleware.
8
- #
9
- # If you are using Ruby on Rails, you do not need to manually insert this
9
+ #
10
+ # If you are using Ruby on Rails, you do not need to manually insert this
10
11
  # middleware into your middleware stack.
11
- #
12
+ #
12
13
  # @example Sinatra
13
14
  # require "better_errors"
14
- #
15
+ #
15
16
  # if development?
16
17
  # use BetterErrors::Middleware
17
18
  # end
@@ -21,39 +22,51 @@ module BetterErrors
21
22
  # if ENV["RACK_ENV"] == "development"
22
23
  # use BetterErrors::Middleware
23
24
  # end
24
- #
25
+ #
25
26
  class Middleware
27
+ # The set of IP addresses that are allowed to access Better Errors.
28
+ #
29
+ # Set to `{ "127.0.0.1/8", "::1/128" }` by default.
30
+ ALLOWED_IPS = Set.new
31
+
32
+ # Adds an address to the set of IP addresses allowed to access Better
33
+ # Errors.
34
+ def self.allow_ip!(addr)
35
+ ALLOWED_IPS << IPAddr.new(addr)
36
+ end
37
+
38
+ allow_ip! "127.0.0.0/8"
39
+ allow_ip! "::1/128"
40
+
26
41
  # A new instance of BetterErrors::Middleware
27
- #
42
+ #
28
43
  # @param app The Rack app/middleware to wrap with Better Errors
29
44
  # @param handler The error handler to use.
30
45
  def initialize(app, handler = ErrorPage)
31
46
  @app = app
32
47
  @handler = handler
33
48
  end
34
-
49
+
35
50
  # Calls the Better Errors middleware
36
- #
51
+ #
37
52
  # @param [Hash] env
38
53
  # @return [Array]
39
54
  def call(env)
40
- if local_request? env
55
+ if allow_ip? env
41
56
  better_errors_call env
42
57
  else
43
58
  @app.call env
44
59
  end
45
60
  end
46
-
61
+
47
62
  private
48
- IPV4_LOCAL = IPAddr.new("127.0.0.0/8")
49
- IPV6_LOCAL = IPAddr.new("::1/128")
50
63
 
51
- def local_request?(env)
64
+ def allow_ip?(env)
52
65
  # REMOTE_ADDR is not in the rack spec, so some application servers do
53
66
  # not provide it.
54
67
  return true unless env["REMOTE_ADDR"]
55
68
  ip = IPAddr.new env["REMOTE_ADDR"]
56
- IPV4_LOCAL.include? ip or IPV6_LOCAL.include? ip
69
+ ALLOWED_IPS.any? { |subnet| subnet.include? ip }
57
70
  end
58
71
 
59
72
  def better_errors_call(env)
@@ -93,18 +106,18 @@ module BetterErrors
93
106
  env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ||
94
107
  !env["HTTP_ACCEPT"].to_s.include?('html')
95
108
  end
96
-
109
+
97
110
  def log_exception
98
111
  return unless BetterErrors.logger
99
-
112
+
100
113
  message = "\n#{@error_page.exception.class} - #{@error_page.exception.message}:\n"
101
114
  @error_page.backtrace_frames.each do |frame|
102
115
  message << " #{frame}\n"
103
116
  end
104
-
117
+
105
118
  BetterErrors.logger.fatal message
106
119
  end
107
-
120
+
108
121
  def internal_call(env, opts)
109
122
  if opts[:oid].to_i != @error_page.object_id
110
123
  return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
@@ -1,3 +1,3 @@
1
1
  module BetterErrors
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -23,48 +23,54 @@ module BetterErrors
23
23
  app.call("REMOTE_ADDR" => "1.2.3.4")
24
24
  end
25
25
 
26
+ it "should show to a whitelisted IP" do
27
+ BetterErrors::Middleware.allow_ip! '77.55.33.11'
28
+ app.should_receive :better_errors_call
29
+ app.call("REMOTE_ADDR" => "77.55.33.11")
30
+ end
31
+
26
32
  context "when requesting the /__better_errors manually" do
27
33
  let(:app) { Middleware.new(->env { ":)" }) }
28
-
34
+
29
35
  it "should show that no errors have been recorded" do
30
36
  status, headers, body = app.call("PATH_INFO" => "/__better_errors")
31
37
  body.join.should match /No errors have been recorded yet./
32
38
  end
33
39
  end
34
-
40
+
35
41
  context "when handling an error" do
36
42
  let(:app) { Middleware.new(->env { raise "oh no :(" }) }
37
-
43
+
38
44
  it "should return status 500" do
39
45
  status, headers, body = app.call({})
40
-
46
+
41
47
  status.should == 500
42
48
  end
43
-
49
+
44
50
  it "should return UTF-8 error pages" do
45
51
  status, headers, body = app.call({})
46
-
52
+
47
53
  headers["Content-Type"].should match /charset=utf-8/
48
54
  end
49
55
 
50
56
  it "should return text pages by default" do
51
57
  status, headers, body = app.call({})
52
-
58
+
53
59
  headers["Content-Type"].should match /text\/plain/
54
60
  end
55
-
61
+
56
62
  it "should return HTML pages by default" do
57
63
  # Chrome's 'Accept' header looks similar this.
58
64
  status, headers, body = app.call("HTTP_ACCEPT" => "text/html,application/xhtml+xml;q=0.9,*/*")
59
-
65
+
60
66
  headers["Content-Type"].should match /text\/html/
61
67
  end
62
-
68
+
63
69
  it "should log the exception" do
64
70
  logger = Object.new
65
71
  logger.should_receive :fatal
66
72
  BetterErrors.stub!(:logger).and_return(logger)
67
-
73
+
68
74
  app.call({})
69
75
  end
70
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-14 00:00:00.000000000 Z
12
+ date: 2013-02-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -165,6 +165,7 @@ extensions: []
165
165
  extra_rdoc_files: []
166
166
  files:
167
167
  - .gitignore
168
+ - .travis.yml
168
169
  - .yardopts
169
170
  - CONTRIBUTING.md
170
171
  - Gemfile
@@ -211,13 +212,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
211
212
  requirements:
212
213
  - - '>='
213
214
  - !ruby/object:Gem::Version
214
- version: '0'
215
+ version: 1.9.2
215
216
  required_rubygems_version: !ruby/object:Gem::Requirement
216
217
  none: false
217
218
  requirements:
218
219
  - - '>='
219
220
  - !ruby/object:Gem::Version
220
221
  version: '0'
222
+ segments:
223
+ - 0
224
+ hash: -156735969867517913
221
225
  requirements: []
222
226
  rubyforge_project:
223
227
  rubygems_version: 1.8.25
@@ -235,3 +239,4 @@ test_files:
235
239
  - spec/better_errors/support/my_source.rb
236
240
  - spec/better_errors_spec.rb
237
241
  - spec/spec_helper.rb
242
+ has_rdoc: