rack-webconsole 0.0.4 → 0.0.5

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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-webconsole (0.0.4)
4
+ rack-webconsole (0.0.5)
5
5
  json
6
6
  rack
7
7
 
data/Readme.md CHANGED
@@ -14,11 +14,15 @@ works. Without any configuration.
14
14
 
15
15
  Tested with MRI versions 1.8.7, 1.9.2, ruby-head, and JRuby 1.6.3.
16
16
 
17
+ **SECURITY NOTE**: From version v0.0.5 rack-webconsole uses a token system to
18
+ protect against cross-site request forgery.
19
+
17
20
  ##Resources
18
21
 
19
22
  * [Example video](http://youtu.be/yKK5J01Dqts?hd=1)
20
23
  * [Documentation](http://rubydoc.info/github/codegram/rack-webconsole)
21
24
 
25
+
22
26
  ##Install
23
27
 
24
28
  In your Gemfile:
@@ -33,7 +37,6 @@ some configuration file):
33
37
 
34
38
  Rack::Webconsole.inject_jquery = true
35
39
 
36
-
37
40
  ##Usage with Rails 3
38
41
 
39
42
  If you are using Rails 3, you have no further steps to do. It works! To give
@@ -94,3 +97,4 @@ You can also build the documentation with the following command:
94
97
  Copyright (c) 2011 Codegram. See LICENSE for details.
95
98
 
96
99
 
100
+
@@ -32,8 +32,12 @@ module Rack
32
32
  response_body = response.first
33
33
  end
34
34
 
35
+ # Regenerate the security token
36
+ Webconsole::Repl.reset_token
37
+
35
38
  # Inject the html, css and js code to the view
36
39
  response_body.gsub!('</body>', "#{code}</body>")
40
+
37
41
  headers['Content-Length'] = (response_body.length + 2).to_s
38
42
 
39
43
  [status, headers, [response_body]]
@@ -42,9 +46,14 @@ module Rack
42
46
  # Returns a string with all the HTML, CSS and JavaScript code needed for
43
47
  # the view.
44
48
  #
49
+ # It puts the security token inside the JavaScript to make AJAX calls
50
+ # secure.
51
+ #
45
52
  # @return [String] the injectable code.
46
53
  def code
47
- html_code << css_code << js_code
54
+ html_code <<
55
+ css_code <<
56
+ js_code.gsub('TOKEN', Webconsole::Repl.token)
48
57
  end
49
58
 
50
59
  private
@@ -9,6 +9,21 @@ module Rack
9
9
  # variables and stores them in an instance variable for further retrieval.
10
10
  #
11
11
  class Repl
12
+ @@token = nil
13
+ class << self
14
+ # Returns the autogenerated security token
15
+ #
16
+ # @return [String] the autogenerated token
17
+ def token
18
+ @@token
19
+ end
20
+
21
+ # Regenerates the token.
22
+ def reset_token
23
+ @@token = Digest::SHA1.hexdigest("#{rand(36**8)}#{Time.now}")[4..20]
24
+ end
25
+ end
26
+
12
27
  # Honor the Rack contract by saving the passed Rack application in an ivar.
13
28
  #
14
29
  # @param [Rack::Application] app the previous Rack application in the
@@ -30,9 +45,10 @@ module Rack
30
45
  status, headers, response = @app.call(env)
31
46
 
32
47
  req = Rack::Request.new(env)
33
-
34
48
  params = req.params
35
49
 
50
+ return [status, headers, response] unless check_legitimate(req)
51
+
36
52
  result = begin
37
53
  $sandbox ||= Sandbox.new
38
54
 
@@ -59,6 +75,12 @@ module Rack
59
75
  headers['Content-Length'] = response_body.length.to_s
60
76
  [200, headers, [response_body]]
61
77
  end
78
+
79
+ private
80
+
81
+ def check_legitimate(req)
82
+ req.post? && !Repl.token.nil? && req.params['token'] == Repl.token
83
+ end
62
84
  end
63
85
  end
64
86
  end
@@ -2,6 +2,6 @@
2
2
  module Rack
3
3
  class Webconsole
4
4
  # rack-webconsole version number.
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.5"
6
6
  end
7
7
  end
data/public/webconsole.js CHANGED
@@ -33,7 +33,7 @@ $("#console form input").keyup(function(event) {
33
33
  url: '/webconsole',
34
34
  type: 'POST',
35
35
  dataType: 'json',
36
- data: ({query: query}),
36
+ data: ({query: query, token: "TOKEN"}),
37
37
  success: function (data) {
38
38
  var q = "<div>>> " + query.escapeHTML() + "</div>";
39
39
  var r = "<div>=> " + data.result.escapeHTML() + "</div>";
@@ -15,7 +15,8 @@ module Rack
15
15
  it 'evaluates the :query param in a sandbox and returns the result' do
16
16
  @app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
17
17
  env = {}
18
- request = OpenStruct.new(:params => {'query' => 'a = 4; a * 2'})
18
+ Webconsole::Repl.stubs(:token).returns('abc')
19
+ request = OpenStruct.new(:params => {'query' => 'a = 4; a * 2', 'token' => 'abc'}, :post? => true)
19
20
  Rack::Request.stubs(:new).returns request
20
21
 
21
22
  @repl = Webconsole::Repl.new(@app)
@@ -28,13 +29,14 @@ module Rack
28
29
  it 'maintains local state in subsequent calls thanks to an evil global variable' do
29
30
  @app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
30
31
  env = {}
31
- request = OpenStruct.new(:params => {'query' => 'a = 4'})
32
+ Webconsole::Repl.stubs(:token).returns('abc')
33
+ request = OpenStruct.new(:params => {'query' => 'a = 4', 'token' => 'abc'}, :post? => true)
32
34
  Rack::Request.stubs(:new).returns request
33
35
  @repl = Webconsole::Repl.new(@app)
34
36
 
35
37
  @repl.call(env) # call 1 sets a to 4
36
38
 
37
- request = OpenStruct.new(:params => {'query' => 'a * 8'})
39
+ request = OpenStruct.new(:params => {'query' => 'a * 8', 'token' => 'abc'}, :post? => true)
38
40
  Rack::Request.stubs(:new).returns request
39
41
 
40
42
  response = @repl.call(env).last.first # call 2 retrieves a and multiplies it by 8
@@ -46,7 +48,8 @@ module Rack
46
48
  it "returns any found errors prepended with 'Error:'" do
47
49
  @app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
48
50
  env = {}
49
- request = OpenStruct.new(:params => {'query' => 'unknown_method'})
51
+ Webconsole::Repl.stubs(:token).returns('abc')
52
+ request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'abc'}, :post? => true)
50
53
  Rack::Request.stubs(:new).returns request
51
54
  @repl = Webconsole::Repl.new(@app)
52
55
 
@@ -54,6 +57,41 @@ module Rack
54
57
 
55
58
  JSON.parse(response)['result'].must_match /Error:/
56
59
  end
60
+
61
+ it 'rejects non-post requests' do
62
+ @app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
63
+ env = {}
64
+ Webconsole::Repl.stubs(:token).returns('abc')
65
+ request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'abc'}, :post? => false)
66
+ Rack::Request.stubs(:new).returns request
67
+ @repl = Webconsole::Repl.new(@app)
68
+
69
+ $sandbox.expects(:instance_eval).never
70
+
71
+ @repl.call(env).must_equal @app.call(env)
72
+ end
73
+
74
+ it 'rejects requests with invalid token' do
75
+ @app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
76
+ env = {}
77
+ Webconsole::Repl.stubs(:token).returns('abc')
78
+ request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'cba'}, :post? => true)
79
+ Rack::Request.stubs(:new).returns request
80
+ @repl = Webconsole::Repl.new(@app)
81
+
82
+ $sandbox.expects(:instance_eval).never
83
+
84
+ @repl.call(env).must_equal @app.call(env)
85
+ end
86
+ end
87
+
88
+ describe 'class methods' do
89
+ describe '#reset_token and #token' do
90
+ it 'returns the security token' do
91
+ Webconsole::Repl.reset_token
92
+ Webconsole::Repl.token.must_be_kind_of String
93
+ end
94
+ end
57
95
  end
