rack-webconsole 0.0.4 → 0.0.5

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