web-console 3.7.0 → 4.0.4
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/CHANGELOG.markdown +32 -0
- data/README.markdown +20 -20
- data/lib/web_console.rb +3 -1
- data/lib/web_console/evaluator.rb +5 -3
- data/lib/web_console/exception_mapper.rb +23 -2
- data/lib/web_console/extensions.rb +10 -23
- data/lib/web_console/injector.rb +5 -3
- data/lib/web_console/interceptor.rb +18 -0
- data/lib/web_console/middleware.rb +6 -9
- data/lib/web_console/{whitelist.rb → permissions.rb} +5 -9
- data/lib/web_console/railtie.rb +19 -4
- data/lib/web_console/request.rb +4 -20
- data/lib/web_console/session.rb +11 -9
- data/lib/web_console/source_location.rb +15 -0
- data/lib/web_console/template.rb +2 -3
- data/lib/web_console/templates/console.js.erb +12 -4
- data/lib/web_console/templates/error_page.js.erb +4 -3
- data/lib/web_console/templates/style.css.erb +182 -34
- data/lib/web_console/testing/fake_middleware.rb +0 -1
- data/lib/web_console/version.rb +1 -1
- data/lib/web_console/whiny_request.rb +4 -4
- metadata +17 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17cae9167c8b3d4e2047cfaf811a37618feccf0061a79d16d25f79aec051dd58
|
4
|
+
data.tar.gz: 97b196f4c170ce48a407cd44a9844d7ee5a7291d34504a73c3356c7b60ede6f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1f7db6881432d184a73388a11750d84b7f14f5d1e0682cec3b916fbc34882b0ed648b381d64c2f552549251fb1281c83786f6960903ea6dc409662c3eec8e1f
|
7
|
+
data.tar.gz: 9e1d58172566c9d9d3bdeb7692c4dc2196e26f4572694b37da22da62d1281749a5b8d592a76b8f3d26b813962d65b1b0077ee515c609b0e03b23c1d2e4f0ea03
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
|
3
3
|
## master (unreleased)
|
4
4
|
|
5
|
+
## 4.0.4
|
6
|
+
|
7
|
+
* [fb483743](https://github.com/rails/web-console/commit/fb483743a6a2a4168cdc0b2e03f48fc393991b73) Fix a crash on webrick with Rack 2.2.3 ([@gsamokovarov])
|
8
|
+
|
9
|
+
## 4.0.3
|
10
|
+
|
11
|
+
* [#291](https://github.com/rails/web-console/pull/291) Deprecate config.web_console.whitelisted_ips ([@JuanitoFatas])
|
12
|
+
* [#290](https://github.com/rails/web-console/pull/290) Fix Content-Length for rack >= 2.1.0 ([@p8])
|
13
|
+
|
14
|
+
## 4.0.2
|
15
|
+
|
16
|
+
* [#285](https://github.com/rails/web-console/pull/285) Increase timeout on paste ([@celvro])
|
17
|
+
|
18
|
+
## 4.0.1
|
19
|
+
|
20
|
+
* [#279](https://github.com/rails/web-console/pull/279) Fix initial config.web_console.permissions value ([@patorash])
|
21
|
+
|
22
|
+
## 4.0.0
|
23
|
+
|
24
|
+
* [61ce65b5](https://github.com/rails/web-console/commit/61ce65b599f56809de1bd8da6590a80acbd92017) Move to config.web_console.permissions ([@gsamokovarov])
|
25
|
+
* [96127ac1](https://github.com/rails/web-console/commit/96127aac143e1e653fffdc4bb65e1ce0b5ff342d) Introduce Binding#console as an alternative interface ([@gsamokovarov])
|
26
|
+
* [d4591ca5](https://github.com/rails/web-console/commit/d4591ca5396ed15a08818f3da11134852a485b27) Introduce Rails 6 support ([@gsamokovarov])
|
27
|
+
* [f97d8a88](https://github.com/rails/web-console/commit/f97d8a889a38366485e5c5e8985995c19bf61d13) Introduce Ruby 2.6 support ([@gsamokovarov])
|
28
|
+
* [d6deacd9](https://github.com/rails/web-console/commit/d6deacd9d5fcaabf3e3051d6985b53f924f86956) Drop Rails 5 support ([@gsamokovarov])
|
29
|
+
* [90fda878](https://github.com/rails/web-console/commit/90fda8789d402f05647c18f8cdf8e5c3d01692dd) Drop Ruby 2.4 support ([@gsamokovarov])
|
30
|
+
* [#265](https://github.com/rails/web-console/pull/265) Add support for nested exceptions ([@yuki24])
|
31
|
+
|
5
32
|
## 3.7.0
|
6
33
|
|
7
34
|
* [#263](https://github.com/rails/web-console/pull/263) Show binding changes ([@causztic])
|
@@ -138,3 +165,8 @@ go to 3.1.0 instead.
|
|
138
165
|
[@fl0l0u]: https://github.com/fl0l0u
|
139
166
|
[@timomeh]: https://github.com/timomeh
|
140
167
|
[@causztic]: https://github.com/causztic
|
168
|
+
[@yuki24]: https://github.com/yuki24
|
169
|
+
[@patorash]: https://github.com/patorash
|
170
|
+
[@celvro]: https://github.com/celvro
|
171
|
+
[@JuanitoFatas]: https://github.com/JuanitoFatas
|
172
|
+
[@p8]: https://github.com/p8
|
data/README.markdown
CHANGED
@@ -16,7 +16,7 @@ _Web Console_ is a debugging tool for your Ruby on Rails applications.
|
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
19
|
-
Add the following to your `Gemfile
|
19
|
+
Add the following to your `Gemfile`:
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
group :development do
|
@@ -27,8 +27,8 @@ end
|
|
27
27
|
## Usage
|
28
28
|
|
29
29
|
The web console allows you to create an interactive Ruby session in your
|
30
|
-
browser. Those sessions are launched automatically in case of an error
|
31
|
-
|
30
|
+
browser. Those sessions are launched automatically in case of an error and can
|
31
|
+
also be launched manually in any page.
|
32
32
|
|
33
33
|
For example, calling `console` in a view will display a console in the current
|
34
34
|
page in the context of the view binding.
|
@@ -56,30 +56,30 @@ have multiple ones, `WebConsole::DoubleRenderError` will be raised.
|
|
56
56
|
|
57
57
|
## Configuration
|
58
58
|
|
59
|
-
_Web Console_ allows you to execute arbitrary code on the server,
|
60
|
-
|
59
|
+
_Web Console_ allows you to execute arbitrary code on the server. Therefore, be
|
60
|
+
very careful who you give access to.
|
61
61
|
|
62
|
-
### config.web_console.
|
62
|
+
### config.web_console.permissions
|
63
63
|
|
64
64
|
By default, only requests coming from IPv4 and IPv6 localhosts are allowed.
|
65
65
|
|
66
|
-
`config.web_console.
|
66
|
+
`config.web_console.permissions` lets you control which IP's have access to
|
67
67
|
the console.
|
68
68
|
|
69
|
-
You can
|
70
|
-
console with `192.168.0.100
|
69
|
+
You can allow single IP's or whole networks. Say you want to share your
|
70
|
+
console with `192.168.0.100`:
|
71
71
|
|
72
72
|
```ruby
|
73
73
|
class Application < Rails::Application
|
74
|
-
config.web_console.
|
74
|
+
config.web_console.permissions = '192.168.0.100'
|
75
75
|
end
|
76
76
|
```
|
77
77
|
|
78
|
-
If you want to
|
78
|
+
If you want to allow the whole private network:
|
79
79
|
|
80
80
|
```ruby
|
81
81
|
Rails.application.configure do
|
82
|
-
config.web_console.
|
82
|
+
config.web_console.permissions = '192.168.0.0/16'
|
83
83
|
end
|
84
84
|
```
|
85
85
|
|
@@ -88,8 +88,8 @@ case in 2.0.
|
|
88
88
|
|
89
89
|
### config.web_console.whiny_requests
|
90
90
|
|
91
|
-
When a console cannot be shown for a given IP address or content type,
|
92
|
-
messages
|
91
|
+
When a console cannot be shown for a given IP address or content type,
|
92
|
+
messages such as the following is printed in the server logs:
|
93
93
|
|
94
94
|
> Cannot render console from 192.168.1.133! Allowed networks:
|
95
95
|
> 127.0.0.0/127.255.255.255, ::1
|
@@ -104,7 +104,7 @@ end
|
|
104
104
|
|
105
105
|
### config.web_console.template_paths
|
106
106
|
|
107
|
-
If you want to style the console yourself, you can place `style.css` at a
|
107
|
+
If you want to style the console yourself, then you can place `style.css` at a
|
108
108
|
directory pointed by `config.web_console.template_paths`:
|
109
109
|
|
110
110
|
```ruby
|
@@ -119,8 +119,8 @@ may override.
|
|
119
119
|
### config.web_console.mount_point
|
120
120
|
|
121
121
|
Usually the middleware of _Web Console_ is mounted at `/__web_console`.
|
122
|
-
If
|
123
|
-
|
122
|
+
If there is a need to change the path, then you can specify it by
|
123
|
+
`config.web_console.mount_point`:
|
124
124
|
|
125
125
|
```ruby
|
126
126
|
Rails.application.configure do
|
@@ -132,7 +132,7 @@ end
|
|
132
132
|
|
133
133
|
### Where did /console go?
|
134
134
|
|
135
|
-
The remote terminal emulator was extracted in its own gem
|
135
|
+
The remote terminal emulator was extracted in its own gem which is no longer
|
136
136
|
bundled with _Web Console_.
|
137
137
|
|
138
138
|
If you miss this feature, check out [rvt].
|
@@ -140,11 +140,11 @@ If you miss this feature, check out [rvt].
|
|
140
140
|
### Why do I constantly get unavailable session errors?
|
141
141
|
|
142
142
|
All of _Web Console_ sessions are stored in memory. If you happen to run on a
|
143
|
-
multi-process server (like Unicorn) you may
|
143
|
+
multi-process server (like Unicorn), you may encounter unavailable session errors
|
144
144
|
while the server is still running. This is because a request may hit a
|
145
145
|
different worker (process) that doesn't have the desired session in memory.
|
146
146
|
To avoid that, if you use such servers in development, configure them so they
|
147
|
-
|
147
|
+
serve requests only out of one process.
|
148
148
|
|
149
149
|
#### Passenger
|
150
150
|
|
data/lib/web_console.rb
CHANGED
@@ -11,12 +11,14 @@ module WebConsole
|
|
11
11
|
autoload :ExceptionMapper
|
12
12
|
autoload :Session
|
13
13
|
autoload :Injector
|
14
|
+
autoload :Interceptor
|
14
15
|
autoload :Request
|
15
16
|
autoload :WhinyRequest
|
16
|
-
autoload :
|
17
|
+
autoload :Permissions
|
17
18
|
autoload :Template
|
18
19
|
autoload :Middleware
|
19
20
|
autoload :Context
|
21
|
+
autoload :SourceLocation
|
20
22
|
|
21
23
|
autoload_at "web_console/errors" do
|
22
24
|
autoload :Error
|
@@ -8,9 +8,11 @@ module WebConsole
|
|
8
8
|
# return a string and will format exception output.
|
9
9
|
class Evaluator
|
10
10
|
# Cleanses exceptions raised inside #eval.
|
11
|
-
cattr_reader :cleaner
|
12
|
-
|
13
|
-
|
11
|
+
cattr_reader :cleaner, default: begin
|
12
|
+
cleaner = ActiveSupport::BacktraceCleaner.new
|
13
|
+
cleaner.add_silencer { |line| line.start_with?(File.expand_path("..", __FILE__)) }
|
14
|
+
cleaner
|
15
|
+
end
|
14
16
|
|
15
17
|
def initialize(binding = TOPLEVEL_BINDING)
|
16
18
|
@binding = binding
|
@@ -2,9 +2,28 @@
|
|
2
2
|
|
3
3
|
module WebConsole
|
4
4
|
class ExceptionMapper
|
5
|
+
attr_reader :exc
|
6
|
+
|
7
|
+
def self.follow(exc)
|
8
|
+
mappers = [new(exc)]
|
9
|
+
|
10
|
+
while cause = (cause || exc).cause
|
11
|
+
mappers << new(cause)
|
12
|
+
end
|
13
|
+
|
14
|
+
mappers
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_binding(mappers, exception_object_id)
|
18
|
+
mappers.detect do |exception_mapper|
|
19
|
+
exception_mapper.exc.object_id == exception_object_id.to_i
|
20
|
+
end || mappers.first
|
21
|
+
end
|
22
|
+
|
5
23
|
def initialize(exception)
|
6
24
|
@backtrace = exception.backtrace
|
7
25
|
@bindings = exception.bindings
|
26
|
+
@exc = exception
|
8
27
|
end
|
9
28
|
|
10
29
|
def first
|
@@ -22,13 +41,15 @@ module WebConsole
|
|
22
41
|
line = line.to_i
|
23
42
|
|
24
43
|
@bindings.find do |binding|
|
25
|
-
|
44
|
+
source_location = SourceLocation.new(binding)
|
45
|
+
source_location.path == file && source_location.lineno == line
|
26
46
|
end
|
27
47
|
end
|
28
48
|
|
29
49
|
def guess_the_first_application_binding
|
30
50
|
@bindings.find do |binding|
|
31
|
-
|
51
|
+
source_location = SourceLocation.new(binding)
|
52
|
+
source_location.path.to_s.start_with?(Rails.root.to_s)
|
32
53
|
end
|
33
54
|
end
|
34
55
|
end
|
@@ -8,8 +8,8 @@ module Kernel
|
|
8
8
|
# If +binding+ isn't explicitly given it will default to the binding of the
|
9
9
|
# previous frame. E.g. the one that invoked +console+.
|
10
10
|
#
|
11
|
-
# Raises DoubleRenderError if a
|
12
|
-
# detected.
|
11
|
+
# Raises +DoubleRenderError+ if a more than one +console+ invocation per
|
12
|
+
# request is detected.
|
13
13
|
def console(binding = Bindex.current_bindings.second)
|
14
14
|
raise WebConsole::DoubleRenderError if Thread.current[:__web_console_binding]
|
15
15
|
|
@@ -22,26 +22,13 @@ module Kernel
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
Thread.current[:__web_console_exception] = error
|
34
|
-
|
35
|
-
# ActionView::Template::Error bypass ExceptionWrapper original
|
36
|
-
# exception following. The backtrace in the view is generated from
|
37
|
-
# reaching out to original_exception in the view.
|
38
|
-
if error.is_a?(ActionView::Template::Error)
|
39
|
-
Thread.current[:__web_console_exception] = error.cause
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
alias_method :render_exception_without_web_console, :render_exception
|
45
|
-
alias_method :render_exception, :render_exception_with_web_console
|
25
|
+
class Binding
|
26
|
+
# Instructs Web Console to render a console in the current binding, without
|
27
|
+
# the need to unroll the stack.
|
28
|
+
#
|
29
|
+
# Raises +DoubleRenderError+ if a more than one +console+ invocation per
|
30
|
+
# request is detected.
|
31
|
+
def console
|
32
|
+
Kernel.console(self)
|
46
33
|
end
|
47
34
|
end
|
data/lib/web_console/injector.rb
CHANGED
@@ -13,9 +13,11 @@ module WebConsole
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def inject(content)
|
16
|
-
#
|
17
|
-
# the
|
18
|
-
@headers
|
16
|
+
# Set Content-Length header to the size of the current body
|
17
|
+
# + the extra content. Otherwise the response will be truncated.
|
18
|
+
if @headers["Content-Length"]
|
19
|
+
@headers["Content-Length"] = (@body.bytesize + content.bytesize).to_s
|
20
|
+
end
|
19
21
|
|
20
22
|
[
|
21
23
|
if position = @body.rindex("</body>")
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module WebConsole
|
2
|
+
module Interceptor
|
3
|
+
def self.call(request, exception)
|
4
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
5
|
+
error = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).exception
|
6
|
+
|
7
|
+
# Get the original exception if ExceptionWrapper decides to follow it.
|
8
|
+
Thread.current[:__web_console_exception] = error
|
9
|
+
|
10
|
+
# ActionView::Template::Error bypass ExceptionWrapper original
|
11
|
+
# exception following. The backtrace in the view is generated from
|
12
|
+
# reaching out to original_exception in the view.
|
13
|
+
if error.is_a?(ActionView::Template::Error)
|
14
|
+
Thread.current[:__web_console_exception] = error.cause
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,11 +6,8 @@ module WebConsole
|
|
6
6
|
class Middleware
|
7
7
|
TEMPLATES_PATH = File.expand_path("../templates", __FILE__)
|
8
8
|
|
9
|
-
cattr_accessor :mount_point
|
10
|
-
|
11
|
-
|
12
|
-
cattr_accessor :whiny_requests
|
13
|
-
@@whiny_requests = true
|
9
|
+
cattr_accessor :mount_point, default: "/__web_console"
|
10
|
+
cattr_accessor :whiny_requests, default: true
|
14
11
|
|
15
12
|
def initialize(app)
|
16
13
|
@app = app
|
@@ -19,7 +16,7 @@ module WebConsole
|
|
19
16
|
def call(env)
|
20
17
|
app_exception = catch :app_exception do
|
21
18
|
request = create_regular_or_whiny_request(env)
|
22
|
-
return call_app(env) unless request.
|
19
|
+
return call_app(env) unless request.permitted?
|
23
20
|
|
24
21
|
if id = id_for_repl_session_update(request)
|
25
22
|
return update_repl_session(id, request)
|
@@ -27,6 +24,7 @@ module WebConsole
|
|
27
24
|
return change_stack_trace(id, request)
|
28
25
|
end
|
29
26
|
|
27
|
+
|
30
28
|
status, headers, body = call_app(env)
|
31
29
|
|
32
30
|
if (session = Session.from(Thread.current)) && acceptable_content_type?(headers)
|
@@ -54,7 +52,7 @@ module WebConsole
|
|
54
52
|
private
|
55
53
|
|
56
54
|
def acceptable_content_type?(headers)
|
57
|
-
|
55
|
+
headers["Content-Type"].to_s.include?("html")
|
58
56
|
end
|
59
57
|
|
60
58
|
def json_response(opts = {})
|
@@ -66,7 +64,6 @@ module WebConsole
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def json_response_with_session(id, request, opts = {})
|
69
|
-
return respond_with_unacceptable_request unless request.acceptable?
|
70
67
|
return respond_with_unavailable_session(id) unless session = Session.find(id)
|
71
68
|
|
72
69
|
json_response(opts) { yield session }
|
@@ -113,7 +110,7 @@ module WebConsole
|
|
113
110
|
|
114
111
|
def change_stack_trace(id, request)
|
115
112
|
json_response_with_session(id, request) do |session|
|
116
|
-
session.switch_binding_to(request.params[:frame_id])
|
113
|
+
session.switch_binding_to(request.params[:frame_id], request.params[:exception_object_id])
|
117
114
|
|
118
115
|
{ ok: true }
|
119
116
|
end
|
@@ -3,20 +3,16 @@
|
|
3
3
|
require "ipaddr"
|
4
4
|
|
5
5
|
module WebConsole
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# networks.
|
10
|
-
class Whitelist
|
11
|
-
# IPv4 and IPv6 localhost should be always whitelisted.
|
12
|
-
ALWAYS_WHITELISTED_NETWORKS = %w( 127.0.0.0/8 ::1 )
|
6
|
+
class Permissions
|
7
|
+
# IPv4 and IPv6 localhost should be always allowed.
|
8
|
+
ALWAYS_PERMITTED_NETWORKS = %w( 127.0.0.0/8 ::1 )
|
13
9
|
|
14
10
|
def initialize(networks = nil)
|
15
11
|
@networks = normalize_networks(networks).map(&method(:coerce_network_to_ipaddr)).uniq
|
16
12
|
end
|
17
13
|
|
18
14
|
def include?(network)
|
19
|
-
@networks.any? { |
|
15
|
+
@networks.any? { |permission| permission.include?(network.to_s) }
|
20
16
|
rescue IPAddr::InvalidAddressError
|
21
17
|
false
|
22
18
|
end
|
@@ -28,7 +24,7 @@ module WebConsole
|
|
28
24
|
private
|
29
25
|
|
30
26
|
def normalize_networks(networks)
|
31
|
-
Array(networks).concat(
|
27
|
+
Array(networks).concat(ALWAYS_PERMITTED_NETWORKS)
|
32
28
|
end
|
33
29
|
|
34
30
|
def coerce_network_to_ipaddr(network)
|
data/lib/web_console/railtie.rb
CHANGED
@@ -5,11 +5,12 @@ require "rails/railtie"
|
|
5
5
|
module WebConsole
|
6
6
|
class Railtie < ::Rails::Railtie
|
7
7
|
config.web_console = ActiveSupport::OrderedOptions.new
|
8
|
-
config.web_console.whitelisted_ips = %w( 127.0.0.1 ::1 )
|
9
8
|
|
10
9
|
initializer "web_console.initialize" do
|
11
10
|
require "bindex"
|
12
11
|
require "web_console/extensions"
|
12
|
+
|
13
|
+
ActionDispatch::DebugExceptions.register_interceptor(Interceptor)
|
13
14
|
end
|
14
15
|
|
15
16
|
initializer "web_console.development_only" do
|
@@ -50,9 +51,23 @@ module WebConsole
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
initializer "web_console.
|
54
|
-
|
55
|
-
|
54
|
+
initializer "web_console.permissions" do
|
55
|
+
permissions = web_console_permissions
|
56
|
+
Request.permissions = Permissions.new(permissions)
|
57
|
+
end
|
58
|
+
|
59
|
+
def web_console_permissions
|
60
|
+
case
|
61
|
+
when config.web_console.permissions
|
62
|
+
config.web_console.permissions
|
63
|
+
when config.web_console.allowed_ips
|
64
|
+
config.web_console.allowed_ips
|
65
|
+
when config.web_console.whitelisted_ips
|
66
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
67
|
+
The config.web_console.whitelisted_ips is deprecated and will be ignored in future release of web_console.
|
68
|
+
Please use config.web_console.allowed_ips instead.
|
69
|
+
MSG
|
70
|
+
config.web_console.whitelisted_ips
|
56
71
|
end
|
57
72
|
end
|
58
73
|
|
data/lib/web_console/request.rb
CHANGED
@@ -1,35 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module WebConsole
|
4
|
-
# Web Console tailored request object.
|
5
4
|
class Request < ActionDispatch::Request
|
6
|
-
|
7
|
-
cattr_accessor :whitelisted_ips
|
8
|
-
@@whitelisted_ips = Whitelist.new
|
5
|
+
cattr_accessor :permissions, default: Permissions.new
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# Returns whether a request came from a whitelisted IP.
|
14
|
-
#
|
15
|
-
# For a request to hit Web Console features, it needs to come from a white
|
16
|
-
# listed IP.
|
17
|
-
def from_whitelisted_ip?
|
18
|
-
whitelisted_ips.include?(strict_remote_ip)
|
7
|
+
def permitted?
|
8
|
+
permissions.include?(strict_remote_ip)
|
19
9
|
end
|
20
10
|
|
21
|
-
# Determines the remote IP using our much stricter whitelist.
|
22
11
|
def strict_remote_ip
|
23
|
-
GetSecureIp.new(self,
|
12
|
+
GetSecureIp.new(self, permissions).to_s
|
24
13
|
rescue ActionDispatch::RemoteIp::IpSpoofAttackError
|
25
14
|
"[Spoofed]"
|
26
15
|
end
|
27
16
|
|
28
|
-
# Returns whether the request is acceptable.
|
29
|
-
def acceptable?
|
30
|
-
xhr? && accepts.any? { |mime| Mime[:web_console_v2] == mime }
|
31
|
-
end
|
32
|
-
|
33
17
|
private
|
34
18
|
|
35
19
|
class GetSecureIp < ActionDispatch::RemoteIp::GetIp
|
data/lib/web_console/session.rb
CHANGED
@@ -11,8 +11,7 @@ module WebConsole
|
|
11
11
|
# error pages only, as currently, this is the only client that needs to do
|
12
12
|
# that.
|
13
13
|
class Session
|
14
|
-
cattr_reader :inmemory_storage
|
15
|
-
@@inmemory_storage = {}
|
14
|
+
cattr_reader :inmemory_storage, default: {}
|
16
15
|
|
17
16
|
class << self
|
18
17
|
# Finds a persisted session in memory by its id.
|
@@ -32,9 +31,9 @@ module WebConsole
|
|
32
31
|
# storage.
|
33
32
|
def from(storage)
|
34
33
|
if exc = storage[:__web_console_exception]
|
35
|
-
new(ExceptionMapper.
|
34
|
+
new(ExceptionMapper.follow(exc))
|
36
35
|
elsif binding = storage[:__web_console_binding]
|
37
|
-
new([binding])
|
36
|
+
new([[binding]])
|
38
37
|
end
|
39
38
|
end
|
40
39
|
end
|
@@ -42,10 +41,11 @@ module WebConsole
|
|
42
41
|
# An unique identifier for every REPL.
|
43
42
|
attr_reader :id
|
44
43
|
|
45
|
-
def initialize(
|
44
|
+
def initialize(exception_mappers)
|
46
45
|
@id = SecureRandom.hex(16)
|
47
|
-
|
48
|
-
@
|
46
|
+
|
47
|
+
@exception_mappers = exception_mappers
|
48
|
+
@evaluator = Evaluator.new(@current_binding = exception_mappers.first.first)
|
49
49
|
|
50
50
|
store_into_memory
|
51
51
|
end
|
@@ -60,8 +60,10 @@ module WebConsole
|
|
60
60
|
# Switches the current binding to the one at specified +index+.
|
61
61
|
#
|
62
62
|
# Returns nothing.
|
63
|
-
def switch_binding_to(index)
|
64
|
-
|
63
|
+
def switch_binding_to(index, exception_object_id)
|
64
|
+
bindings = ExceptionMapper.find_binding(@exception_mappers, exception_object_id)
|
65
|
+
|
66
|
+
@evaluator = Evaluator.new(@current_binding = bindings[index.to_i])
|
65
67
|
end
|
66
68
|
|
67
69
|
# Returns context of the current binding
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SourceLocation
|
4
|
+
def initialize(binding)
|
5
|
+
@binding = binding
|
6
|
+
end
|
7
|
+
|
8
|
+
if RUBY_VERSION >= "2.6"
|
9
|
+
def path() @binding.source_location.first end
|
10
|
+
def lineno() @binding.source_location.last end
|
11
|
+
else
|
12
|
+
def path() @binding.eval("__FILE__") end
|
13
|
+
def lineno() @binding.eval("__LINE__") end
|
14
|
+
end
|
15
|
+
end
|
data/lib/web_console/template.rb
CHANGED
@@ -7,8 +7,7 @@ module WebConsole
|
|
7
7
|
# Rails error pages.
|
8
8
|
class Template
|
9
9
|
# Lets you customize the default templates folder location.
|
10
|
-
cattr_accessor :template_paths
|
11
|
-
@@template_paths = [ File.expand_path("../templates", __FILE__) ]
|
10
|
+
cattr_accessor :template_paths, default: [ File.expand_path("../templates", __FILE__) ]
|
12
11
|
|
13
12
|
def initialize(env, session)
|
14
13
|
@env = env
|
@@ -18,7 +17,7 @@ module WebConsole
|
|
18
17
|
|
19
18
|
# Render a template (inferred from +template_paths+) as a plain string.
|
20
19
|
def render(template)
|
21
|
-
view = View.new(template_paths, instance_values)
|
20
|
+
view = View.new(ActionView::LookupContext.new(template_paths), instance_values)
|
22
21
|
view.render(template: template, layout: false)
|
23
22
|
end
|
24
23
|
end
|
@@ -380,8 +380,13 @@ REPLConsole.prototype.install = function(container) {
|
|
380
380
|
var clientHeightStart = consoleOuter.clientHeight;
|
381
381
|
|
382
382
|
var doDrag = function(e) {
|
383
|
-
|
383
|
+
var height = startHeight + startY - e.clientY;
|
384
384
|
consoleOuter.scrollTop = scrollTopStart + (clientHeightStart - consoleOuter.clientHeight);
|
385
|
+
if (height > document.documentElement.clientHeight) {
|
386
|
+
container.style.height = document.documentElement.clientHeight;
|
387
|
+
} else {
|
388
|
+
container.style.height = height + 'px';
|
389
|
+
}
|
385
390
|
shiftConsoleActions();
|
386
391
|
};
|
387
392
|
|
@@ -755,7 +760,7 @@ REPLConsole.prototype.onKeyDown = function(ev) {
|
|
755
760
|
_this.addToInput(_this.clipboard.value);
|
756
761
|
_this.clipboard.value = "";
|
757
762
|
_this.clipboard.blur();
|
758
|
-
},
|
763
|
+
}, 100);
|
759
764
|
}
|
760
765
|
}
|
761
766
|
|
@@ -871,10 +876,14 @@ REPLConsole.prototype.scrollToBottom = function() {
|
|
871
876
|
};
|
872
877
|
|
873
878
|
// Change the binding of the console.
|
874
|
-
REPLConsole.prototype.switchBindingTo = function(frameId, callback) {
|
879
|
+
REPLConsole.prototype.switchBindingTo = function(frameId, exceptionObjectId, callback) {
|
875
880
|
var url = this.getSessionUrl('trace');
|
876
881
|
var params = "frame_id=" + encodeURIComponent(frameId);
|
877
882
|
|
883
|
+
if (exceptionObjectId) {
|
884
|
+
params = params + "&exception_object_id=" + encodeURIComponent(exceptionObjectId);
|
885
|
+
}
|
886
|
+
|
878
887
|
var _this = this;
|
879
888
|
postRequest(url, params, function() {
|
880
889
|
var text = "Context has changed to: " + callback();
|
@@ -915,7 +924,6 @@ REPLConsole.request = function request(method, url, params, callback) {
|
|
915
924
|
xhr.open(method, url, true);
|
916
925
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
917
926
|
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
918
|
-
xhr.setRequestHeader("Accept", "<%= Mime[:web_console_v2] %>");
|
919
927
|
xhr.send(params);
|
920
928
|
|
921
929
|
xhr.onreadystatechange = function() {
|
@@ -8,9 +8,10 @@ for (var i = 0; i < traceFrames.length; i++) {
|
|
8
8
|
e.preventDefault();
|
9
9
|
var target = e.target;
|
10
10
|
var frameId = target.dataset.frameId;
|
11
|
+
var exceptionObjectId = target.dataset.exceptionObjectId;
|
11
12
|
|
12
13
|
// Change the binding of the console.
|
13
|
-
changeBinding(frameId, function() {
|
14
|
+
changeBinding(frameId, exceptionObjectId, function() {
|
14
15
|
// Rails already handles toggling the select class
|
15
16
|
selectedFrame = target;
|
16
17
|
return target.innerHTML;
|
@@ -22,8 +23,8 @@ for (var i = 0; i < traceFrames.length; i++) {
|
|
22
23
|
}
|
23
24
|
|
24
25
|
// Change the binding of the current session and prompt the user.
|
25
|
-
function changeBinding(frameId, callback) {
|
26
|
-
REPLConsole.currentSession.switchBindingTo(frameId, callback);
|
26
|
+
function changeBinding(frameId, exceptionObjectId, callback) {
|
27
|
+
REPLConsole.currentSession.switchBindingTo(frameId, exceptionObjectId, callback);
|
27
28
|
}
|
28
29
|
|
29
30
|
function changeSourceExtract(frameId) {
|
@@ -1,34 +1,182 @@
|
|
1
|
-
.console .pos-absolute {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
.console .
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
.console .
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
.console .
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
.console .
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
.console .
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
.console .
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
.console
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
1
|
+
.console .pos-absolute {
|
2
|
+
position: absolute;
|
3
|
+
}
|
4
|
+
|
5
|
+
.console .pos-fixed {
|
6
|
+
position: fixed;
|
7
|
+
}
|
8
|
+
|
9
|
+
.console .pos-right {
|
10
|
+
right: 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
.console .border-box {
|
14
|
+
box-sizing: border-box;
|
15
|
+
}
|
16
|
+
|
17
|
+
.console .layer {
|
18
|
+
width: 100%;
|
19
|
+
height: 100%;
|
20
|
+
}
|
21
|
+
|
22
|
+
.console .layer.console-outer {
|
23
|
+
z-index: 1;
|
24
|
+
}
|
25
|
+
|
26
|
+
.console .layer.resizer {
|
27
|
+
z-index: 2;
|
28
|
+
}
|
29
|
+
|
30
|
+
.console {
|
31
|
+
position: fixed;
|
32
|
+
left: 0;
|
33
|
+
bottom: 0;
|
34
|
+
width: 100%;
|
35
|
+
height: 148px;
|
36
|
+
padding: 0;
|
37
|
+
margin: 0;
|
38
|
+
background: none repeat scroll 0% 0% #333;
|
39
|
+
z-index: 9999;
|
40
|
+
}
|
41
|
+
|
42
|
+
.console .console-outer {
|
43
|
+
overflow: auto;
|
44
|
+
padding-top: 4px;
|
45
|
+
}
|
46
|
+
|
47
|
+
.console .console-inner {
|
48
|
+
font-family: monospace;
|
49
|
+
font-size: 11px;
|
50
|
+
width: 100%;
|
51
|
+
height: 100%;
|
52
|
+
overflow: none;
|
53
|
+
background: #333;
|
54
|
+
}
|
55
|
+
|
56
|
+
.console .console-prompt-box {
|
57
|
+
color: #fff;
|
58
|
+
}
|
59
|
+
|
60
|
+
.console .console-message {
|
61
|
+
color: #1ad027;
|
62
|
+
margin: 0;
|
63
|
+
border: 0;
|
64
|
+
white-space: pre-wrap;
|
65
|
+
background-color: #333;
|
66
|
+
padding: 0;
|
67
|
+
}
|
68
|
+
|
69
|
+
.console .console-message.error-message {
|
70
|
+
color: #fc9;
|
71
|
+
}
|
72
|
+
|
73
|
+
.console .console-message.notification-message {
|
74
|
+
color: #99f;
|
75
|
+
}
|
76
|
+
|
77
|
+
.console .console-message.auto-complete {
|
78
|
+
word-break: break-all;
|
79
|
+
}
|
80
|
+
|
81
|
+
.console .console-message.auto-complete .keyword {
|
82
|
+
margin-right: 11px;
|
83
|
+
}
|
84
|
+
|
85
|
+
.console .console-message.auto-complete .keyword.selected {
|
86
|
+
background: #fff;
|
87
|
+
color: #000;
|
88
|
+
}
|
89
|
+
|
90
|
+
.console .console-message.auto-complete .hidden {
|
91
|
+
display: none;
|
92
|
+
}
|
93
|
+
|
94
|
+
.console .console-message.auto-complete .trimmed {
|
95
|
+
display: none;
|
96
|
+
}
|
97
|
+
|
98
|
+
.console .console-hint {
|
99
|
+
color: #096;
|
100
|
+
}
|
101
|
+
|
102
|
+
.console .console-focus .console-cursor {
|
103
|
+
background: #fefefe;
|
104
|
+
color: #333;
|
105
|
+
font-weight: bold;
|
106
|
+
}
|
107
|
+
|
108
|
+
.console .resizer {
|
109
|
+
background: #333;
|
110
|
+
width: 100%;
|
111
|
+
height: 4px;
|
112
|
+
cursor: ns-resize;
|
113
|
+
}
|
114
|
+
|
115
|
+
.console .console-actions {
|
116
|
+
padding-right: 3px;
|
117
|
+
}
|
118
|
+
|
119
|
+
.console .console-actions .button {
|
120
|
+
float: left;
|
121
|
+
}
|
122
|
+
|
123
|
+
.console .button {
|
124
|
+
cursor: pointer;
|
125
|
+
border-radius: 1px;
|
126
|
+
font-family: monospace;
|
127
|
+
font-size: 13px;
|
128
|
+
width: 14px;
|
129
|
+
height: 14px;
|
130
|
+
line-height: 14px;
|
131
|
+
text-align: center;
|
132
|
+
color: #ccc;
|
133
|
+
}
|
134
|
+
|
135
|
+
.console .button:hover {
|
136
|
+
background: #666;
|
137
|
+
color: #fff;
|
138
|
+
}
|
139
|
+
|
140
|
+
.console .button.close-button:hover {
|
141
|
+
background: #966;
|
142
|
+
}
|
143
|
+
|
144
|
+
.console .clipboard {
|
145
|
+
height: 0px;
|
146
|
+
padding: 0px;
|
147
|
+
margin: 0px;
|
148
|
+
width: 0px;
|
149
|
+
margin-left: -1000px;
|
150
|
+
}
|
151
|
+
|
152
|
+
.console .console-prompt-label {
|
153
|
+
display: inline;
|
154
|
+
color: #fff;
|
155
|
+
background: none repeat scroll 0% 0% #333;
|
156
|
+
border: 0;
|
157
|
+
padding: 0;
|
158
|
+
}
|
159
|
+
|
160
|
+
.console .console-prompt-display {
|
161
|
+
display: inline;
|
162
|
+
color: #fff;
|
163
|
+
background: none repeat scroll 0% 0% #333;
|
164
|
+
border: 0;
|
165
|
+
padding: 0;
|
166
|
+
}
|
167
|
+
|
168
|
+
.console.full-screen {
|
169
|
+
height: 100%;
|
170
|
+
}
|
171
|
+
|
172
|
+
.console.full-screen .console-outer {
|
173
|
+
padding-top: 3px;
|
174
|
+
}
|
175
|
+
|
176
|
+
.console.full-screen .resizer {
|
177
|
+
display: none;
|
178
|
+
}
|
179
|
+
|
180
|
+
.console.full-screen .close-button {
|
181
|
+
display: none;
|
182
|
+
}
|
data/lib/web_console/version.rb
CHANGED
@@ -3,13 +3,13 @@
|
|
3
3
|
module WebConsole
|
4
4
|
# Noisy wrapper around +Request+.
|
5
5
|
#
|
6
|
-
# If any calls to +
|
6
|
+
# If any calls to +permitted?+ and +acceptable_content_type?+
|
7
7
|
# return false, an info log message will be displayed in users' logs.
|
8
8
|
class WhinyRequest < SimpleDelegator
|
9
|
-
def
|
10
|
-
whine_unless request.
|
9
|
+
def permitted?
|
10
|
+
whine_unless request.permitted? do
|
11
11
|
"Cannot render console from #{request.strict_remote_ip}! " \
|
12
|
-
"Allowed networks: #{request.
|
12
|
+
"Allowed networks: #{request.permissions}"
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: web-console
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Somerville
|
8
8
|
- Genadi Samokovarov
|
9
9
|
- Guillermo Iguaran
|
10
10
|
- Ryan Dao
|
11
|
-
autorequire:
|
11
|
+
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2020-07-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|
@@ -19,42 +19,42 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 6.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 6.0.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: activemodel
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
34
|
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
36
|
+
version: 6.0.0
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 6.0.0
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
45
|
name: actionview
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
48
|
- - ">="
|
49
49
|
- !ruby/object:Gem::Version
|
50
|
-
version:
|
50
|
+
version: 6.0.0
|
51
51
|
type: :runtime
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
55
|
- - ">="
|
56
56
|
- !ruby/object:Gem::Version
|
57
|
-
version:
|
57
|
+
version: 6.0.0
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bindex
|
60
60
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
- - ">="
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: 0.4.0
|
72
|
-
description:
|
72
|
+
description:
|
73
73
|
email:
|
74
74
|
- charlie@charliesomerville.com
|
75
75
|
- gsamokovarov@gmail.com
|
@@ -91,11 +91,14 @@ files:
|
|
91
91
|
- lib/web_console/exception_mapper.rb
|
92
92
|
- lib/web_console/extensions.rb
|
93
93
|
- lib/web_console/injector.rb
|
94
|
+
- lib/web_console/interceptor.rb
|
94
95
|
- lib/web_console/locales/en.yml
|
95
96
|
- lib/web_console/middleware.rb
|
97
|
+
- lib/web_console/permissions.rb
|
96
98
|
- lib/web_console/railtie.rb
|
97
99
|
- lib/web_console/request.rb
|
98
100
|
- lib/web_console/session.rb
|
101
|
+
- lib/web_console/source_location.rb
|
99
102
|
- lib/web_console/tasks/extensions.rake
|
100
103
|
- lib/web_console/tasks/templates.rake
|
101
104
|
- lib/web_console/template.rb
|
@@ -116,12 +119,11 @@ files:
|
|
116
119
|
- lib/web_console/version.rb
|
117
120
|
- lib/web_console/view.rb
|
118
121
|
- lib/web_console/whiny_request.rb
|
119
|
-
- lib/web_console/whitelist.rb
|
120
122
|
homepage: https://github.com/rails/web-console
|
121
123
|
licenses:
|
122
124
|
- MIT
|
123
125
|
metadata: {}
|
124
|
-
post_install_message:
|
126
|
+
post_install_message:
|
125
127
|
rdoc_options: []
|
126
128
|
require_paths:
|
127
129
|
- lib
|
@@ -129,16 +131,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
131
|
requirements:
|
130
132
|
- - ">="
|
131
133
|
- !ruby/object:Gem::Version
|
132
|
-
version: 2.
|
134
|
+
version: '2.5'
|
133
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
136
|
requirements:
|
135
137
|
- - ">="
|
136
138
|
- !ruby/object:Gem::Version
|
137
139
|
version: '0'
|
138
140
|
requirements: []
|
139
|
-
|
140
|
-
|
141
|
-
signing_key:
|
141
|
+
rubygems_version: 3.0.3
|
142
|
+
signing_key:
|
142
143
|
specification_version: 4
|
143
144
|
summary: A debugging tool for your Ruby on Rails applications.
|
144
145
|
test_files: []
|