mruby_sandbox 0.3.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3cdeb5959e180dc64032103ef631251e1eeb4fd
4
+ data.tar.gz: 41e0be05ebcc4067260e7f402237168057aed3b0
5
+ SHA512:
6
+ metadata.gz: 2e78223ebfdd2f0d74e37aa72316e5c2ba86e8d3d30d522c5281f6793400671fd0d8281a448be7cbf1f95cef4cd383af130deefa3b4186a6d7238cfe8bfe7a90
7
+ data.tar.gz: bb9011f3cd6413d47ba2d2141b9ccf60e763c5c2f0b550d13e045e932827e1c5d2c798ba5806d38bfda2a8435731426621b0a8af943bb2b4ca1117b1f4e27b18
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /tmp
2
+ /bin/mruby_sandbox
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper.rb
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mruby_sandbox (0.3.1)
5
+ pipe_rpc (~> 0.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.2.5)
11
+ json (1.8.3)
12
+ pipe_rpc (0.2.0)
13
+ json
14
+ rspec (3.4.0)
15
+ rspec-core (~> 3.4.0)
16
+ rspec-expectations (~> 3.4.0)
17
+ rspec-mocks (~> 3.4.0)
18
+ rspec-core (3.4.1)
19
+ rspec-support (~> 3.4.0)
20
+ rspec-expectations (3.4.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.4.0)
23
+ rspec-its (1.2.0)
24
+ rspec-core (>= 3.0.0)
25
+ rspec-expectations (>= 3.0.0)
26
+ rspec-mocks (3.4.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.4.0)
29
+ rspec-mocks-matchers-send_message (0.3.1)
30
+ rspec (~> 3.0)
31
+ rspec-mocks (~> 3.0)
32
+ rspec-support (3.4.1)
33
+
34
+ PLATFORMS
35
+ ruby
36
+
37
+ DEPENDENCIES
38
+ bundler (~> 1.8)
39
+ mruby_sandbox!
40
+ rspec (~> 3.4)
41
+ rspec-its
42
+ rspec-mocks-matchers-send_message (~> 0.2)
43
+
44
+ BUNDLED WITH
45
+ 1.11.2
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # A mruby sandbox for ruby
2
+
3
+ A mruby sandbox whose job is to run untrusted ruby code in a safe environment.
4
+ Untrusted code is loaded into an environment having no access to the outside
5
+ world by default. Receivers can be registered so methods can be called on the
6
+ outside of the sandbox.
7
+
8
+ The sandbox runs as a subprocess of a managing parent process. To be able to
9
+ communicate with its parent the sandbox also loads a restricted IO library
10
+ that only allows reading and writing through STDIN and STDOUT. Besides STDERR
11
+ no further IO Endpoints are passed down by the parent. The IO library does not
12
+ implement any operations accessing files on the system or the like.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'mruby_sandbox'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install mruby_sandbox
29
+
30
+ After the gem is installed the sandbox needs to be build for your system:
31
+
32
+ $ build_mruby_sandbox
33
+
34
+ ## Usage
35
+
36
+ ```ruby
37
+ sandbox = MrubySandbox.new
38
+ sandbox.eval("8+45") # => 53
39
+ sandbox.eval("system 'rm -rf /'") # => NoMethodError
40
+ ```
41
+
42
+ More complex untrusted code following the rules of mruby is possible:
43
+
44
+ ```ruby
45
+ sandbox = MrubySandbox.new
46
+ sandbox.eval(<<-CODE)
47
+ def meth
48
+ 'result'
49
+ end
50
+
51
+ class Klass
52
+ def meth
53
+ 'klass meth'
54
+ end
55
+ end
56
+ CODE
57
+
58
+ sandbox.eval('meth') # => 'result'
59
+ sandbox.eval('Klass.new.meth') # => 'klass meth'
60
+ ```
61
+
62
+ There are two methods available to communicate with the outside:
63
+
64
+ * `#export`: Registers a receiver inside the sandbox to reach from the outside
65
+ ```ruby
66
+ sandbox = MrubySandbox.new
67
+ sandbox.eval(<<-CODE)
68
+ class Calc
69
+ def multiply(a, b)
70
+ a * b
71
+ end
72
+ end
73
+ export(math: Calc)
74
+ CODE
75
+
76
+ sandbox.client_for(:math).multiply(5, 9) # => 45
77
+ sandbox.client_for(:math).add(5, 9) # => NoMethodError
78
+ ```
79
+
80
+ * `#client_for`: Reaches out to a receiver on the outside of the sandbox
81
+ ```ruby
82
+ class Calc < MrubySandbox::Receiver
83
+ def exp(a, b)
84
+ a ** b
85
+ end
86
+ end
87
+
88
+ sandbox = MrubySandbox.new
89
+ sandbox.add_receiver(math: Calc)
90
+
91
+ sandbox.eval 'client_for(:math).exp(2,8)' # => 256
92
+ sandbox.eval 'client_for(:math).exp' # => ArgumentError
93
+ ```
94
+
95
+ To create Receivers outside the sandbox let them inherit from `MrubySandbox::Receiver`. This is
96
+ basically a `BasicObject` without `#instance_{eval|exec}` so it does not respond to methods
97
+ capable of executing potential malicious code. Inside the sandbox, in mruby land, nothing bad should
98
+ be possible, if a receiver is an ordinary object. Otherwise we are screwed anyway.
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ def test?
6
+ ARGV.include? 'test'
7
+ end
8
+
9
+ def clean?
10
+ ARGV.include? 'clean'
11
+ end
12
+
13
+ def build?
14
+ not test? and not clean?
15
+ end
16
+
17
+ def clone_mruby_into(mruby_dir)
18
+ system "git clone https://github.com/cremno/mruby.git #{mruby_dir}"
19
+ Dir.chdir mruby_dir
20
+ system "git checkout fix-exception-backtrace"
21
+ end
22
+
23
+ def build_mruby(config:, args:)
24
+ system "#{test? ? 'TEST=test' : ''} MRUBY_CONFIG=#{config} ./minirake #{args.join(' ')}"
25
+ end
26
+
27
+ bin_dir = File.expand_path(File.dirname(__FILE__))
28
+ root_dir = File.join(bin_dir, '..')
29
+ mruby_dir = File.join(root_dir, 'tmp/mruby')
30
+ build_config = File.join(root_dir, 'mruby/config/build_config.rb')
31
+
32
+ clone_mruby_into(mruby_dir) unless File.exist?(mruby_dir)
33
+
34
+ Dir.chdir mruby_dir
35
+ build_mruby(config: build_config, args: ARGV)
36
+
37
+ FileUtils.cp File.join(mruby_dir, 'build/host/bin/mruby'), File.join(bin_dir, 'mruby_sandbox') if build?
38
+ FileUtils.cp File.join(mruby_dir, 'build/host/bin/mirb'), File.join(bin_dir, 'mirb') if test?
@@ -0,0 +1,75 @@
1
+ require 'pipe_rpc'
2
+ require 'forwardable'
3
+ require 'logger'
4
+ require_relative 'mruby_sandbox/version'
5
+ require_relative 'mruby_sandbox/server'
6
+
7
+ class MrubySandbox
8
+ extend Forwardable
9
+
10
+ class << self
11
+ attr_writer :logger
12
+
13
+ def logger
14
+ @logger ||= Logger.new(STDOUT)
15
+ end
16
+ end
17
+
18
+ def self.finalize(pid)
19
+ proc do |id|
20
+ Process.kill 9, pid
21
+ Process.wait pid
22
+ logger.debug "Sandbox(#{id}) garbage collected and process #{pid} killed"
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ input, w = IO.pipe
28
+ r, output = IO.pipe
29
+ pid = spawn(executable, in: r, out: w)
30
+ r.close; w.close
31
+
32
+ self.class.logger.debug "Sandbox(#{__id__}) created with process #{pid}"
33
+ ObjectSpace.define_finalizer(self, self.class.finalize(pid))
34
+
35
+ @hub = PipeRpc::Hub.new(input: input, output: output)
36
+ @data = {}
37
+
38
+ rescue Errno::ENOENT => e
39
+ STDERR.puts "The mruby_sandbox executable is missing. Run `build_mruby_sandbox` first."
40
+ fail e
41
+ end
42
+
43
+ attr_reader :data
44
+
45
+ def client
46
+ client_for :default
47
+ end
48
+
49
+ delegate [:clear, :eval] => :client
50
+ delegate [:add_server, :rmv_server, :client_for, :channel, :handle_message, :loop_iteration=,
51
+ :on_sent, :on_received, :on_incoming_request] => :@hub
52
+ alias_method :export, :add_server
53
+
54
+ def start_logging
55
+ @hub.logger = proc do |message|
56
+ self.class.logger.debug "Sandbox(#{__id__}) #{message}"
57
+ end
58
+ end
59
+
60
+ def reflect_logger_server=(server)
61
+ client.debug_mode(!!server)
62
+ if server
63
+ add_server(reflect_logger: server)
64
+ else
65
+ rmv_server(:reflect_logger)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def executable
72
+ current_dir = File.expand_path(File.dirname(__FILE__))
73
+ File.join(current_dir, '../bin/mruby_sandbox')
74
+ end
75
+ end
@@ -0,0 +1,32 @@
1
+ class MrubySandbox::Server < BasicObject
2
+ # instance_eval imposes a security vulnerability on the ordinary ruby side of the sandbox: All
3
+ # server's methods are accessible from the inside. So with instance_eval from the inside the
4
+ # following is possible: `client.instance_eval '::Kernel.system("rm -rf /")'`
5
+ undef_method :instance_eval
6
+ undef_method :instance_exec
7
+
8
+ def self.inherited(subclass)
9
+ subclass.__send__(:define_method, :respond_to?) do |method|
10
+ subclass.instance_methods.include?(method.to_sym)
11
+ end
12
+
13
+ subclass.__send__(:define_method, :class) do
14
+ subclass
15
+ end
16
+ end
17
+
18
+ def self.const_missing(name)
19
+ ::Object.const_get(name)
20
+ end
21
+
22
+ def inspect
23
+ "#<#{self.class}:#{'%#016x' % __id__}>"
24
+ end
25
+ alias_method :to_s, :inspect
26
+
27
+ private
28
+
29
+ def raise(*args)
30
+ Kernel.raise *args
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ class MrubySandbox
2
+ VERSION = "0.3.1"
3
+ end
@@ -0,0 +1,17 @@
1
+ MRuby::Build.new do |conf|
2
+ # Gets set by the VS command prompts.
3
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
4
+ toolchain :visualcpp
5
+ else
6
+ toolchain :gcc
7
+ end
8
+
9
+ dir = File.expand_path(File.dirname(__FILE__))
10
+ root_dir = File.expand_path('..', dir)
11
+ sandbox_mrbgem = File.join(root_dir, 'sandbox')
12
+
13
+ conf.gembox File.join(dir, 'safe-core')
14
+ conf.gem core: 'mruby-bin-mirb' if ENV['TEST']
15
+ #conf.gem File.join(root_dir, '../../mruby-pipe_rpc')
16
+ conf.gem sandbox_mrbgem unless ENV['TEST']
17
+ end
@@ -0,0 +1,64 @@
1
+ MRuby::GemBox.new do |conf|
2
+ # Use standard Kernel#sprintf method
3
+ conf.gem :core => "mruby-sprintf"
4
+
5
+ # Use standard Math module
6
+ conf.gem :core => "mruby-math"
7
+
8
+ # Use standard Time class
9
+ conf.gem :core => "mruby-time"
10
+
11
+ # Use standard Struct class
12
+ conf.gem :core => "mruby-struct"
13
+
14
+ # Use extensional Enumerable module
15
+ conf.gem :core => "mruby-enum-ext"
16
+
17
+ # Use extensional String class
18
+ conf.gem :core => "mruby-string-ext"
19
+
20
+ # Use extensional Numeric class
21
+ conf.gem :core => "mruby-numeric-ext"
22
+
23
+ # Use extensional Array class
24
+ conf.gem :core => "mruby-array-ext"
25
+
26
+ # Use extensional Hash class
27
+ conf.gem :core => "mruby-hash-ext"
28
+
29
+ # Use extensional Range class
30
+ conf.gem :core => "mruby-range-ext"
31
+
32
+ # Use extensional Proc class
33
+ conf.gem :core => "mruby-proc-ext"
34
+
35
+ # Use extensional Symbol class
36
+ conf.gem :core => "mruby-symbol-ext"
37
+
38
+ # Use Random class
39
+ conf.gem :core => "mruby-random"
40
+
41
+ # Use extensional Object class
42
+ conf.gem :core => "mruby-object-ext"
43
+
44
+ # Use ObjectSpace class
45
+ conf.gem :core => "mruby-objectspace"
46
+
47
+ # Use Enumerator class (require mruby-fiber)
48
+ conf.gem :core => "mruby-enumerator"
49
+
50
+ # Use Enumerable::Lazy class (require mruby-enumerator)
51
+ conf.gem :core => "mruby-enum-lazy"
52
+
53
+ # Use extended toplevel object (main) methods
54
+ conf.gem :core => "mruby-toplevel-ext"
55
+
56
+ # Generate mruby command
57
+ conf.gem :core => "mruby-bin-mruby"
58
+
59
+ # Use extensional Kernel module
60
+ conf.gem :core => "mruby-kernel-ext"
61
+
62
+ # Use eval module
63
+ conf.gem :core => "mruby-eval"
64
+ end
@@ -0,0 +1,10 @@
1
+ MRuby::Gem::Specification.new('mruby-sandbox') do |spec|
2
+ spec.license = 'MIT'
3
+ spec.version = '0.1.0'
4
+ spec.summary = "A mruby sandbox whose job is to run untrusted ruby code in a safe environment"
5
+ spec.authors = ['Christopher Aue']
6
+ spec.homepage = 'https://github.com/christopheraue/ruby-mruby-sandbox'
7
+
8
+ spec.add_dependency 'mruby-pipe_rpc', '~> 0.1', github: 'christopheraue/mruby-pipe_rpc'
9
+ spec.add_dependency 'mruby-onig-regexp', '>= 0', github: 'mattn/mruby-onig-regexp'
10
+ end
@@ -0,0 +1,77 @@
1
+ class Sandbox < BasicObject
2
+ def initialize(main)
3
+ @main = main
4
+ @main.sandbox = self
5
+ input = IO.new(0, 'r') #STDIN
6
+ output = IO.new(1, 'w') #STDOUT
7
+ @hub = PipeRpc::Hub.new(input: input, output: output)
8
+ add_server(default: Controller.new(self))
9
+ ::Kernel.loop { iteration }
10
+ end
11
+
12
+ def eval(code, file = '', lineno = 0)
13
+ @main.eval(code, file, lineno)
14
+ end
15
+
16
+ def add_server(args = {})
17
+ @hub.add_server(args)
18
+ end
19
+
20
+ def client_for(server_name)
21
+ @hub.client_for(server_name)
22
+ end
23
+
24
+ def debug_mode(debug = true)
25
+ @hub.logger = debug ? :reflect : false
26
+ end
27
+
28
+ private
29
+
30
+ def iteration
31
+ @hub.handle_message # blocks every iteration
32
+ rescue ::Exception => e
33
+ # reflect ALL rescueable errors back to the managing process
34
+ backtrace = e.backtrace
35
+ @hub.send_error(code: -32603, data: { message: e.message, backtrace: backtrace })
36
+ ::Kernel.raise e
37
+ end
38
+ end
39
+
40
+ class Sandbox::Controller
41
+ def initialize(sandbox)
42
+ @sandbox = sandbox
43
+ end
44
+
45
+ def eval(code, file = '', lineno = 0)
46
+ @sandbox.eval(code, file, lineno)
47
+ end
48
+
49
+ def debug_mode(debug = true)
50
+ @sandbox.debug_mode(debug)
51
+ end
52
+ end
53
+
54
+ # Interface for untrusted code to communicate with the outside
55
+ class << self
56
+ attr_writer :sandbox
57
+
58
+ def eval(code, file = '', lineno = 0)
59
+ instance_eval(code, file, lineno)
60
+ end
61
+
62
+ def export(args = {})
63
+ @sandbox.add_server(args)
64
+ end
65
+
66
+ def client_for(server = :default)
67
+ @sandbox.client_for(server)
68
+ end
69
+ alias_method :client, :client_for
70
+ end
71
+
72
+ # Remove constants from global namespace so untrusted code cannot mess around with it.
73
+ Sandbox::GC = Object.remove_const(:GC)
74
+ Sandbox::ObjectSpace = Object.remove_const(:ObjectSpace)
75
+ Sandbox::IO = Object.remove_const(:IO)
76
+ Sandbox::PipeRpc = Object.remove_const(:PipeRpc)
77
+ Object.remove_const(:Sandbox).new(self)
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mruby_sandbox/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mruby_sandbox"
8
+ spec.version = MrubySandbox::VERSION
9
+ spec.authors = ["Christopher Aue"]
10
+ spec.email = ["mail@christopheraue.net"]
11
+
12
+ spec.summary = %q{A mruby sandbox for ruby}
13
+ spec.description = %q{A mruby sandbox running in its own sub process and having only a single
14
+ pipe in and out to communicate with the outside. Provides a rather safe
15
+ environment to run untrusted code.}
16
+ spec.homepage = "https://github.com/christopheraue/ruby-mruby_sandbox"
17
+ spec.license = "MIT"
18
+ spec.post_install_message = "Run `build_mruby_sandbox` to finish installation."
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.executables = ["build_mruby_sandbox"]
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency "pipe_rpc", "~> 0.2"
25
+ spec.add_development_dependency "bundler", "~> 1.8"
26
+ spec.add_development_dependency "rspec", "~> 3.4"
27
+ spec.add_development_dependency "rspec-its"
28
+ spec.add_development_dependency "rspec-mocks-matchers-send_message", "~> 0.2"
29
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mruby_sandbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Christopher Aue
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pipe_rpc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-its
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-mocks-matchers-send_message
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.2'
83
+ description: |-
84
+ A mruby sandbox running in its own sub process and having only a single
85
+ pipe in and out to communicate with the outside. Provides a rather safe
86
+ environment to run untrusted code.
87
+ email:
88
+ - mail@christopheraue.net
89
+ executables:
90
+ - build_mruby_sandbox
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".gitignore"
95
+ - ".rspec"
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - README.md
99
+ - bin/build_mruby_sandbox
100
+ - lib/mruby_sandbox.rb
101
+ - lib/mruby_sandbox/server.rb
102
+ - lib/mruby_sandbox/version.rb
103
+ - mruby/config/build_config.rb
104
+ - mruby/config/safe-core.gembox
105
+ - mruby/sandbox/mrbgem.rake
106
+ - mruby/sandbox/mrblib/sandbox.rb
107
+ - mruby_sandbox.gemspec
108
+ homepage: https://github.com/christopheraue/ruby-mruby_sandbox
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message: Run `build_mruby_sandbox` to finish installation.
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.4.5.1
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: A mruby sandbox for ruby
132
+ test_files: []