58
96
 
59
97
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-webconsole
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -16,7 +16,7 @@ default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rack
19
- requirement: &2151839200 !ruby/object:Gem::Requirement
19
+ requirement: &2151839300 !ruby/object:Gem::Requirement
20
20
  none: false
21
21
  requirements:
22
22
  - - ! '>='
@@ -24,10 +24,10 @@ dependencies:
24
24
  version: '0'
25
25
  type: :runtime
26
26
  prerelease: false
27
- version_requirements: *2151839200
27
+ version_requirements: *2151839300
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: json
30
- requirement: &2151838520 !ruby/object:Gem::Requirement
30
+ requirement: &2151838600 !ruby/object:Gem::Requirement
31
31
  none: false
32
32
  requirements:
33
33
  - - ! '>='
@@ -35,10 +35,10 @@ dependencies:
35
35
  version: '0'
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *2151838520
38
+ version_requirements: *2151838600
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: minitest
41
- requirement: &2151837980 !ruby/object:Gem::Requirement
41
+ requirement: &2151838060 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ! '>='
@@ -46,10 +46,10 @@ dependencies:
46
46
  version: '0'
47
47
  type: :development
48
48
  prerelease: false
49
- version_requirements: *2151837980
49
+ version_requirements: *2151838060
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: purdytest
52
- requirement: &2151837320 !ruby/object:Gem::Requirement
52
+ requirement: &2151837520 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ! '>='
@@ -57,10 +57,10 @@ dependencies:
57
57
  version: '0'
