jruby_sandbox 0.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rvmrc +47 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +39 -0
- data/README.md +66 -0
- data/Rakefile +24 -0
- data/ext/java/sandbox/BoxedClass.java +25 -0
- data/ext/java/sandbox/SandboxFull.java +308 -0
- data/ext/java/sandbox/SandboxModule.java +16 -0
- data/ext/java/sandbox/SandboxProfile.java +22 -0
- data/ext/java/sandbox/SandboxService.java +33 -0
- data/jruby_sandbox.gemspec +27 -0
- data/lib/sandbox/prelude.rb +19 -0
- data/lib/sandbox/sandbox.jar +0 -0
- data/lib/sandbox/version.rb +3 -0
- data/lib/sandbox.rb +333 -0
- data/spec/sandbox_spec.rb +226 -0
- data/spec/support/foo.txt +1 -0
- metadata +122 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
|
+
environment_id="jruby-1.6.3@jruby_sandbox"
|
8
|
+
|
9
|
+
#
|
10
|
+
# Uncomment following line if you want options to be set only for given project.
|
11
|
+
#
|
12
|
+
PROJECT_JRUBY_OPTS=( --1.9 )
|
13
|
+
|
14
|
+
#
|
15
|
+
# First we attempt to load the desired environment directly from the environment
|
16
|
+
# file. This is very fast and efficient compared to running through the entire
|
17
|
+
# CLI and selector. If you want feedback on which environment was used then
|
18
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
19
|
+
#
|
20
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
21
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
22
|
+
then
|
23
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
24
|
+
|
25
|
+
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
|
26
|
+
then
|
27
|
+
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
28
|
+
fi
|
29
|
+
else
|
30
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
31
|
+
if ! rvm --create "$environment_id"
|
32
|
+
then
|
33
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
34
|
+
exit 1
|
35
|
+
fi
|
36
|
+
fi
|
37
|
+
|
38
|
+
#
|
39
|
+
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
40
|
+
# it be automatically loaded. Uncomment the following and adjust the filename if
|
41
|
+
# necessary.
|
42
|
+
#
|
43
|
+
# filename=".gems"
|
44
|
+
# if [[ -s "$filename" ]] ; then
|
45
|
+
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
46
|
+
# fi
|
47
|
+
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/nanothief/fakefs.git
|
3
|
+
revision: cb63ff262e1ab9dffdda6d20a1b9c90434e97d58
|
4
|
+
specs:
|
5
|
+
fakefs (0.3.2)
|
6
|
+
|
7
|
+
PATH
|
8
|
+
remote: .
|
9
|
+
specs:
|
10
|
+
jruby_sandbox (0.1.0-java)
|
11
|
+
fakefs
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
diff-lcs (1.1.2)
|
17
|
+
rake (0.9.2)
|
18
|
+
rake-compiler (0.7.9)
|
19
|
+
rake
|
20
|
+
rspec (2.6.0)
|
21
|
+
rspec-core (~> 2.6.0)
|
22
|
+
rspec-expectations (~> 2.6.0)
|
23
|
+
rspec-mocks (~> 2.6.0)
|
24
|
+
rspec-core (2.6.4)
|
25
|
+
rspec-expectations (2.6.0)
|
26
|
+
diff-lcs (~> 1.1.2)
|
27
|
+
rspec-mocks (2.6.0)
|
28
|
+
yard (0.7.2)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
java
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
fakefs!
|
35
|
+
jruby_sandbox!
|
36
|
+
rake
|
37
|
+
rake-compiler
|
38
|
+
rspec
|
39
|
+
yard
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
JRuby Sandbox
|
2
|
+
=============
|
3
|
+
|
4
|
+
The JRuby sandbox is a reimplementation of _why's freaky freaky sandbox
|
5
|
+
in JRuby, and is heavily based on [javasand][1] by Ola Bini, but updated
|
6
|
+
for JRuby 1.6.
|
7
|
+
|
8
|
+
## Prerequisites
|
9
|
+
|
10
|
+
This gem requires JRuby 1.6. As of the time of this writing, it is known to
|
11
|
+
work with the latest stable version of JRuby, 1.6.3. You can install it via
|
12
|
+
RVM with the following command:
|
13
|
+
|
14
|
+
rvm install jruby-1.6.3
|
15
|
+
|
16
|
+
## Building
|
17
|
+
|
18
|
+
To build the JRuby extension, run `rake compile`. This will build the
|
19
|
+
`lib/sandbox/sandbox.jar` file, which `lib/sandbox.rb` loads.
|
20
|
+
|
21
|
+
## Basic Usage
|
22
|
+
|
23
|
+
Sandbox gives you a self-contained JRuby interpreter in which to eval
|
24
|
+
code without polluting the host environment.
|
25
|
+
|
26
|
+
>> require "sandbox"
|
27
|
+
=> true
|
28
|
+
>> sand = Sandbox::Full.new
|
29
|
+
=> #<Sandbox::Full:0x46377e2a>
|
30
|
+
>> sand.eval("x = 1 + 2")
|
31
|
+
=> 3
|
32
|
+
>> sand.eval("x")
|
33
|
+
=> 3
|
34
|
+
>> x
|
35
|
+
NameError: undefined local variable or method `x' for #<Object:0x11cdc190>
|
36
|
+
|
37
|
+
There's also `Sandbox::Full#require`, which lets you invoke
|
38
|
+
`Kernel#require` directly for the sandbox, so you can load any trusted
|
39
|
+
core libraries. Note that this is a direct binding to `Kernel#require`,
|
40
|
+
so it will only load ruby stdlib libraries (i.e. no rubygems support
|
41
|
+
yet).
|
42
|
+
|
43
|
+
## Sandbox::Safe usage
|
44
|
+
|
45
|
+
Sandbox::Safe exposes an `#activate!` method which will lock down the sandbox, removing unsafe methods. Before calling `#activate!`, Sandbox::Safe is the same as Sandbox::Full.
|
46
|
+
|
47
|
+
>> require 'sandbox'
|
48
|
+
=> true
|
49
|
+
>> sand = Sandbox.safe
|
50
|
+
=> #<Sandbox::Safe:0x17072b90>
|
51
|
+
>> sand.eval %{`echo HELLO`}
|
52
|
+
=> "HELLO\n"
|
53
|
+
>> sand.activate!
|
54
|
+
>> sand.eval %{`echo HELLO`}
|
55
|
+
Sandbox::SandboxException: NoMethodError: undefined method ``' for main:Object
|
56
|
+
|
57
|
+
Sandbox::Safe works by whitelisting methods to keep, and removing the rest. Checkout sandbox.rb for which methods are kept.
|
58
|
+
|
59
|
+
## Known Issues / TODOs
|
60
|
+
|
61
|
+
* It would be a good idea to integrate something like FakeFS to stub
|
62
|
+
out the filesystem in the sandbox.
|
63
|
+
* There is currently no timeout support, so it's possible for a
|
64
|
+
sandbox to loop indefinitely and block the host interpreter.
|
65
|
+
|
66
|
+
[1]: http://ola-bini.blogspot.com/2006/12/freaky-freaky-sandbox-has-come-to-jruby.html
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake/javaextensiontask'
|
5
|
+
|
6
|
+
Rake::JavaExtensionTask.new('sandbox') do |ext|
|
7
|
+
jruby_home = RbConfig::CONFIG['prefix']
|
8
|
+
ext.ext_dir = 'ext/java'
|
9
|
+
ext.lib_dir = 'lib/sandbox'
|
10
|
+
jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar']
|
11
|
+
ext.classpath = jars.map { |x| File.expand_path(x) }.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
17
|
+
t.pattern = 'spec/**/*_spec.rb'
|
18
|
+
t.rspec_opts = ['--backtrace']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Make sure the jar is up to date before running specs
|
22
|
+
task :spec => :compile
|
23
|
+
|
24
|
+
task :default => :spec
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.anno.JRubyClass;
|
4
|
+
import org.jruby.anno.JRubyMethod;
|
5
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
6
|
+
import org.jruby.runtime.Block;
|
7
|
+
|
8
|
+
@JRubyClass(name="BoxedClass")
|
9
|
+
public class BoxedClass {
|
10
|
+
@JRubyMethod(module=true, rest=true)
|
11
|
+
public static IRubyObject method_missing(IRubyObject recv, IRubyObject[] args, Block block) {
|
12
|
+
IRubyObject[] args2 = new IRubyObject[args.length - 1];
|
13
|
+
System.arraycopy(args, 1, args2, 0, args2.length);
|
14
|
+
String name = args[0].toString();
|
15
|
+
|
16
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
17
|
+
return box.runMethod(recv, name, args2, block);
|
18
|
+
}
|
19
|
+
|
20
|
+
@JRubyMethod(name="new", meta=true, rest=true)
|
21
|
+
public static IRubyObject _new(IRubyObject recv, IRubyObject[] args, Block block) {
|
22
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
23
|
+
return box.runMethod(recv, "new", args, block);
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,308 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import java.util.Collection;
|
4
|
+
import java.util.Map;
|
5
|
+
|
6
|
+
import org.jruby.Ruby;
|
7
|
+
import org.jruby.RubyClass;
|
8
|
+
import org.jruby.RubyInstanceConfig;
|
9
|
+
import org.jruby.RubyKernel;
|
10
|
+
import org.jruby.RubyModule;
|
11
|
+
import org.jruby.RubyObject;
|
12
|
+
import org.jruby.CompatVersion;
|
13
|
+
import org.jruby.anno.JRubyClass;
|
14
|
+
import org.jruby.anno.JRubyMethod;
|
15
|
+
import org.jruby.internal.runtime.methods.DynamicMethod;
|
16
|
+
import org.jruby.runtime.Block;
|
17
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
18
|
+
import org.jruby.common.IRubyWarnings;
|
19
|
+
import org.jruby.exceptions.RaiseException;
|
20
|
+
|
21
|
+
|
22
|
+
@JRubyClass(name="Sandbox::Full")
|
23
|
+
public class SandboxFull extends RubyObject {
|
24
|
+
private Ruby wrapped;
|
25
|
+
|
26
|
+
public SandboxFull(Ruby runtime, RubyClass type) {
|
27
|
+
super(runtime, type);
|
28
|
+
reload();
|
29
|
+
}
|
30
|
+
|
31
|
+
@JRubyMethod
|
32
|
+
public IRubyObject reload() {
|
33
|
+
RubyInstanceConfig cfg = new RubyInstanceConfig();
|
34
|
+
cfg.setObjectSpaceEnabled(getRuntime().getInstanceConfig().isObjectSpaceEnabled());
|
35
|
+
cfg.setInput(getRuntime().getInstanceConfig().getInput());
|
36
|
+
cfg.setOutput(getRuntime().getInstanceConfig().getOutput());
|
37
|
+
cfg.setError(getRuntime().getInstanceConfig().getError());
|
38
|
+
cfg.setCompatVersion(CompatVersion.RUBY1_9);
|
39
|
+
cfg.setScriptFileName("(sandbox)");
|
40
|
+
|
41
|
+
SandboxProfile profile = new SandboxProfile(this);
|
42
|
+
cfg.setProfile(profile);
|
43
|
+
|
44
|
+
wrapped = Ruby.newInstance(cfg);
|
45
|
+
|
46
|
+
RubyClass cBoxedClass = wrapped.defineClass("BoxedClass", wrapped.getObject(), wrapped.getObject().getAllocator());
|
47
|
+
cBoxedClass.defineAnnotatedMethods(BoxedClass.class);
|
48
|
+
|
49
|
+
return this;
|
50
|
+
}
|
51
|
+
|
52
|
+
@JRubyMethod(required=1)
|
53
|
+
public IRubyObject eval(IRubyObject str) {
|
54
|
+
try {
|
55
|
+
IRubyObject result = wrapped.evalScriptlet(str.asJavaString(), wrapped.getCurrentContext().getCurrentScope());
|
56
|
+
return unbox(result);
|
57
|
+
} catch (RaiseException e) {
|
58
|
+
String msg = e.getException().callMethod(wrapped.getCurrentContext(), "message").asJavaString();
|
59
|
+
String path = e.getException().type().getName();
|
60
|
+
RubyClass eSandboxException = (RubyClass) getRuntime().getClassFromPath("Sandbox::SandboxException");
|
61
|
+
throw new RaiseException(getRuntime(), eSandboxException, path + ": " + msg, false);
|
62
|
+
} catch (Exception e) {
|
63
|
+
e.printStackTrace();
|
64
|
+
getRuntime().getWarnings().warn(IRubyWarnings.ID.MISCELLANEOUS, "NativeException: " + e);
|
65
|
+
return getRuntime().getNil();
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
@JRubyMethod(name="import", required=1)
|
70
|
+
public IRubyObject _import(IRubyObject klass) {
|
71
|
+
if (!(klass instanceof RubyModule)) {
|
72
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
73
|
+
}
|
74
|
+
String name = ((RubyModule) klass).getName();
|
75
|
+
importClassPath(name, false);
|
76
|
+
return getRuntime().getNil();
|
77
|
+
}
|
78
|
+
|
79
|
+
@JRubyMethod(required=1)
|
80
|
+
public IRubyObject ref(IRubyObject klass) {
|
81
|
+
if (!(klass instanceof RubyModule)) {
|
82
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
83
|
+
}
|
84
|
+
String name = ((RubyModule) klass).getName();
|
85
|
+
importClassPath(name, true);
|
86
|
+
return getRuntime().getNil();
|
87
|
+
}
|
88
|
+
|
89
|
+
private RubyModule importClassPath(String path, final boolean link) {
|
90
|
+
RubyModule runtimeModule = getRuntime().getObject();
|
91
|
+
RubyModule wrappedModule = wrapped.getObject();
|
92
|
+
|
93
|
+
if (path.startsWith("#")) {
|
94
|
+
throw getRuntime().newArgumentError("can't import anonymous class " + path);
|
95
|
+
}
|
96
|
+
|
97
|
+
for (String name : path.split("::")) {
|
98
|
+
runtimeModule = (RubyModule) runtimeModule.getConstantAt(name);
|
99
|
+
// Create the module when it did not exist yet...
|
100
|
+
if (wrappedModule.const_defined_p(wrapped.getCurrentContext(), wrapped.newString(name)).isFalse()) {
|
101
|
+
// The BoxedClass takes the place of Object as top of the inheritance
|
102
|
+
// hierarchy. As a result, we can intercept all new instances that are
|
103
|
+
// created and all method_missing calls.
|
104
|
+
RubyModule sup = wrapped.getClass("BoxedClass");
|
105
|
+
if (!link && runtimeModule instanceof RubyClass) {
|
106
|
+
// If we're importing a class, recursively import all of its
|
107
|
+
// superclasses as well.
|
108
|
+
sup = importClassPath(runtimeModule.getSuperClass().getName(), true);
|
109
|
+
}
|
110
|
+
|
111
|
+
RubyClass klass = (RubyClass) sup;
|
112
|
+
if (wrappedModule == wrapped.getObject()) {
|
113
|
+
|
114
|
+
if (link || runtimeModule instanceof RubyClass){ // if this is a ref and not an import
|
115
|
+
wrappedModule = wrapped.defineClass(name, klass, klass.getAllocator());
|
116
|
+
} else {
|
117
|
+
wrappedModule = wrapped.defineModule(name);
|
118
|
+
}
|
119
|
+
|
120
|
+
} else {
|
121
|
+
if (runtimeModule instanceof RubyClass){
|
122
|
+
wrappedModule = wrappedModule.defineClassUnder(name, klass, klass.getAllocator());
|
123
|
+
} else {
|
124
|
+
wrappedModule = wrappedModule.defineModuleUnder(name);
|
125
|
+
}
|
126
|
+
|
127
|
+
}
|
128
|
+
} else {
|
129
|
+
// ...or just resolve it, if it was already known
|
130
|
+
wrappedModule = (RubyModule) wrappedModule.getConstantAt(name);
|
131
|
+
}
|
132
|
+
|
133
|
+
// Check the consistency of the hierarchy
|
134
|
+
if (runtimeModule instanceof RubyClass) {
|
135
|
+
if (!link && !runtimeModule.getSuperClass().getName().equals(wrappedModule.getSuperClass().getName())) {
|
136
|
+
throw getRuntime().newTypeError("superclass mismatch for class " + runtimeModule.getSuperClass().getName());
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
if (link || runtimeModule instanceof RubyClass) {
|
141
|
+
linkObject(runtimeModule, wrappedModule);
|
142
|
+
} else {
|
143
|
+
copyMethods(runtimeModule, wrappedModule);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
return runtimeModule;
|
148
|
+
}
|
149
|
+
|
150
|
+
private void copyMethods(RubyModule from, RubyModule to) {
|
151
|
+
to.getMethodsForWrite().putAll(from.getMethods());
|
152
|
+
to.getSingletonClass().getMethodsForWrite().putAll(from.getSingletonClass().getMethods());
|
153
|
+
}
|
154
|
+
|
155
|
+
@JRubyMethod(required=2)
|
156
|
+
public IRubyObject keep_methods(IRubyObject className, IRubyObject methods) {
|
157
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
158
|
+
if (module != null) {
|
159
|
+
keepMethods(module, methods.convertToArray());
|
160
|
+
}
|
161
|
+
return methods;
|
162
|
+
}
|
163
|
+
|
164
|
+
@JRubyMethod(required=2)
|
165
|
+
public IRubyObject keep_singleton_methods(IRubyObject className, IRubyObject methods) {
|
166
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
167
|
+
if (module != null) {
|
168
|
+
keepMethods(module, methods.convertToArray());
|
169
|
+
}
|
170
|
+
return methods;
|
171
|
+
}
|
172
|
+
|
173
|
+
private void keepMethods(RubyModule module, Collection retain) {
|
174
|
+
for (Map.Entry<String, DynamicMethod> methodEntry : module.getMethods().entrySet()) {
|
175
|
+
String methodName = methodEntry.getKey();
|
176
|
+
if (!retain.contains(methodName)) {
|
177
|
+
removeMethod(module, methodName);
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
@JRubyMethod(required=2)
|
183
|
+
public IRubyObject remove_method(IRubyObject className, IRubyObject methodName) {
|
184
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
185
|
+
if (module != null) {
|
186
|
+
removeMethod(module, methodName.asJavaString());
|
187
|
+
}
|
188
|
+
return getRuntime().getNil();
|
189
|
+
}
|
190
|
+
|
191
|
+
@JRubyMethod(required=2)
|
192
|
+
public IRubyObject remove_singleton_method(IRubyObject className, IRubyObject methodName) {
|
193
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
194
|
+
if (module != null) {
|
195
|
+
removeMethod(module, methodName.asJavaString());
|
196
|
+
}
|
197
|
+
return getRuntime().getNil();
|
198
|
+
}
|
199
|
+
|
200
|
+
private void removeMethod(RubyModule module, String methodName) {
|
201
|
+
// System.err.println("removing method " + methodName + " from " + module.inspect().asJavaString());
|
202
|
+
module.removeMethod(wrapped.getCurrentContext(), methodName);
|
203
|
+
}
|
204
|
+
|
205
|
+
@JRubyMethod(required=1)
|
206
|
+
public IRubyObject load(IRubyObject str) {
|
207
|
+
try {
|
208
|
+
wrapped.getLoadService().load(str.asJavaString(), true);
|
209
|
+
return getRuntime().getTrue();
|
210
|
+
} catch (RaiseException e) {
|
211
|
+
e.printStackTrace();
|
212
|
+
return getRuntime().getFalse();
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
@JRubyMethod(required=1)
|
217
|
+
public IRubyObject require(IRubyObject str) {
|
218
|
+
try {
|
219
|
+
IRubyObject result = RubyKernel.require(wrapped.getKernel(), wrapped.newString(str.asJavaString()), Block.NULL_BLOCK);
|
220
|
+
return unbox(result);
|
221
|
+
} catch (RaiseException e) {
|
222
|
+
e.printStackTrace();
|
223
|
+
return getRuntime().getFalse();
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
private IRubyObject unbox(IRubyObject obj) {
|
228
|
+
return box(obj);
|
229
|
+
}
|
230
|
+
|
231
|
+
private IRubyObject rebox(IRubyObject obj) {
|
232
|
+
return box(obj);
|
233
|
+
}
|
234
|
+
|
235
|
+
private IRubyObject box(IRubyObject obj) {
|
236
|
+
if (obj.isImmediate()) {
|
237
|
+
return cross(obj);
|
238
|
+
} else {
|
239
|
+
// If this object already existed and was returned from the wrapped
|
240
|
+
// runtime on an earlier occasion, it will already contain a link to its
|
241
|
+
// brother in the regular runtime and we can safely return that link.
|
242
|
+
IRubyObject link = getLinkedObject(obj);
|
243
|
+
if (!link.isNil()) {
|
244
|
+
IRubyObject box = getLinkedBox(obj);
|
245
|
+
if (box == this) return link;
|
246
|
+
}
|
247
|
+
|
248
|
+
// Is the class already known on both sides of the fence?
|
249
|
+
IRubyObject klass = constFind(obj.getMetaClass().getRealClass().getName());
|
250
|
+
link = getRuntime().getNil();
|
251
|
+
if (!klass.isNil()) {
|
252
|
+
link = getLinkedObject(klass);
|
253
|
+
}
|
254
|
+
|
255
|
+
if (link.isNil()) {
|
256
|
+
return cross(obj);
|
257
|
+
} else {
|
258
|
+
IRubyObject v = ((RubyClass)klass).allocate();
|
259
|
+
linkObject(obj, v);
|
260
|
+
return v;
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
private IRubyObject cross(IRubyObject obj) {
|
266
|
+
IRubyObject dumped = wrapped.getModule("Marshal").callMethod(wrapped.getCurrentContext(), "dump", obj);
|
267
|
+
return getRuntime().getModule("Marshal").callMethod(getRuntime().getCurrentContext(), "load", dumped);
|
268
|
+
}
|
269
|
+
|
270
|
+
protected static IRubyObject getLinkedObject(IRubyObject arg) {
|
271
|
+
IRubyObject object = arg.getRuntime().getNil();
|
272
|
+
if (arg.getInstanceVariables().getInstanceVariable("__link__") != null) {
|
273
|
+
object = (IRubyObject) arg.getInstanceVariables().fastGetInstanceVariable("__link__");
|
274
|
+
}
|
275
|
+
return object;
|
276
|
+
}
|
277
|
+
|
278
|
+
protected static IRubyObject getLinkedBox(IRubyObject arg) {
|
279
|
+
IRubyObject object = arg.getRuntime().getNil();
|
280
|
+
if (arg.getInstanceVariables().getInstanceVariable("__box__") != null) {
|
281
|
+
object = (IRubyObject) arg.getInstanceVariables().fastGetInstanceVariable("__box__");
|
282
|
+
}
|
283
|
+
return object;
|
284
|
+
}
|
285
|
+
|
286
|
+
private void linkObject(IRubyObject runtimeObject, IRubyObject wrappedObject) {
|
287
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__link__", runtimeObject);
|
288
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__box__", this);
|
289
|
+
}
|
290
|
+
|
291
|
+
private IRubyObject constFind(String path) {
|
292
|
+
try {
|
293
|
+
return wrapped.getClassFromPath(path);
|
294
|
+
} catch (Exception e) {
|
295
|
+
return wrapped.getNil();
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
protected IRubyObject runMethod(IRubyObject recv, String name, IRubyObject[] args, Block block) {
|
300
|
+
IRubyObject[] args2 = new IRubyObject[args.length];
|
301
|
+
for (int i = 0; i < args.length; i++) {
|
302
|
+
args2[i] = unbox(args[i]);
|
303
|
+
}
|
304
|
+
IRubyObject recv2 = unbox(recv);
|
305
|
+
IRubyObject result = recv2.callMethod(getRuntime().getCurrentContext(), name, args2, block);
|
306
|
+
return rebox(result);
|
307
|
+
}
|
308
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.Profile;
|
4
|
+
import org.jruby.anno.JRubyMethod;
|
5
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
6
|
+
|
7
|
+
public class SandboxModule {
|
8
|
+
@JRubyMethod(name="current", meta=true)
|
9
|
+
public static IRubyObject s_current(IRubyObject recv) {
|
10
|
+
Profile prof = recv.getRuntime().getProfile();
|
11
|
+
if (prof instanceof SandboxProfile) {
|
12
|
+
return ((SandboxProfile) prof).getSandbox();
|
13
|
+
}
|
14
|
+
return recv.getRuntime().getNil();
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.Profile;
|
4
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
5
|
+
|
6
|
+
public class SandboxProfile implements Profile {
|
7
|
+
private IRubyObject sandbox;
|
8
|
+
|
9
|
+
public SandboxProfile(IRubyObject sandbox) {
|
10
|
+
this.sandbox = sandbox;
|
11
|
+
}
|
12
|
+
|
13
|
+
public IRubyObject getSandbox() {
|
14
|
+
return sandbox;
|
15
|
+
}
|
16
|
+
|
17
|
+
public boolean allowBuiltin(String name) { return true; }
|
18
|
+
public boolean allowClass(String name) { return true; }
|
19
|
+
public boolean allowModule(String name) { return true; }
|
20
|
+
public boolean allowLoad(String name) { return true; }
|
21
|
+
public boolean allowRequire(String name) { return true; }
|
22
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import java.io.IOException;
|
4
|
+
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
import org.jruby.RubyClass;
|
7
|
+
import org.jruby.RubyModule;
|
8
|
+
import org.jruby.runtime.ObjectAllocator;
|
9
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
10
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
11
|
+
|
12
|
+
public class SandboxService implements BasicLibraryService {
|
13
|
+
public boolean basicLoad(Ruby runtime) throws IOException {
|
14
|
+
init(runtime);
|
15
|
+
return true;
|
16
|
+
}
|
17
|
+
|
18
|
+
private void init(Ruby runtime) {
|
19
|
+
RubyModule mSandbox = runtime.defineModule("Sandbox");
|
20
|
+
mSandbox.defineAnnotatedMethods(SandboxModule.class);
|
21
|
+
|
22
|
+
RubyClass cSandboxFull = mSandbox.defineClassUnder("Full", runtime.getObject(), FULL_ALLOCATOR);
|
23
|
+
cSandboxFull.defineAnnotatedMethods(SandboxFull.class);
|
24
|
+
|
25
|
+
RubyClass cSandboxException = mSandbox.defineClassUnder("SandboxException", runtime.getException(), runtime.getException().getAllocator());
|
26
|
+
}
|
27
|
+
|
28
|
+
protected static final ObjectAllocator FULL_ALLOCATOR = new ObjectAllocator() {
|
29
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
30
|
+
return new SandboxFull(runtime, klass);
|
31
|
+
}
|
32
|
+
};
|
33
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'sandbox/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'jruby_sandbox'
|
7
|
+
s.version = Sandbox::VERSION
|
8
|
+
s.platform = 'java'
|
9
|
+
s.authors = ['Dray Lacy', 'Eric Allam']
|
10
|
+
s.email = ['dray@envylabs.com', 'eric@envylabs.com']
|
11
|
+
s.homepage = 'http://github.com/omghax/jruby_sandbox'
|
12
|
+
s.summary = 'Sandbox support for JRuby'
|
13
|
+
s.description = "A version of _why's Freaky Freaky Sandbox for JRuby."
|
14
|
+
|
15
|
+
s.rubyforge_project = 'jruby_sandbox'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n") + ['lib/sandbox/sandbox.jar']
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ['lib']
|
21
|
+
|
22
|
+
s.add_dependency 'fakefs'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
s.add_development_dependency 'rake-compiler'
|
25
|
+
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'yard'
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Alternate "safer" versions of Ruby methods. Mostly non-blocking.
|
2
|
+
[Fixnum, Bignum, Float].each do |klass|
|
3
|
+
klass.class_eval do
|
4
|
+
# A very weak version of pow, it doesn't work on Floats, but it's gonna
|
5
|
+
# fill the most common uses for now.
|
6
|
+
def **(x)
|
7
|
+
case x
|
8
|
+
when 0; 1
|
9
|
+
when 1; self
|
10
|
+
else
|
11
|
+
y = 1
|
12
|
+
while 0 <= (x -= 1) do
|
13
|
+
y *= self
|
14
|
+
end
|
15
|
+
y
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
Binary file
|
data/lib/sandbox.rb
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
require 'sandbox/sandbox'
|
2
|
+
require 'sandbox/version'
|
3
|
+
require 'fakefs/safe'
|
4
|
+
|
5
|
+
module Sandbox
|
6
|
+
PRELUDE = File.expand_path('../sandbox/prelude.rb', __FILE__).freeze # :nodoc:
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def new
|
10
|
+
Full.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def safe
|
14
|
+
Safe.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Safe < Full
|
19
|
+
def activate!
|
20
|
+
activate_fakefs
|
21
|
+
|
22
|
+
keep_singleton_methods(:Kernel, KERNEL_S_METHODS)
|
23
|
+
keep_singleton_methods(:Symbol, SYMBOL_S_METHODS)
|
24
|
+
keep_singleton_methods(:String, STRING_S_METHODS)
|
25
|
+
|
26
|
+
keep_methods(:Kernel, KERNEL_METHODS)
|
27
|
+
keep_methods(:NilClass, NILCLASS_METHODS)
|
28
|
+
keep_methods(:Symbol, SYMBOL_METHODS)
|
29
|
+
keep_methods(:TrueClass, TRUECLASS_METHODS)
|
30
|
+
keep_methods(:FalseClass, FALSECLASS_METHODS)
|
31
|
+
keep_methods(:Enumerable, ENUMERABLE_METHODS)
|
32
|
+
keep_methods(:String, STRING_METHODS)
|
33
|
+
end
|
34
|
+
|
35
|
+
def activate_fakefs
|
36
|
+
require 'fileutils'
|
37
|
+
|
38
|
+
# unfortunately, the authors of FakeFS used `extend self` in FileUtils, instead of `module_function`.
|
39
|
+
# I fixed it for them
|
40
|
+
(FakeFS::FileUtils.methods - Module.methods - Kernel.methods).each do |module_method_name|
|
41
|
+
FakeFS::FileUtils.send(:module_function, module_method_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
import FakeFS
|
45
|
+
ref FakeFS::Dir
|
46
|
+
ref FakeFS::File
|
47
|
+
ref FakeFS::FileTest
|
48
|
+
import FakeFS::FileUtils #import FileUtils because it is a module
|
49
|
+
|
50
|
+
eval <<-RUBY
|
51
|
+
Object.class_eval do
|
52
|
+
remove_const(:Dir)
|
53
|
+
remove_const(:File)
|
54
|
+
remove_const(:FileTest)
|
55
|
+
remove_const(:FileUtils)
|
56
|
+
|
57
|
+
const_set(:Dir, FakeFS::Dir)
|
58
|
+
const_set(:File, FakeFS::File)
|
59
|
+
const_set(:FileUtils, FakeFS::FileUtils)
|
60
|
+
const_set(:FileTest, FakeFS::FileTest)
|
61
|
+
end
|
62
|
+
RUBY
|
63
|
+
|
64
|
+
FakeFS::FileSystem.clear
|
65
|
+
end
|
66
|
+
|
67
|
+
KERNEL_S_METHODS = %w[
|
68
|
+
Array
|
69
|
+
binding
|
70
|
+
block_given?
|
71
|
+
catch
|
72
|
+
chomp
|
73
|
+
chomp!
|
74
|
+
chop
|
75
|
+
chop!
|
76
|
+
eval
|
77
|
+
fail
|
78
|
+
Float
|
79
|
+
format
|
80
|
+
global_variables
|
81
|
+
gsub
|
82
|
+
gsub!
|
83
|
+
Integer
|
84
|
+
iterator?
|
85
|
+
lambda
|
86
|
+
local_variables
|
87
|
+
loop
|
88
|
+
method_missing
|
89
|
+
proc
|
90
|
+
raise
|
91
|
+
scan
|
92
|
+
split
|
93
|
+
sprintf
|
94
|
+
String
|
95
|
+
sub
|
96
|
+
sub!
|
97
|
+
throw
|
98
|
+
].freeze
|
99
|
+
|
100
|
+
SYMBOL_S_METHODS = %w[
|
101
|
+
all_symbols
|
102
|
+
].freeze
|
103
|
+
|
104
|
+
STRING_S_METHODS = %w[
|
105
|
+
new
|
106
|
+
].freeze
|
107
|
+
|
108
|
+
KERNEL_METHODS = %w[
|
109
|
+
==
|
110
|
+
===
|
111
|
+
=~
|
112
|
+
Array
|
113
|
+
binding
|
114
|
+
block_given?
|
115
|
+
catch
|
116
|
+
chomp
|
117
|
+
chomp!
|
118
|
+
chop
|
119
|
+
chop!
|
120
|
+
class
|
121
|
+
clone
|
122
|
+
dup
|
123
|
+
eql?
|
124
|
+
equal?
|
125
|
+
eval
|
126
|
+
fail
|
127
|
+
Float
|
128
|
+
format
|
129
|
+
freeze
|
130
|
+
frozen?
|
131
|
+
global_variables
|
132
|
+
gsub
|
133
|
+
gsub!
|
134
|
+
hash
|
135
|
+
id
|
136
|
+
initialize_copy
|
137
|
+
inspect
|
138
|
+
instance_eval
|
139
|
+
instance_of?
|
140
|
+
instance_variables
|
141
|
+
instance_variable_get
|
142
|
+
instance_variable_set
|
143
|
+
instance_variable_defined?
|
144
|
+
Integer
|
145
|
+
is_a?
|
146
|
+
iterator?
|
147
|
+
kind_of?
|
148
|
+
lambda
|
149
|
+
local_variables
|
150
|
+
loop
|
151
|
+
methods
|
152
|
+
method_missing
|
153
|
+
nil?
|
154
|
+
private_methods
|
155
|
+
print
|
156
|
+
proc
|
157
|
+
protected_methods
|
158
|
+
public_methods
|
159
|
+
raise
|
160
|
+
remove_instance_variable
|
161
|
+
respond_to?
|
162
|
+
respond_to_missing?
|
163
|
+
scan
|
164
|
+
send
|
165
|
+
singleton_methods
|
166
|
+
singleton_method_added
|
167
|
+
singleton_method_removed
|
168
|
+
singleton_method_undefined
|
169
|
+
split
|
170
|
+
sprintf
|
171
|
+
String
|
172
|
+
sub
|
173
|
+
sub!
|
174
|
+
taint
|
175
|
+
tainted?
|
176
|
+
throw
|
177
|
+
to_a
|
178
|
+
to_s
|
179
|
+
type
|
180
|
+
untaint
|
181
|
+
__send__
|
182
|
+
].freeze
|
183
|
+
|
184
|
+
NILCLASS_METHODS = %w[
|
185
|
+
&
|
186
|
+
inspect
|
187
|
+
nil?
|
188
|
+
to_a
|
189
|
+
to_f
|
190
|
+
to_i
|
191
|
+
to_s
|
192
|
+
^
|
193
|
+
|
|
194
|
+
].freeze
|
195
|
+
|
196
|
+
SYMBOL_METHODS = %w[
|
197
|
+
===
|
198
|
+
id2name
|
199
|
+
inspect
|
200
|
+
to_i
|
201
|
+
to_int
|
202
|
+
to_s
|
203
|
+
to_sym
|
204
|
+
].freeze
|
205
|
+
|
206
|
+
TRUECLASS_METHODS = %w[
|
207
|
+
&
|
208
|
+
to_s
|
209
|
+
^
|
210
|
+
|
|
211
|
+
].freeze
|
212
|
+
|
213
|
+
FALSECLASS_METHODS = %w[
|
214
|
+
&
|
215
|
+
to_s
|
216
|
+
^
|
217
|
+
|
|
218
|
+
].freeze
|
219
|
+
|
220
|
+
ENUMERABLE_METHODS = %w[
|
221
|
+
all?
|
222
|
+
any?
|
223
|
+
collect
|
224
|
+
detect
|
225
|
+
each_with_index
|
226
|
+
entries
|
227
|
+
find
|
228
|
+
find_all
|
229
|
+
grep
|
230
|
+
include?
|
231
|
+
inject
|
232
|
+
map
|
233
|
+
max
|
234
|
+
member?
|
235
|
+
min
|
236
|
+
partition
|
237
|
+
reject
|
238
|
+
select
|
239
|
+
sort
|
240
|
+
sort_by
|
241
|
+
to_a
|
242
|
+
zip
|
243
|
+
].freeze
|
244
|
+
|
245
|
+
STRING_METHODS = %w[
|
246
|
+
%
|
247
|
+
*
|
248
|
+
+
|
249
|
+
<<
|
250
|
+
<=>
|
251
|
+
==
|
252
|
+
=~
|
253
|
+
capitalize
|
254
|
+
capitalize!
|
255
|
+
casecmp
|
256
|
+
center
|
257
|
+
chomp
|
258
|
+
chomp!
|
259
|
+
chop
|
260
|
+
chop!
|
261
|
+
concat
|
262
|
+
count
|
263
|
+
crypt
|
264
|
+
delete
|
265
|
+
delete!
|
266
|
+
downcase
|
267
|
+
downcase!
|
268
|
+
dump
|
269
|
+
each
|
270
|
+
each_byte
|
271
|
+
each_line
|
272
|
+
empty?
|
273
|
+
eql?
|
274
|
+
gsub
|
275
|
+
gsub!
|
276
|
+
hash
|
277
|
+
hex
|
278
|
+
include?
|
279
|
+
index
|
280
|
+
initialize
|
281
|
+
initialize_copy
|
282
|
+
insert
|
283
|
+
inspect
|
284
|
+
intern
|
285
|
+
length
|
286
|
+
ljust
|
287
|
+
lstrip
|
288
|
+
lstrip!
|
289
|
+
match
|
290
|
+
next
|
291
|
+
next!
|
292
|
+
oct
|
293
|
+
replace
|
294
|
+
reverse
|
295
|
+
reverse!
|
296
|
+
rindex
|
297
|
+
rjust
|
298
|
+
rstrip
|
299
|
+
rstrip!
|
300
|
+
scan
|
301
|
+
size
|
302
|
+
slice
|
303
|
+
slice!
|
304
|
+
split
|
305
|
+
squeeze
|
306
|
+
squeeze!
|
307
|
+
strip
|
308
|
+
strip!
|
309
|
+
start_with?
|
310
|
+
sub
|
311
|
+
sub!
|
312
|
+
succ
|
313
|
+
succ!
|
314
|
+
sum
|
315
|
+
swapcase
|
316
|
+
swapcase!
|
317
|
+
to_f
|
318
|
+
to_i
|
319
|
+
to_s
|
320
|
+
to_str
|
321
|
+
to_sym
|
322
|
+
tr
|
323
|
+
tr!
|
324
|
+
tr_s
|
325
|
+
tr_s!
|
326
|
+
upcase
|
327
|
+
upcase!
|
328
|
+
upto
|
329
|
+
[]
|
330
|
+
[]=
|
331
|
+
].freeze
|
332
|
+
end
|
333
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'sandbox'
|
3
|
+
|
4
|
+
describe Sandbox do
|
5
|
+
after(:each) do
|
6
|
+
Object.class_eval { remove_const(:Foo) } if defined?(Foo)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ".new" do
|
10
|
+
subject { Sandbox.new }
|
11
|
+
|
12
|
+
it { should_not be_nil }
|
13
|
+
it { should be_an_instance_of(Sandbox::Full) }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".safe" do
|
17
|
+
subject { Sandbox.safe }
|
18
|
+
|
19
|
+
it { should be_an_instance_of(Sandbox::Safe) }
|
20
|
+
|
21
|
+
it 'should not lock down until calling activate!' do
|
22
|
+
subject.eval('`echo hello`').should == "hello\n"
|
23
|
+
|
24
|
+
subject.activate!
|
25
|
+
|
26
|
+
expect {
|
27
|
+
subject.eval('`echo hello`')
|
28
|
+
}.to raise_error(Sandbox::SandboxException)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should activate FakeFS inside the sandbox (and not allow it to be deactivated)" do
|
32
|
+
subject.eval('File').should == ::File
|
33
|
+
|
34
|
+
subject.activate!
|
35
|
+
|
36
|
+
foo = File.join(File.dirname(__FILE__), 'support', 'foo.txt')
|
37
|
+
|
38
|
+
expect {
|
39
|
+
subject.eval(%{File.read('#{foo}')})
|
40
|
+
}.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
|
41
|
+
|
42
|
+
subject.eval('File').should == FakeFS::File
|
43
|
+
subject.eval('Dir').should == FakeFS::Dir
|
44
|
+
subject.eval('FileUtils').should == FakeFS::FileUtils
|
45
|
+
subject.eval('FileTest').should == FakeFS::FileTest
|
46
|
+
|
47
|
+
subject.eval(%{FakeFS.deactivate!})
|
48
|
+
|
49
|
+
expect {
|
50
|
+
subject.eval(%{File.read('#{foo}')})
|
51
|
+
}.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
|
52
|
+
|
53
|
+
subject.eval(%{File.open('/bar.txt', 'w') {|file| file << "bar" }})
|
54
|
+
|
55
|
+
expect {
|
56
|
+
subject.eval(%{FileUtils.cp('/bar.txt', '/baz.txt')})
|
57
|
+
}.to_not raise_error(Sandbox::SandboxException, /NoMethodError/)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "sandboxes global variables" do
|
61
|
+
subject.eval('$0').should == '(sandbox)'
|
62
|
+
/(.)(.)(.)/.match('abc')
|
63
|
+
subject.eval('$TEST = "TEST"; $TEST').should == 'TEST'
|
64
|
+
subject.eval('/(.)(.)(.)/.match("def"); $2').should == 'e'
|
65
|
+
$2.should == 'b'
|
66
|
+
subject.eval('$TEST').should == 'TEST'
|
67
|
+
subject.eval('$2').should == 'e'
|
68
|
+
/(.)(.)(.)/.match('ghi')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe ".current" do
|
73
|
+
it "should not return a current sandbox outside a sandbox" do
|
74
|
+
Sandbox.current.should be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return the current sandbox inside a sandbox" do
|
78
|
+
pending do
|
79
|
+
sandbox = Sandbox.new
|
80
|
+
sandbox.ref(Sandbox)
|
81
|
+
sandbox.eval('Sandbox.current').should == sandbox
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#eval" do
|
87
|
+
subject { Sandbox.new }
|
88
|
+
|
89
|
+
it "should allow a range of common operations" do
|
90
|
+
operations = <<-OPS
|
91
|
+
1 + 1
|
92
|
+
'foo'.chomp
|
93
|
+
'foo'
|
94
|
+
OPS
|
95
|
+
subject.eval(operations).should == 'foo'
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should persist state between evaluations" do
|
99
|
+
subject.eval('o = Object.new')
|
100
|
+
subject.eval('o').should_not be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should be able to define a new class in the sandbox" do
|
104
|
+
result = subject.eval('Foo = Struct.new(:foo); struct = Foo.new("baz"); struct.foo')
|
105
|
+
result.should == 'baz'
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should be able to use a class across invocations" do
|
109
|
+
# Return nil, because the environment doesn't know "Foo"
|
110
|
+
subject.eval('Foo = Struct.new(:foo); nil')
|
111
|
+
subject.eval('struct = Foo.new("baz"); nil')
|
112
|
+
subject.eval('struct.foo').should == 'baz'
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "communication between sandbox and environment" do
|
116
|
+
it "should be possible to pass data from the box to the environment" do
|
117
|
+
Foo = Struct.new(:foo)
|
118
|
+
subject.ref(Foo)
|
119
|
+
struct = subject.eval('struct = Foo.new')
|
120
|
+
subject.eval('struct.foo = "baz"')
|
121
|
+
struct.foo.should == 'baz'
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be possible to pass data from the environment to the box" do
|
125
|
+
Foo = Struct.new(:foo)
|
126
|
+
subject.ref(Foo)
|
127
|
+
struct = subject.eval('struct = Foo.new')
|
128
|
+
struct.foo = 'baz'
|
129
|
+
subject.eval('struct.foo').should == 'baz'
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should be able to pass large object data from the box to the environment" do
|
133
|
+
expect {
|
134
|
+
subject.eval %{
|
135
|
+
(0..1000).to_a.inject({}) {|h,i| h[i] = "HELLO WORLD"; h }
|
136
|
+
}
|
137
|
+
}.to_not raise_error(Sandbox::SandboxException)
|
138
|
+
|
139
|
+
expect {
|
140
|
+
subject.eval %{'RUBY'*100}
|
141
|
+
}.to_not raise_error(Sandbox::SandboxException)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "#import" do
|
147
|
+
subject { Sandbox.new }
|
148
|
+
|
149
|
+
it "should be able to call a referenced namespaced module method" do
|
150
|
+
Foo = Class.new
|
151
|
+
Foo::Bar = Module.new do
|
152
|
+
def baz
|
153
|
+
'baz'
|
154
|
+
end
|
155
|
+
module_function :baz
|
156
|
+
end
|
157
|
+
|
158
|
+
subject.import(Foo::Bar)
|
159
|
+
subject.eval('Foo::Bar.baz').should == 'baz'
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should be able to include a module from the environment" do
|
163
|
+
Foo = Module.new do
|
164
|
+
def baz
|
165
|
+
'baz'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
subject.import(Foo)
|
170
|
+
subject.eval("class Bar; include Foo; end; nil")
|
171
|
+
subject.eval('Bar.new.baz').should == 'baz'
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should be able to copy instance methods from a module that uses module_function" do
|
175
|
+
Foo = Module.new do
|
176
|
+
def baz; 'baz'; end
|
177
|
+
|
178
|
+
module_function :baz
|
179
|
+
end
|
180
|
+
|
181
|
+
subject.import Foo
|
182
|
+
subject.eval('Foo.baz').should == 'baz'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#ref" do
|
187
|
+
subject { Sandbox.new }
|
188
|
+
|
189
|
+
it "should be possible to reference a class defined outside the box" do
|
190
|
+
Foo = Class.new
|
191
|
+
subject.ref(Foo)
|
192
|
+
subject.eval('Foo.new').should be_an_instance_of(Foo)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should be possible to change the class after the ref" do
|
196
|
+
Foo = Class.new
|
197
|
+
subject.ref(Foo)
|
198
|
+
def Foo.foo; 'baz'; end
|
199
|
+
subject.eval('Foo.foo').should == 'baz'
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should be possible to dynamically add a class method after the ref" do
|
203
|
+
Foo = Class.new
|
204
|
+
subject.ref(Foo)
|
205
|
+
Foo.class_eval('def Foo.foo; "baz"; end')
|
206
|
+
subject.eval('Foo.foo').should == 'baz'
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should be possible to dynamically add a class method after the ref" do
|
210
|
+
Foo = Class.new
|
211
|
+
subject.ref(Foo)
|
212
|
+
Foo.instance_eval('def Foo.foo; "baz"; end')
|
213
|
+
subject.eval('Foo.foo').should == 'baz'
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should be possible to call a method on the class that receives a block" do
|
217
|
+
Foo = Class.new do
|
218
|
+
def self.bar
|
219
|
+
yield
|
220
|
+
end
|
221
|
+
end
|
222
|
+
subject.ref(Foo)
|
223
|
+
subject.eval(%{Foo.bar { "baz" }}).should == 'baz'
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
foo
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jruby_sandbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: java
|
7
|
+
authors:
|
8
|
+
- Dray Lacy
|
9
|
+
- Eric Allam
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2011-09-15 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: fakefs
|
17
|
+
requirement: &70097860842780 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70097860842780
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: &70097860842160 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70097860842160
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rake-compiler
|
39
|
+
requirement: &70097860841480 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70097860841480
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
requirement: &70097860840900 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70097860840900
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: yard
|
61
|
+
requirement: &70097860840300 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *70097860840300
|
70
|
+
description: A version of _why's Freaky Freaky Sandbox for JRuby.
|
71
|
+
email:
|
72
|
+
- dray@envylabs.com
|
73
|
+
- eric@envylabs.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- .rvmrc
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- ext/java/sandbox/BoxedClass.java
|
85
|
+
- ext/java/sandbox/SandboxFull.java
|
86
|
+
- ext/java/sandbox/SandboxModule.java
|
87
|
+
- ext/java/sandbox/SandboxProfile.java
|
88
|
+
- ext/java/sandbox/SandboxService.java
|
89
|
+
- jruby_sandbox.gemspec
|
90
|
+
- lib/sandbox.rb
|
91
|
+
- lib/sandbox/prelude.rb
|
92
|
+
- lib/sandbox/version.rb
|
93
|
+
- spec/sandbox_spec.rb
|
94
|
+
- spec/support/foo.txt
|
95
|
+
- lib/sandbox/sandbox.jar
|
96
|
+
homepage: http://github.com/omghax/jruby_sandbox
|
97
|
+
licenses: []
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project: jruby_sandbox
|
116
|
+
rubygems_version: 1.8.10
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Sandbox support for JRuby
|
120
|
+
test_files:
|
121
|
+
- spec/sandbox_spec.rb
|
122
|
+
- spec/support/foo.txt
|