mruby_sandbox 0.3.1

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