58
58
  type: :development
59
59
  prerelease: false
60
- version_requirements: *2151837320
60
+ version_requirements: *2151837520
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: mocha
63
- requirement: &2151836600 !ruby/object:Gem::Requirement
63
+ requirement: &2151836720 !ruby/object:Gem::Requirement
64
64
  none: false
65
65
  requirements:
66
66
  - - ! '>='
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: '0'
69
69
  type: :development
70
70
  prerelease: false
71
- version_requirements: *2151836600
71
+ version_requirements: *2151836720
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: yard
74
- requirement: &2151835980 !ruby/object:Gem::Requirement
74
+ requirement: &2151836080 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ! '>='
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: '0'
80
80
  type: :development
81
81
  prerelease: false
82
- version_requirements: *2151835980
82
+ version_requirements: *2151836080
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: bluecloth
85
- requirement: &2151835360 !ruby/object:Gem::Requirement
85
+ requirement: &2151835520 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ! '>='
@@ -90,10 +90,10 @@ dependencies:
90
90
  version: '0'
91
91
  type: :development
92
92
  prerelease: false
93
- version_requirements: *2151835360
93
+ version_requirements: *2151835520
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: rake
96
- requirement: &2151834480 !ruby/object:Gem::Requirement
96
+ requirement: &2151834600 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ! '>='
@@ -101,7 +101,7 @@ dependencies:
101
101
  version: '0'
102
102
  type: :development
103
103
  prerelease: false
104
- version_requirements: *2151834480
104
+ version_requirements: *2151834600
105
105
  description: Rack-based console inside your web applications
106
106
  email:
107
107
  - info@codegram.com
@@ -150,7 +150,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
150
  version: '0'
151
151
  segments:
152
152
  - 0
153
- hash: 1299359098189414051
153
+ hash: -1999695551081588916
154
154
  required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  none: false
156
156
  requirements:
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  version: '0'
160
160
  segments:
161
161
  - 0
162
- hash: 1299359098189414051
162
+ hash: -1999695551081588916
163
163
  requirements: []
164
164
  rubyforge_project: rack-webconsole
165
165
  rubygems_version: 1.6.2