sandrbox 0.0.2
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/.document +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +45 -0
- data/Rakefile +44 -0
- data/Sandrbox.gemspec +78 -0
- data/VERSION +1 -0
- data/lib/sandrbox/config.rb +57 -0
- data/lib/sandrbox/options/option.rb +70 -0
- data/lib/sandrbox/response.rb +101 -0
- data/lib/sandrbox/value.rb +90 -0
- data/lib/sandrbox.rb +20 -0
- data/spec/sandrbox/config_spec.rb +5 -0
- data/spec/sandrbox/response_spec.rb +105 -0
- data/spec/sandrbox/value_spec.rb +0 -0
- data/spec/sandrbox_spec.rb +85 -0
- data/spec/spec_helper.rb +16 -0
- metadata +197 -0
data/.document
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
github-markup (0.7.1)
|
7
|
+
jeweler (1.8.3)
|
8
|
+
bundler (~> 1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
rdoc
|
12
|
+
json (1.6.6)
|
13
|
+
metaclass (0.0.1)
|
14
|
+
mocha (0.10.5)
|
15
|
+
metaclass (~> 0.0.1)
|
16
|
+
rake (0.9.2.2)
|
17
|
+
rdoc (3.12)
|
18
|
+
json (~> 1.4)
|
19
|
+
redcarpet (1.17.2)
|
20
|
+
rspec (2.9.0)
|
21
|
+
rspec-core (~> 2.9.0)
|
22
|
+
rspec-expectations (~> 2.9.0)
|
23
|
+
rspec-mocks (~> 2.9.0)
|
24
|
+
rspec-core (2.9.0)
|
25
|
+
rspec-expectations (2.9.0)
|
26
|
+
diff-lcs (~> 1.1.3)
|
27
|
+
rspec-mocks (2.9.0)
|
28
|
+
yard (0.7.5)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
bundler
|
35
|
+
github-markup
|
36
|
+
jeweler
|
37
|
+
mocha
|
38
|
+
rake
|
39
|
+
redcarpet (= 1.17.2)
|
40
|
+
rspec
|
41
|
+
yard
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Josh Symonds
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Sandrbox
|
2
|
+
|
3
|
+
Sandrbox allows you to execute arbitrary Ruby code while being assured it won't destroy your life (or your server). It's intended to be small, fast, and secure. I built it to replace TryRuby's really slow Ruby sandbox, since I wanted something faster.
|
4
|
+
|
5
|
+
Note that while I made a concentrated effort to make this secure, it's still possible it's not. I wouldn't run this code outside of a secure prison of some sort, and definitely not on anything connected to a database whose data you care about. (I intend to run this on Heroku without a database.)
|
6
|
+
|
7
|
+
## Set It Up
|
8
|
+
|
9
|
+
I automatically remove all the bad methods and classes I can think of. But maybe you need more:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Sandrbox.configure do |config|
|
13
|
+
config.bad_classes << [:Rails]
|
14
|
+
config.bad_classes << [:ActiveRecord]
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
## How To Use It
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'sandrbox'
|
22
|
+
|
23
|
+
Sandrbox.perform(['a = 1']).output # => [1]
|
24
|
+
Sandrbox.perform(['a = 1', 'a = a + a', 'a ** a']).output # => [1, 2, 4]
|
25
|
+
Sandrbox.perform(['a = 1', 'a = a + a', 'a ** b']).output # => [1, 2, "NameError: undefined local variable or method `b' for main:Object"]
|
26
|
+
|
27
|
+
Sandrbox.perform(['`rm -rf /`']).output # => ["NameError: undefined local variable or method ``' for Kernel:Module"]
|
28
|
+
Sandrbox.perform(['exec("rm -rf /")']).output # => ["NameError: undefined local variable or method `exec' for main:Object"]
|
29
|
+
Sandrbox.perform(['Kernel.exec("rm -rf /")']).output # => ["NameError: undefined local variable or method `exec' for Kernel:Module"]
|
30
|
+
|
31
|
+
Sandrbox.perform(['require "open3"']).output # => ["NameError: undefined local variable or method `require' for main:Object"]
|
32
|
+
|
33
|
+
Sandrbox.perform(['class Foo', 'def test', '"hi"', 'end', 'end']).output # => [nil]
|
34
|
+
Sandrbox.perform(['class Foo', 'def test', '"hi"', 'end', 'end', 'Foo.new.test']).output # => [nil, "hi"]
|
35
|
+
Sandrbox.perform(['Foo.new.test']).output # => ["NameError: uninitialized constant Foo"] Each perform is independent of previous performs
|
36
|
+
|
37
|
+
Sandrbox.perform(['class Foo']).output # => []
|
38
|
+
Sandrbox.perform(['class Foo']).complete? # => false
|
39
|
+
Sandrbox.perform(['class Foo']).indent_level # => 1
|
40
|
+
Sandrbox.perform(['class Foo']).indent_character # => "class"
|
41
|
+
```
|
42
|
+
|
43
|
+
## Copyright
|
44
|
+
|
45
|
+
Copyright (c) 2012 Josh Symonds. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "sandrbox"
|
18
|
+
gem.homepage = "http://github.com/Veraticus/Sandrbox"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = 'A sanitizing sandbox for executing Ruby code'
|
21
|
+
gem.description = 'A sandbox for that tries to change all Ruby code executed to be safe and non-destructive, both to the filesystem and the currently running process'
|
22
|
+
gem.email = "josh@joshsymonds.com"
|
23
|
+
gem.authors = ["Josh Symonds"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Open an irb session preloaded with this library"
|
40
|
+
task :cons do
|
41
|
+
sh "irb -rubygems -I lib -r sandrbox.rb"
|
42
|
+
end
|
43
|
+
|
44
|
+
task :default => :spec
|
data/Sandrbox.gemspec
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "sandrbox"
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Josh Symonds"]
|
12
|
+
s.date = "2012-03-30"
|
13
|
+
s.description = "A sandbox for that tries to change all Ruby code executed to be safe and non-destructive, both to the filesystem and the currently running process"
|
14
|
+
s.email = "josh@joshsymonds.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"Sandrbox.gemspec",
|
27
|
+
"VERSION",
|
28
|
+
"lib/sandrbox.rb",
|
29
|
+
"lib/sandrbox/config.rb",
|
30
|
+
"lib/sandrbox/options/option.rb",
|
31
|
+
"lib/sandrbox/response.rb",
|
32
|
+
"lib/sandrbox/value.rb",
|
33
|
+
"spec/sandrbox/config_spec.rb",
|
34
|
+
"spec/sandrbox/response_spec.rb",
|
35
|
+
"spec/sandrbox/value_spec.rb",
|
36
|
+
"spec/sandrbox_spec.rb",
|
37
|
+
"spec/spec_helper.rb"
|
38
|
+
]
|
39
|
+
s.homepage = "http://github.com/Veraticus/Sandrbox"
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.19"
|
43
|
+
s.summary = "A sanitizing sandbox for executing Ruby code"
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<redcarpet>, ["= 1.17.2"])
|
56
|
+
s.add_development_dependency(%q<github-markup>, [">= 0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
59
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
60
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
61
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
62
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
63
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
64
|
+
s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
|
65
|
+
s.add_dependency(%q<github-markup>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
69
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
71
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
72
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
73
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
74
|
+
s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
|
75
|
+
s.add_dependency(%q<github-markup>, [">= 0"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'sandrbox/options/option'
|
2
|
+
|
3
|
+
module Sandrbox
|
4
|
+
|
5
|
+
module Config
|
6
|
+
extend self
|
7
|
+
extend Options
|
8
|
+
|
9
|
+
option :bad_methods, :default => [
|
10
|
+
[:Object, :abort],
|
11
|
+
[:Kernel, :abort],
|
12
|
+
[:Object, :autoload],
|
13
|
+
[:Kernel, :autoload],
|
14
|
+
[:Object, :autoload?],
|
15
|
+
[:Kernel, :autoload?],
|
16
|
+
[:Object, :callcc],
|
17
|
+
[:Kernel, :callcc],
|
18
|
+
[:Object, :exit],
|
19
|
+
[:Kernel, :exit],
|
20
|
+
[:Object, :exit!],
|
21
|
+
[:Kernel, :exit!],
|
22
|
+
[:Object, :at_exit],
|
23
|
+
[:Kernel, :at_exit],
|
24
|
+
[:Object, :exec],
|
25
|
+
[:Kernel, :exec],
|
26
|
+
[:Object, :fork],
|
27
|
+
[:Kernel, :fork],
|
28
|
+
[:Object, :load],
|
29
|
+
[:Kernel, :load],
|
30
|
+
[:Object, :open],
|
31
|
+
[:Kernel, :open],
|
32
|
+
[:Object, :set_trace_func],
|
33
|
+
[:Kernel, :set_trace_func],
|
34
|
+
[:Object, :spawn],
|
35
|
+
[:Kernel, :spawn],
|
36
|
+
[:Object, :syscall],
|
37
|
+
[:Kernel, :syscall],
|
38
|
+
[:Object, :system],
|
39
|
+
[:Kernel, :system],
|
40
|
+
[:Object, :test],
|
41
|
+
[:Kernel, :test],
|
42
|
+
[:Object, :remove_method],
|
43
|
+
[:Kernel, :remove_method],
|
44
|
+
[:Object, :require],
|
45
|
+
[:Kernel, :require],
|
46
|
+
[:Object, :require_relative],
|
47
|
+
[:Kernel, :require_relative],
|
48
|
+
[:Object, :undef_method],
|
49
|
+
[:Kernel, :undef_method],
|
50
|
+
[:Object, "`".to_sym],
|
51
|
+
[:Kernel, "`".to_sym],
|
52
|
+
[:Class, "`".to_sym]
|
53
|
+
]
|
54
|
+
option :bad_constants, :default => [:Continuation, :Open3, :File, :Dir, :IO, :Sandrbox, :Process, :Thread, :Fiber, :Gem, :Net, :ThreadGroup, :SystemExit, :SignalException, :Interrupt, :FileTest, :Signal]
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Shamelessly stolen from Mongoid!
|
2
|
+
module Sandrbox #:nodoc
|
3
|
+
module Config
|
4
|
+
|
5
|
+
# Encapsulates logic for setting options.
|
6
|
+
module Options
|
7
|
+
|
8
|
+
# Get the defaults or initialize a new empty hash.
|
9
|
+
#
|
10
|
+
# @example Get the defaults.
|
11
|
+
# options.defaults
|
12
|
+
#
|
13
|
+
# @return [ Hash ] The default options.
|
14
|
+
def defaults
|
15
|
+
@defaults ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define a configuration option with a default.
|
19
|
+
#
|
20
|
+
# @example Define the option.
|
21
|
+
# Options.option(:persist_in_safe_mode, :default => false)
|
22
|
+
#
|
23
|
+
# @param [ Symbol ] name The name of the configuration option.
|
24
|
+
# @param [ Hash ] options Extras for the option.
|
25
|
+
#
|
26
|
+
# @option options [ Object ] :default The default value.
|
27
|
+
def option(name, options = {})
|
28
|
+
defaults[name] = settings[name] = options[:default]
|
29
|
+
|
30
|
+
class_eval <<-RUBY
|
31
|
+
def #{name}
|
32
|
+
settings[#{name.inspect}]
|
33
|
+
end
|
34
|
+
|
35
|
+
def #{name}=(value)
|
36
|
+
settings[#{name.inspect}] = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def #{name}?
|
40
|
+
#{name}
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset_#{name}
|
44
|
+
settings[#{name.inspect}] = defaults[#{name.inspect}]
|
45
|
+
end
|
46
|
+
RUBY
|
47
|
+
end
|
48
|
+
|
49
|
+
# Reset the configuration options to the defaults.
|
50
|
+
#
|
51
|
+
# @example Reset the configuration options.
|
52
|
+
# config.reset
|
53
|
+
#
|
54
|
+
# @return [ Hash ] The defaults.
|
55
|
+
def reset
|
56
|
+
settings.replace(defaults)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the settings or initialize a new empty hash.
|
60
|
+
#
|
61
|
+
# @example Get the settings.
|
62
|
+
# options.settings
|
63
|
+
#
|
64
|
+
# @return [ Hash ] The setting options.
|
65
|
+
def settings
|
66
|
+
@settings ||= {}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Sandrbox
|
2
|
+
class Response
|
3
|
+
attr_accessor :array, :compressed_array, :expanded_array, :indents, :results, :old_constants
|
4
|
+
attr_accessor :class_count, :module_count, :def_count, :end_count, :do_count, :left_curly_count, :right_curly_count, :left_bracket_count, :right_bracket_count
|
5
|
+
|
6
|
+
def initialize(array)
|
7
|
+
self.array = array
|
8
|
+
[:expanded_array, :compressed_array, :indents, :results].each do |arr|
|
9
|
+
self.send("#{arr}=".to_sym, [])
|
10
|
+
end
|
11
|
+
[:class, :module, :def, :end, :do, :left_curly, :right_curly, :left_bracket, :right_bracket].each do |count|
|
12
|
+
self.send("#{count}_count=".to_sym, 0)
|
13
|
+
end
|
14
|
+
expand
|
15
|
+
compress
|
16
|
+
end
|
17
|
+
|
18
|
+
def expand
|
19
|
+
self.array.each do |line|
|
20
|
+
next if line.empty?
|
21
|
+
self.expanded_array << uncomment(line).split(';').collect(&:strip)
|
22
|
+
end
|
23
|
+
self.expanded_array.flatten!
|
24
|
+
end
|
25
|
+
|
26
|
+
def compress
|
27
|
+
self.expanded_array.each do |line|
|
28
|
+
uncommented_line = uncomment(line)
|
29
|
+
ending = nil
|
30
|
+
|
31
|
+
[:class, :module, :def, :end, :do].each do |count|
|
32
|
+
if uncommented_line =~ /\s*#{count.to_s}\s*/
|
33
|
+
self.send("#{count}_count=".to_sym, self.send("#{count}_count".to_sym) + 1)
|
34
|
+
self.indents.push(count.to_s) unless count == :end
|
35
|
+
ending = self.indents.pop if count == :end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
{:left_curly => ['{', '}'], :right_curly => ['}', '{'], :left_bracket => ['[', ']'], :right_bracket => [']', '[']}.each do |name, sym|
|
40
|
+
if uncommented_line.count(sym.first) > uncommented_line.count(sym.last)
|
41
|
+
self.send("#{name}_count=".to_sym, self.send("#{name}_count".to_sym) + 1)
|
42
|
+
self.indents.push(sym.first) if name.to_s.include?('left')
|
43
|
+
ending = self.indents.pop if name.to_s.include?('right')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if ending
|
48
|
+
self.compressed_array.last << "#{uncommented_line}#{semicolon(ending)}"
|
49
|
+
elsif indent_level == 1 || self.compressed_array.empty?
|
50
|
+
self.compressed_array << "#{uncommented_line}#{semicolon}"
|
51
|
+
elsif indent_level > 1
|
52
|
+
self.compressed_array.last << "#{uncommented_line}#{semicolon}"
|
53
|
+
else
|
54
|
+
self.compressed_array << uncommented_line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return evaluate if complete?
|
59
|
+
end
|
60
|
+
|
61
|
+
def evaluate
|
62
|
+
preserve_namespace
|
63
|
+
self.compressed_array.each_with_index {|line, line_no| self.results << Sandrbox::Value.new(line, line_no)}
|
64
|
+
restore_namespace
|
65
|
+
end
|
66
|
+
|
67
|
+
def semicolon(char = nil)
|
68
|
+
char ||= indent_character
|
69
|
+
(char == '{' || char == '[') ? '' : ';'
|
70
|
+
end
|
71
|
+
|
72
|
+
def uncomment(line)
|
73
|
+
line.split('#').first.strip
|
74
|
+
end
|
75
|
+
|
76
|
+
def indent_level
|
77
|
+
self.indents.count
|
78
|
+
end
|
79
|
+
|
80
|
+
def indent_character
|
81
|
+
self.indents.last
|
82
|
+
end
|
83
|
+
|
84
|
+
def complete?
|
85
|
+
self.indents.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
def output
|
89
|
+
results.collect(&:to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def preserve_namespace
|
93
|
+
self.old_constants = Object.constants
|
94
|
+
end
|
95
|
+
|
96
|
+
def restore_namespace
|
97
|
+
(Object.constants - self.old_constants).each {|bad_constant| Object.send(:remove_const, bad_constant)}
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Sandrbox
|
2
|
+
class Value
|
3
|
+
attr_accessor :line, :line_no, :result, :time, :unbound_methods, :unbound_constants
|
4
|
+
|
5
|
+
def initialize(line, line_no)
|
6
|
+
self.unbound_methods = []
|
7
|
+
self.unbound_constants = []
|
8
|
+
self.line = line
|
9
|
+
self.line_no = line_no
|
10
|
+
evaluate
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate
|
14
|
+
t = Thread.new do
|
15
|
+
$SAFE = 2
|
16
|
+
begin
|
17
|
+
Timeout::timeout(0.5) do
|
18
|
+
Sandrbox.config.bad_methods.each {|meth| remove_method(meth.first, meth.last)}
|
19
|
+
Sandrbox.config.bad_constants.each {|const| remove_constant(const)}
|
20
|
+
self.result = eval(line, TOPLEVEL_BINDING, "sandrbox", line_no)
|
21
|
+
end
|
22
|
+
rescue Exception => e
|
23
|
+
self.result = "#{e.class}: #{e.to_s}"
|
24
|
+
ensure
|
25
|
+
restore_constants
|
26
|
+
restore_methods
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
timeout = t.join(3)
|
31
|
+
self.result = "SandrboxError: execution expired" if timeout.nil?
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
self.result
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def remove_method(klass, method)
|
43
|
+
const = Object.const_get(klass.to_s)
|
44
|
+
if const.methods.include?(method) || const.instance_methods.include?(method)
|
45
|
+
self.unbound_methods << [const, const.method(method).unbind]
|
46
|
+
metaclass = class << const; self; end
|
47
|
+
|
48
|
+
message = if const == Object
|
49
|
+
"undefined local variable or method `#{method}' for main:Object"
|
50
|
+
else
|
51
|
+
"undefined local variable or method `#{method}' for #{klass}:#{const.class}"
|
52
|
+
end
|
53
|
+
|
54
|
+
metaclass.send(:define_method, method) do |*args|
|
55
|
+
raise NameError, message
|
56
|
+
end
|
57
|
+
|
58
|
+
const.send(:define_method, method) do |*args|
|
59
|
+
raise NameError, message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def restore_methods
|
65
|
+
self.unbound_methods.each do |unbound|
|
66
|
+
klass = unbound.first
|
67
|
+
method = unbound.last
|
68
|
+
|
69
|
+
metaclass = class << klass; self; end
|
70
|
+
|
71
|
+
metaclass.send(:define_method, method.name) do |*args|
|
72
|
+
method.bind(klass).call(*args)
|
73
|
+
end
|
74
|
+
|
75
|
+
klass.send(:define_method, method.name) do |*args|
|
76
|
+
method.bind(klass).call(*args)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def remove_constant(constant)
|
82
|
+
self.unbound_constants << Object.send(:remove_const, constant) if Object.const_defined?(constant)
|
83
|
+
end
|
84
|
+
|
85
|
+
def restore_constants
|
86
|
+
self.unbound_constants.each {|const| Object.const_set(const.to_s.to_sym, const) unless Object.const_defined?(const.to_s.to_sym)}
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/lib/sandrbox.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'irb'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
require 'sandrbox/response'
|
5
|
+
require 'sandrbox/value'
|
6
|
+
require 'sandrbox/config'
|
7
|
+
|
8
|
+
module Sandrbox
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def configure
|
12
|
+
block_given? ? yield(Sandrbox::Config) : Sandrbox::Config
|
13
|
+
end
|
14
|
+
alias :config :configure
|
15
|
+
|
16
|
+
def perform(array)
|
17
|
+
Sandrbox::Response.new(array)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Sandrbox::Response" do
|
4
|
+
it 'initializes values correctly' do
|
5
|
+
@response = Sandrbox::Response.new([])
|
6
|
+
|
7
|
+
@response.class_count.should == 0
|
8
|
+
@response.module_count.should == 0
|
9
|
+
@response.def_count.should == 0
|
10
|
+
@response.end_count.should == 0
|
11
|
+
@response.do_count.should == 0
|
12
|
+
@response.left_curly_count.should == 0
|
13
|
+
@response.right_curly_count.should == 0
|
14
|
+
@response.compressed_array.should == []
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'expands semicolons into new lines' do
|
18
|
+
Sandrbox::Response.new(['class Test; def foo; "hi"; end; end']).expanded_array == ['class Test', 'def foo', '"hi"', 'end', 'end']
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'detects class correctly' do
|
22
|
+
Sandrbox::Response.new(['class Test < ActiveRecord::Base']).class_count.should == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'detects module correctly' do
|
26
|
+
Sandrbox::Response.new([' module Enumerable']).module_count.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'detects def correctly' do
|
30
|
+
Sandrbox::Response.new(['def method(test)']).def_count.should == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'detects end correctly' do
|
34
|
+
Sandrbox::Response.new([' end']).end_count.should == 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'detects do correctly' do
|
38
|
+
Sandrbox::Response.new(['test.each do |foo, wee|']).do_count.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'detects left curly correctly' do
|
42
|
+
Sandrbox::Response.new(['test.each { |foo| ']).left_curly_count.should == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'detects right curly correctly' do
|
46
|
+
Sandrbox::Response.new([' } ']).right_curly_count.should == 1
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'detects left bracket correctly' do
|
50
|
+
Sandrbox::Response.new(['[ "test", ']).left_bracket_count.should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'detects right curly correctly' do
|
54
|
+
Sandrbox::Response.new(['] ']).right_bracket_count.should == 1
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'compresses multiline statements into one line' do
|
58
|
+
Sandrbox::Response.new(['class Test', 'def foo', '"hi"', 'end', 'end']).compressed_array.should == ['class Test;def foo;"hi";end;end;']
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'ignores comments in parsing' do
|
62
|
+
response = Sandrbox::Response.new([]).uncomment('def method(test) # end class module').should == 'def method(test)'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'correctly determines a line is complete for def, class, module, do and end' do
|
66
|
+
Sandrbox::Response.new(['class Test; def foo; "hi"; end; end']).should be_complete
|
67
|
+
Sandrbox::Response.new(['class Test', 'def foo', '"hi"', 'end', 'end']).should be_complete
|
68
|
+
Sandrbox::Response.new(['module Wee', '[1, 2, 3].each do |num|', '"#{num}"', 'end', 'end']).should be_complete
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'correctly determines a line is incomplete for def, class, module, do and end' do
|
72
|
+
Sandrbox::Response.new(['class Test; def foo; "hi"; end']).should_not be_complete
|
73
|
+
Sandrbox::Response.new(['class Test', 'def foo', '"hi"', 'end']).should_not be_complete
|
74
|
+
Sandrbox::Response.new(['module Wee', '[1, 2, 3].each do |num|', '"#{num}"']).should_not be_complete
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'correctly determines indent level' do
|
78
|
+
Sandrbox::Response.new(['class Test; def foo; "hi"; end']).indent_level.should == 1
|
79
|
+
Sandrbox::Response.new(['class Test', 'def foo', '"hi"', 'end']).indent_level.should == 1
|
80
|
+
Sandrbox::Response.new(['module Wee', '[1, 2, 3].each do |num|', '"#{num}"']).indent_level.should == 2
|
81
|
+
Sandrbox::Response.new(['[1, 2, 3].each { |num|', '']).indent_level.should == 1
|
82
|
+
Sandrbox::Response.new(['module Test', 'class Weeha', 'def Foo', '[ "test", ']).indent_level.should == 4
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'correctly determines indent character' do
|
86
|
+
Sandrbox::Response.new(['class Test; def foo; "hi"; end']).indent_character.should == 'class'
|
87
|
+
Sandrbox::Response.new(['class Test', 'def foo', '"hi"', 'end']).indent_character.should == 'class'
|
88
|
+
Sandrbox::Response.new(['module Wee', '[1, 2, 3].each do |num|', '"#{num}"']).indent_character.should == 'do'
|
89
|
+
Sandrbox::Response.new(['[1, 2, 3].each { |num|', '']).indent_character.should == '{'
|
90
|
+
Sandrbox::Response.new(['module Test', 'class Weeha', 'def Foo', '[ "test", ']).indent_character.should == '['
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'does not make an indent character if indents are on the same line' do
|
94
|
+
Sandrbox::Response.new(['[1, 2, 3]']).indent_character.should == nil
|
95
|
+
Sandrbox::Response.new(['{:test => "foo"}']).indent_character.should == nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does make an indent character if indents are on the same line but are unmatched' do
|
99
|
+
Sandrbox::Response.new(['{:test => "foo"}, {:test => "wee"']).indent_character.should == '{'
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'does not return a negative indent level' do
|
103
|
+
Sandrbox::Response.new(['end', 'end', 'end']).indent_level.should == 0
|
104
|
+
end
|
105
|
+
end
|
File without changes
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Sandrbox" do
|
4
|
+
|
5
|
+
it 'performs arbitrary correct irb commands' do
|
6
|
+
Sandrbox.perform(['a = 1 + 1', 'a + a', 'a * a']).output.should == [2, 4, 4]
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns syntax errors immediately' do
|
10
|
+
Sandrbox.perform(['a = 1 + 1', 'b', 'a * a']).output.should == [2, "NameError: undefined local variable or method `b' for main:Object", 4]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'allows constants to be used after uninitializing them' do
|
14
|
+
Sandrbox.perform(['a = 1 + 1'])
|
15
|
+
lambda {Object.const_get(:File)}.should_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'allows methods to be called after removing them' do
|
19
|
+
Sandrbox.perform(['a = 1 + 1'])
|
20
|
+
Kernel.methods.should include(:exit)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'only waits half a second for each command' do
|
24
|
+
Sandrbox.perform(['sleep 5']).output.should == ["Timeout::Error: execution expired"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does multiline class definitions correctly' do
|
28
|
+
Sandrbox.perform(['class Foo', 'def test', '"hi"', 'end', 'end', 'Foo.new.test']).output.should == [nil, "hi"]
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'removes previous class definitions and methods between calls' do
|
32
|
+
Sandrbox.perform(['class Foo', 'def test', '"hi"', 'end', 'end'])
|
33
|
+
Sandrbox.perform(['Foo.new.test']).output.should == ["NameError: uninitialized constant Foo"]
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'unsafe commands' do
|
37
|
+
it 'does not exit' do
|
38
|
+
Sandrbox.perform(['exit']).output.should == ["NameError: undefined local variable or method `exit' for main:Object"]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not exit for kernel' do
|
42
|
+
Sandrbox.perform(['Kernel.exit']).output.should == ["NameError: undefined local variable or method `exit' for Kernel:Module"]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not exec' do
|
46
|
+
Sandrbox.perform(['exec("ps")']).output.should == ["NameError: undefined local variable or method `exec' for main:Object"]
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not exec for kernel' do
|
50
|
+
Sandrbox.perform(['Kernel.exec("ps")']).output.should == ["NameError: undefined local variable or method `exec' for Kernel:Module"]
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not `' do
|
54
|
+
Sandrbox.perform(['`ls`']).output.should == ["NameError: undefined local variable or method ``' for main:Object"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not implement File' do
|
58
|
+
Sandrbox.perform(['File']).output.should == ["NameError: uninitialized constant File"]
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'does not implement Dir' do
|
62
|
+
Sandrbox.perform(['Dir']).output.should == ["NameError: uninitialized constant Dir"]
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not implement IO' do
|
66
|
+
Sandrbox.perform(['IO']).output.should == ["NameError: uninitialized constant IO"]
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'does not implement Open3' do
|
70
|
+
Sandrbox.perform(['Open3']).output.should == ["NameError: uninitialized constant Open3"]
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does not implement Open3 even after requiring it' do
|
74
|
+
Sandrbox.perform(['require "open3"', 'Open3']).output.should == ["NameError: undefined local variable or method `require' for main:Object", "NameError: uninitialized constant Open3"]
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not allow you to manually call protected Sandrbox methods' do
|
78
|
+
Sandrbox.perform(['Sandrbox Sandrbox.inspect']).output.should == ["NameError: uninitialized constant Sandrbox"]
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'does not allow you to manually call children of removed classes' do
|
82
|
+
Sandrbox.perform(['Sandrbox Sandrbox::Config.inspect']).output.should == ["NameError: uninitialized constant Sandrbox"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'sandrbox'
|
6
|
+
require 'mocha'
|
7
|
+
|
8
|
+
# Requires supporting files with custom matchers and macros, etc,
|
9
|
+
# in ./support/ and its subdirectories.
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.mock_with(:mocha)
|
14
|
+
config.backtrace_clean_patterns = []
|
15
|
+
config.before(:each) {Sandrbox.config.reset_bad_methods}
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sandrbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josh Symonds
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mocha
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: jeweler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: yard
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redcarpet
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.17.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - '='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.17.2
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: github-markup
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: A sandbox for that tries to change all Ruby code executed to be safe
|
143
|
+
and non-destructive, both to the filesystem and the currently running process
|
144
|
+
email: josh@joshsymonds.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files:
|
148
|
+
- LICENSE.txt
|
149
|
+
- README.markdown
|
150
|
+
files:
|
151
|
+
- .document
|
152
|
+
- Gemfile
|
153
|
+
- Gemfile.lock
|
154
|
+
- LICENSE.txt
|
155
|
+
- README.markdown
|
156
|
+
- Rakefile
|
157
|
+
- Sandrbox.gemspec
|
158
|
+
- VERSION
|
159
|
+
- lib/sandrbox.rb
|
160
|
+
- lib/sandrbox/config.rb
|
161
|
+
- lib/sandrbox/options/option.rb
|
162
|
+
- lib/sandrbox/response.rb
|
163
|
+
- lib/sandrbox/value.rb
|
164
|
+
- spec/sandrbox/config_spec.rb
|
165
|
+
- spec/sandrbox/response_spec.rb
|
166
|
+
- spec/sandrbox/value_spec.rb
|
167
|
+
- spec/sandrbox_spec.rb
|
168
|
+
- spec/spec_helper.rb
|
169
|
+
homepage: http://github.com/Veraticus/Sandrbox
|
170
|
+
licenses:
|
171
|
+
- MIT
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
segments:
|
183
|
+
- 0
|
184
|
+
hash: 928819448596422268
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ! '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 1.8.19
|
194
|
+
signing_key:
|
195
|
+
specification_version: 3
|
196
|
+
summary: A sanitizing sandbox for executing Ruby code
|
197
|
+
test_files: []
|