xplenty-jruby_sandbox 0.2.4-java
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 +7 -0
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +91 -0
- data/Rakefile +24 -0
- data/ext/java/sandbox/BoxedClass.java +35 -0
- data/ext/java/sandbox/SandboxFull.java +317 -0
- data/ext/java/sandbox/SandboxModule.java +39 -0
- data/ext/java/sandbox/SandboxProfile.java +22 -0
- data/ext/java/sandbox/SandboxService.java +17 -0
- data/jruby_sandbox.gemspec +28 -0
- data/lib/sandbox/prelude.rb +19 -0
- data/lib/sandbox/safe.rb +417 -0
- data/lib/sandbox/sandbox.jar +0 -0
- data/lib/sandbox/version.rb +3 -0
- data/lib/sandbox.rb +17 -0
- data/spec/exploits_spec.rb +196 -0
- data/spec/sandbox_spec.rb +270 -0
- data/spec/support/foo.txt +1 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d06b08e73589246267426b4e93121d583a168c01
|
4
|
+
data.tar.gz: b72fcac1cc5956601d781cde42d74bae26516cf6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc6380715019cb8f27918bca1f673e7164d90631495ae1821fdf57ed31707cf16575dfc7c69c288cc01517447a1866e91a61f69de907c633c3238cfc1c7e7eca
|
7
|
+
data.tar.gz: d0ae61acb863f50a86df5dca25eb9630c74ceee64bbe6487aef1d54629ef890a045a796014dc4779b461b373e36a112d5964dc1dad1227ff7f1feb8634c06a3b
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.9
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in jruby_sandbox.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# this fork has some recent work done for 1.9, like File.foreach
|
7
|
+
gem 'fakefs', :git => 'git://github.com/codeschool/fakefs.git', :require => 'fakefs/safe', :ref => "6ae3212e3dab013b4b5e290d1aceba6466b30dec"
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2006 Ola Bini <ola@ologix.com>
|
2
|
+
Copyright (c) 2011 Dray Lacy and Eric Allam
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
JRuby Sandbox
|
2
|
+
=============
|
3
|
+
|
4
|
+
[](https://travis-ci.org/omghax/jruby-sandbox)
|
5
|
+
|
6
|
+
The JRuby sandbox is a reimplementation of \_why's freaky freaky sandbox
|
7
|
+
in JRuby, and is heavily based on [javasand][1] by Ola Bini, but updated
|
8
|
+
for JRuby 1.7.
|
9
|
+
|
10
|
+
## Prerequisites
|
11
|
+
|
12
|
+
This gem was developed against JRuby 1.7.6, and is known to work with 1.7.8,
|
13
|
+
but has not been tested against other versions, so proceed at your own risk.
|
14
|
+
The Travis CI configuration specifies the `jruby-19mode` target, which floats
|
15
|
+
between exact versions of JRuby. At the time of writing, this is currently
|
16
|
+
JRuby 1.7.8. You can see a list of Travis CI's provided rubies [here][2]. As
|
17
|
+
long as the build is green you should be good to go.
|
18
|
+
|
19
|
+
Installing JRuby is simple with RVM:
|
20
|
+
|
21
|
+
rvm install jruby-1.7.6
|
22
|
+
|
23
|
+
## Building
|
24
|
+
|
25
|
+
To build the JRuby extension, run `rake compile`. This will build the
|
26
|
+
`lib/sandbox/sandbox.jar` file, which `lib/sandbox.rb` loads.
|
27
|
+
|
28
|
+
## Basic Usage
|
29
|
+
|
30
|
+
Sandbox gives you a self-contained JRuby interpreter in which to eval
|
31
|
+
code without polluting the host environment.
|
32
|
+
|
33
|
+
>> require "sandbox"
|
34
|
+
=> true
|
35
|
+
>> sand = Sandbox::Full.new
|
36
|
+
=> #<Sandbox::Full:0x46377e2a>
|
37
|
+
>> sand.eval("x = 1 + 2") # we've defined x in the sandbox
|
38
|
+
=> 3
|
39
|
+
>> sand.eval("x")
|
40
|
+
=> 3
|
41
|
+
>> x # but it hasn't leaked out into the host interpreter
|
42
|
+
NameError: undefined local variable or method `x' for #<Object:0x11cdc190>
|
43
|
+
|
44
|
+
There's also `Sandbox::Full#require`, which lets you invoke `Kernel#require`
|
45
|
+
directly for the sandbox, so you can load any trusted core libraries. Note that
|
46
|
+
this is a direct binding to `Kernel#require`, so it will only load ruby stdlib
|
47
|
+
libraries (i.e. no rubygems support yet).
|
48
|
+
|
49
|
+
## Sandbox::Safe usage
|
50
|
+
|
51
|
+
Sandbox::Safe exposes an `#activate!` method which will lock down the sandbox,
|
52
|
+
removing unsafe methods. Before calling `#activate!`, Sandbox::Safe is the same
|
53
|
+
as Sandbox::Full.
|
54
|
+
|
55
|
+
>> require 'sandbox'
|
56
|
+
=> true
|
57
|
+
>> sand = Sandbox.safe
|
58
|
+
=> #<Sandbox::Safe:0x17072b90>
|
59
|
+
>> sand.eval %{`echo HELLO`}
|
60
|
+
=> "HELLO\n"
|
61
|
+
>> sand.activate!
|
62
|
+
>> sand.eval %{`echo HELLO`}
|
63
|
+
Sandbox::SandboxException: NoMethodError: undefined method ``' for main:Object
|
64
|
+
|
65
|
+
Sandbox::Safe works by whitelisting methods to keep, and removing the rest.
|
66
|
+
Checkout sandbox.rb for which methods are kept.
|
67
|
+
|
68
|
+
Sandbox::Safe.activate! will also isolate the sandbox environment from the
|
69
|
+
filesystem using FakeFS.
|
70
|
+
|
71
|
+
>> require 'sandbox'
|
72
|
+
=> true
|
73
|
+
>> s = Sandbox.safe
|
74
|
+
=> #<Sandbox::Safe:0x3fdb8a73>
|
75
|
+
>> s.eval('Dir["/"]')
|
76
|
+
=> ["/"]
|
77
|
+
>> s.eval('Dir["/*"]')
|
78
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
79
|
+
> s.activate!
|
80
|
+
>> s.eval('Dir["/*"]')
|
81
|
+
=> []
|
82
|
+
> Dir['/*']
|
83
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
84
|
+
|
85
|
+
## Known Issues / TODOs
|
86
|
+
|
87
|
+
* There is currently no timeout support, so it's possible for a
|
88
|
+
sandbox to loop indefinitely and block the host interpreter.
|
89
|
+
|
90
|
+
[1]: http://ola-bini.blogspot.com/2006/12/freaky-freaky-sandbox-has-come-to-jruby.html
|
91
|
+
[2]: http://about.travis-ci.org/docs/user/ci-environment/#Ruby-(aka-common)-VM-images
|
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,35 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyClass;
|
5
|
+
import org.jruby.anno.JRubyClass;
|
6
|
+
import org.jruby.anno.JRubyMethod;
|
7
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
8
|
+
import org.jruby.runtime.Block;
|
9
|
+
|
10
|
+
@JRubyClass(name="BoxedClass")
|
11
|
+
public class BoxedClass {
|
12
|
+
protected static RubyClass createBoxedClassClass(final Ruby runtime) {
|
13
|
+
RubyClass cObject = runtime.getObject();
|
14
|
+
RubyClass cBoxedClass = runtime.defineClass("BoxedClass", cObject, cObject.getAllocator());
|
15
|
+
cBoxedClass.defineAnnotatedMethods(BoxedClass.class);
|
16
|
+
|
17
|
+
return cBoxedClass;
|
18
|
+
}
|
19
|
+
|
20
|
+
@JRubyMethod(module=true, rest=true)
|
21
|
+
public static IRubyObject method_missing(IRubyObject recv, IRubyObject[] args, Block block) {
|
22
|
+
IRubyObject[] args2 = new IRubyObject[args.length - 1];
|
23
|
+
System.arraycopy(args, 1, args2, 0, args2.length);
|
24
|
+
String name = args[0].toString();
|
25
|
+
|
26
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
27
|
+
return box.runMethod(recv, name, args2, block);
|
28
|
+
}
|
29
|
+
|
30
|
+
@JRubyMethod(name="new", meta=true, rest=true)
|
31
|
+
public static IRubyObject _new(IRubyObject recv, IRubyObject[] args, Block block) {
|
32
|
+
SandboxFull box = (SandboxFull) SandboxFull.getLinkedBox(recv);
|
33
|
+
return box.runMethod(recv, "new", args, block);
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,317 @@
|
|
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.ObjectAllocator;
|
18
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
19
|
+
import org.jruby.common.IRubyWarnings;
|
20
|
+
import org.jruby.exceptions.RaiseException;
|
21
|
+
import org.jruby.runtime.DynamicScope;
|
22
|
+
|
23
|
+
|
24
|
+
@JRubyClass(name="Sandbox::Full")
|
25
|
+
public class SandboxFull extends RubyObject {
|
26
|
+
protected static ObjectAllocator FULL_ALLOCATOR = new ObjectAllocator() {
|
27
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
28
|
+
return new SandboxFull(runtime, klass);
|
29
|
+
}
|
30
|
+
};
|
31
|
+
|
32
|
+
private Ruby wrapped;
|
33
|
+
private DynamicScope currentScope;
|
34
|
+
|
35
|
+
protected SandboxFull(Ruby runtime, RubyClass type) {
|
36
|
+
super(runtime, type);
|
37
|
+
reload();
|
38
|
+
}
|
39
|
+
|
40
|
+
@JRubyMethod
|
41
|
+
public IRubyObject reload() {
|
42
|
+
RubyInstanceConfig cfg = new RubyInstanceConfig();
|
43
|
+
cfg.setObjectSpaceEnabled(getRuntime().getInstanceConfig().isObjectSpaceEnabled());
|
44
|
+
cfg.setInput(getRuntime().getInstanceConfig().getInput());
|
45
|
+
cfg.setOutput(getRuntime().getInstanceConfig().getOutput());
|
46
|
+
cfg.setError(getRuntime().getInstanceConfig().getError());
|
47
|
+
cfg.setCompatVersion(CompatVersion.RUBY1_9);
|
48
|
+
cfg.setScriptFileName("(sandbox)");
|
49
|
+
|
50
|
+
SandboxProfile profile = new SandboxProfile(this);
|
51
|
+
cfg.setProfile(profile);
|
52
|
+
|
53
|
+
wrapped = Ruby.newInstance(cfg);
|
54
|
+
currentScope = wrapped.getCurrentContext().getCurrentScope();
|
55
|
+
|
56
|
+
BoxedClass.createBoxedClassClass(wrapped);
|
57
|
+
|
58
|
+
return this;
|
59
|
+
}
|
60
|
+
|
61
|
+
@JRubyMethod(required=1)
|
62
|
+
public IRubyObject eval(IRubyObject str) {
|
63
|
+
try {
|
64
|
+
IRubyObject result = wrapped.evalScriptlet(str.asJavaString(), currentScope);
|
65
|
+
return unbox(result);
|
66
|
+
} catch (RaiseException e) {
|
67
|
+
String msg = e.getException().callMethod(wrapped.getCurrentContext(), "message").asJavaString();
|
68
|
+
String path = e.getException().type().getName();
|
69
|
+
RubyClass eSandboxException = (RubyClass) getRuntime().getClassFromPath("Sandbox::SandboxException");
|
70
|
+
throw new RaiseException(getRuntime(), eSandboxException, path + ": " + msg, false);
|
71
|
+
} catch (Exception e) {
|
72
|
+
e.printStackTrace();
|
73
|
+
getRuntime().getWarnings().warn(IRubyWarnings.ID.MISCELLANEOUS, "NativeException: " + e);
|
74
|
+
return getRuntime().getNil();
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
@JRubyMethod(name="import", required=1)
|
79
|
+
public IRubyObject _import(IRubyObject klass) {
|
80
|
+
if (!(klass instanceof RubyModule)) {
|
81
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
82
|
+
}
|
83
|
+
String name = ((RubyModule) klass).getName();
|
84
|
+
importClassPath(name, false);
|
85
|
+
return getRuntime().getNil();
|
86
|
+
}
|
87
|
+
|
88
|
+
@JRubyMethod(required=1)
|
89
|
+
public IRubyObject ref(IRubyObject klass) {
|
90
|
+
if (!(klass instanceof RubyModule)) {
|
91
|
+
throw getRuntime().newTypeError(klass, getRuntime().getClass("Module"));
|
92
|
+
}
|
93
|
+
String name = ((RubyModule) klass).getName();
|
94
|
+
importClassPath(name, true);
|
95
|
+
return getRuntime().getNil();
|
96
|
+
}
|
97
|
+
|
98
|
+
private RubyModule importClassPath(String path, final boolean link) {
|
99
|
+
RubyModule runtimeModule = getRuntime().getObject();
|
100
|
+
RubyModule wrappedModule = wrapped.getObject();
|
101
|
+
|
102
|
+
if (path.startsWith("#")) {
|
103
|
+
throw getRuntime().newArgumentError("can't import anonymous class " + path);
|
104
|
+
}
|
105
|
+
|
106
|
+
for (String name : path.split("::")) {
|
107
|
+
runtimeModule = (RubyModule) runtimeModule.getConstantAt(name);
|
108
|
+
// Create the module when it did not exist yet...
|
109
|
+
if (wrappedModule.const_defined_p(wrapped.getCurrentContext(), wrapped.newString(name)).isFalse()) {
|
110
|
+
// The BoxedClass takes the place of Object as top of the inheritance
|
111
|
+
// hierarchy. As a result, we can intercept all new instances that are
|
112
|
+
// created and all method_missing calls.
|
113
|
+
RubyModule sup = wrapped.getClass("BoxedClass");
|
114
|
+
if (!link && runtimeModule instanceof RubyClass) {
|
115
|
+
// If we're importing a class, recursively import all of its
|
116
|
+
// superclasses as well.
|
117
|
+
sup = importClassPath(runtimeModule.getSuperClass().getName(), true);
|
118
|
+
}
|
119
|
+
|
120
|
+
RubyClass klass = (RubyClass) sup;
|
121
|
+
if (wrappedModule == wrapped.getObject()) {
|
122
|
+
|
123
|
+
if (link || runtimeModule instanceof RubyClass) { // if this is a ref and not an import
|
124
|
+
wrappedModule = wrapped.defineClass(name, klass, klass.getAllocator());
|
125
|
+
} else {
|
126
|
+
wrappedModule = wrapped.defineModule(name);
|
127
|
+
}
|
128
|
+
|
129
|
+
} else {
|
130
|
+
if (runtimeModule instanceof RubyClass) {
|
131
|
+
wrappedModule = wrappedModule.defineClassUnder(name, klass, klass.getAllocator());
|
132
|
+
} else {
|
133
|
+
wrappedModule = wrappedModule.defineModuleUnder(name);
|
134
|
+
}
|
135
|
+
|
136
|
+
}
|
137
|
+
} else {
|
138
|
+
// ...or just resolve it, if it was already known
|
139
|
+
wrappedModule = (RubyModule) wrappedModule.getConstantAt(name);
|
140
|
+
}
|
141
|
+
|
142
|
+
// Check the consistency of the hierarchy
|
143
|
+
if (runtimeModule instanceof RubyClass) {
|
144
|
+
if (!link && !runtimeModule.getSuperClass().getName().equals(wrappedModule.getSuperClass().getName())) {
|
145
|
+
throw getRuntime().newTypeError("superclass mismatch for class " + runtimeModule.getSuperClass().getName());
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
if (link || runtimeModule instanceof RubyClass) {
|
150
|
+
linkObject(runtimeModule, wrappedModule);
|
151
|
+
} else {
|
152
|
+
copyMethods(runtimeModule, wrappedModule);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
return runtimeModule;
|
157
|
+
}
|
158
|
+
|
159
|
+
private void copyMethods(RubyModule from, RubyModule to) {
|
160
|
+
to.getMethodsForWrite().putAll(from.getMethods());
|
161
|
+
to.getSingletonClass().getMethodsForWrite().putAll(from.getSingletonClass().getMethods());
|
162
|
+
}
|
163
|
+
|
164
|
+
@JRubyMethod(required=2)
|
165
|
+
public IRubyObject keep_methods(IRubyObject className, IRubyObject methods) {
|
166
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
167
|
+
if (module != null) {
|
168
|
+
keepMethods(module, methods.convertToArray());
|
169
|
+
}
|
170
|
+
return methods;
|
171
|
+
}
|
172
|
+
|
173
|
+
@JRubyMethod(required=2)
|
174
|
+
public IRubyObject keep_singleton_methods(IRubyObject className, IRubyObject methods) {
|
175
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
176
|
+
if (module != null) {
|
177
|
+
keepMethods(module, methods.convertToArray());
|
178
|
+
}
|
179
|
+
return methods;
|
180
|
+
}
|
181
|
+
|
182
|
+
private void keepMethods(RubyModule module, Collection retain) {
|
183
|
+
for (Map.Entry<String, DynamicMethod> methodEntry : module.getMethods().entrySet()) {
|
184
|
+
String methodName = methodEntry.getKey();
|
185
|
+
if (!retain.contains(methodName)) {
|
186
|
+
removeMethod(module, methodName);
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
@JRubyMethod(required=2)
|
192
|
+
public IRubyObject remove_method(IRubyObject className, IRubyObject methodName) {
|
193
|
+
RubyModule module = wrapped.getModule(className.asJavaString());
|
194
|
+
if (module != null) {
|
195
|
+
removeMethod(module, methodName.asJavaString());
|
196
|
+
}
|
197
|
+
return getRuntime().getNil();
|
198
|
+
}
|
199
|
+
|
200
|
+
@JRubyMethod(required=2)
|
201
|
+
public IRubyObject remove_singleton_method(IRubyObject className, IRubyObject methodName) {
|
202
|
+
RubyModule module = wrapped.getModule(className.asJavaString()).getSingletonClass();
|
203
|
+
if (module != null) {
|
204
|
+
removeMethod(module, methodName.asJavaString());
|
205
|
+
}
|
206
|
+
return getRuntime().getNil();
|
207
|
+
}
|
208
|
+
|
209
|
+
private void removeMethod(RubyModule module, String methodName) {
|
210
|
+
// System.err.println("removing method " + methodName + " from " + module.inspect().asJavaString());
|
211
|
+
module.removeMethod(wrapped.getCurrentContext(), methodName);
|
212
|
+
}
|
213
|
+
|
214
|
+
@JRubyMethod(required=1)
|
215
|
+
public IRubyObject load(IRubyObject str) {
|
216
|
+
try {
|
217
|
+
wrapped.getLoadService().load(str.asJavaString(), true);
|
218
|
+
return getRuntime().getTrue();
|
219
|
+
} catch (RaiseException e) {
|
220
|
+
e.printStackTrace();
|
221
|
+
return getRuntime().getFalse();
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
@JRubyMethod(required=1)
|
226
|
+
public IRubyObject require(IRubyObject str) {
|
227
|
+
try {
|
228
|
+
IRubyObject result = RubyKernel.require(wrapped.getKernel(), wrapped.newString(str.asJavaString()), Block.NULL_BLOCK);
|
229
|
+
return unbox(result);
|
230
|
+
} catch (RaiseException e) {
|
231
|
+
e.printStackTrace();
|
232
|
+
return getRuntime().getFalse();
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
private IRubyObject unbox(IRubyObject obj) {
|
237
|
+
return box(obj);
|
238
|
+
}
|
239
|
+
|
240
|
+
private IRubyObject rebox(IRubyObject obj) {
|
241
|
+
return box(obj);
|
242
|
+
}
|
243
|
+
|
244
|
+
private IRubyObject box(IRubyObject obj) {
|
245
|
+
if (obj.isImmediate()) {
|
246
|
+
return cross(obj);
|
247
|
+
} else {
|
248
|
+
// If this object already existed and was returned from the wrapped
|
249
|
+
// runtime on an earlier occasion, it will already contain a link to its
|
250
|
+
// brother in the regular runtime and we can safely return that link.
|
251
|
+
IRubyObject link = getLinkedObject(obj);
|
252
|
+
if (!link.isNil()) {
|
253
|
+
IRubyObject box = getLinkedBox(obj);
|
254
|
+
if (box == this) return link;
|
255
|
+
}
|
256
|
+
|
257
|
+
// Is the class already known on both sides of the fence?
|
258
|
+
IRubyObject klass = constFind(obj.getMetaClass().getRealClass().getName());
|
259
|
+
link = getRuntime().getNil();
|
260
|
+
if (!klass.isNil()) {
|
261
|
+
link = getLinkedObject(klass);
|
262
|
+
}
|
263
|
+
|
264
|
+
if (link.isNil()) {
|
265
|
+
return cross(obj);
|
266
|
+
} else {
|
267
|
+
IRubyObject v = ((RubyClass)klass).allocate();
|
268
|
+
linkObject(obj, v);
|
269
|
+
return v;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
private IRubyObject cross(IRubyObject obj) {
|
275
|
+
IRubyObject dumped = wrapped.getModule("Marshal").callMethod(wrapped.getCurrentContext(), "dump", obj);
|
276
|
+
return getRuntime().getModule("Marshal").callMethod(getRuntime().getCurrentContext(), "load", dumped);
|
277
|
+
}
|
278
|
+
|
279
|
+
protected static IRubyObject getLinkedObject(IRubyObject arg) {
|
280
|
+
IRubyObject object = arg.getRuntime().getNil();
|
281
|
+
if (arg.getInstanceVariables().hasInstanceVariable("__link__")) {
|
282
|
+
object = (IRubyObject) arg.getInstanceVariables().getInstanceVariable("__link__");
|
283
|
+
}
|
284
|
+
return object;
|
285
|
+
}
|
286
|
+
|
287
|
+
protected static IRubyObject getLinkedBox(IRubyObject arg) {
|
288
|
+
IRubyObject object = arg.getRuntime().getNil();
|
289
|
+
if (arg.getInstanceVariables().hasInstanceVariable("__box__")) {
|
290
|
+
object = (IRubyObject) arg.getInstanceVariables().getInstanceVariable("__box__");
|
291
|
+
}
|
292
|
+
return object;
|
293
|
+
}
|
294
|
+
|
295
|
+
private void linkObject(IRubyObject runtimeObject, IRubyObject wrappedObject) {
|
296
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__link__", runtimeObject);
|
297
|
+
wrappedObject.getInstanceVariables().setInstanceVariable("__box__", this);
|
298
|
+
}
|
299
|
+
|
300
|
+
private IRubyObject constFind(String path) {
|
301
|
+
try {
|
302
|
+
return wrapped.getClassFromPath(path);
|
303
|
+
} catch (Exception e) {
|
304
|
+
return wrapped.getNil();
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
protected IRubyObject runMethod(IRubyObject recv, String name, IRubyObject[] args, Block block) {
|
309
|
+
IRubyObject[] args2 = new IRubyObject[args.length];
|
310
|
+
for (int i = 0; i < args.length; i++) {
|
311
|
+
args2[i] = unbox(args[i]);
|
312
|
+
}
|
313
|
+
IRubyObject recv2 = unbox(recv);
|
314
|
+
IRubyObject result = recv2.callMethod(getRuntime().getCurrentContext(), name, args2, block);
|
315
|
+
return rebox(result);
|
316
|
+
}
|
317
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import org.jruby.Profile;
|
4
|
+
import org.jruby.Ruby;
|
5
|
+
import org.jruby.RubyClass;
|
6
|
+
import org.jruby.RubyModule;
|
7
|
+
import org.jruby.anno.JRubyClass;
|
8
|
+
import org.jruby.anno.JRubyMethod;
|
9
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
10
|
+
|
11
|
+
public class SandboxModule {
|
12
|
+
/**
|
13
|
+
* Create the Sandbox module and add it to the Ruby runtime.
|
14
|
+
*/
|
15
|
+
public static RubyModule createSandboxModule(final Ruby runtime) {
|
16
|
+
RubyModule mSandbox = runtime.defineModule("Sandbox");
|
17
|
+
mSandbox.defineAnnotatedMethods(SandboxModule.class);
|
18
|
+
|
19
|
+
RubyClass cObject = runtime.getObject();
|
20
|
+
RubyClass cSandboxFull = mSandbox.defineClassUnder("Full", cObject, SandboxFull.FULL_ALLOCATOR);
|
21
|
+
cSandboxFull.defineAnnotatedMethods(SandboxFull.class);
|
22
|
+
RubyClass cStandardError = runtime.getStandardError();
|
23
|
+
RubyClass cSandboxException = mSandbox.defineClassUnder("SandboxException", cStandardError, cStandardError.getAllocator());
|
24
|
+
|
25
|
+
return mSandbox;
|
26
|
+
}
|
27
|
+
|
28
|
+
@JRubyClass(name="Sandbox::SandboxException", parent="StandardError")
|
29
|
+
public static class SandboxException {}
|
30
|
+
|
31
|
+
@JRubyMethod(name="current", meta=true)
|
32
|
+
public static IRubyObject s_current(IRubyObject recv) {
|
33
|
+
Profile prof = recv.getRuntime().getProfile();
|
34
|
+
if (prof instanceof SandboxProfile) {
|
35
|
+
return ((SandboxProfile) prof).getSandbox();
|
36
|
+
}
|
37
|
+
return recv.getRuntime().getNil();
|
38
|
+
}
|
39
|
+
}
|
@@ -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,17 @@
|
|
1
|
+
package sandbox;
|
2
|
+
|
3
|
+
import java.io.IOException;
|
4
|
+
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
7
|
+
|
8
|
+
public class SandboxService implements BasicLibraryService {
|
9
|
+
public boolean basicLoad(Ruby runtime) throws IOException {
|
10
|
+
init(runtime);
|
11
|
+
return true;
|
12
|
+
}
|
13
|
+
|
14
|
+
private void init(Ruby runtime) {
|
15
|
+
SandboxModule.createSandboxModule(runtime);
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,28 @@
|
|
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 = "xplenty-jruby_sandbox"
|
7
|
+
s.version = Sandbox::VERSION
|
8
|
+
s.platform = "java"
|
9
|
+
s.authors = ["Dray Lacy", "Eric Allam", "Moty Michaely"]
|
10
|
+
s.email = ["dray@envylabs.com", "eric@envylabs.com", "moty.mi@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/xplenty/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
|
+
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
s.add_development_dependency "rake-compiler"
|
26
|
+
s.add_development_dependency "rspec"
|
27
|
+
s.add_development_dependency "yard"
|
28
|
+
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
|