jruby-safe 0.2.2-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 +8 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +40 -0
- data/LICENSE +21 -0
- data/README.md +71 -0
- data/Rakefile +24 -0
- data/ext/java/sandbox/BoxedClass.java +35 -0
- data/ext/java/sandbox/SandboxFull.java +356 -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-safe.gemspec +26 -0
- data/lib/jruby-safe.rb +1 -0
- data/lib/sandbox.rb +18 -0
- data/lib/sandbox/binding.rb +27 -0
- data/lib/sandbox/prelude.rb +19 -0
- data/lib/sandbox/safe.rb +397 -0
- data/lib/sandbox/version.rb +3 -0
- data/spec/exploits_spec.rb +185 -0
- data/spec/sandbox_spec.rb +319 -0
- data/spec/support/foo.txt +1 -0
- metadata +140 -0
@@ -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
|
+
}
|
data/jruby-safe.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
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-safe"
|
7
|
+
s.version = Sandbox::VERSION
|
8
|
+
s.platform = "java"
|
9
|
+
s.authors = ["Kazuhiro yamada"]
|
10
|
+
s.email = ["yamadakazu45@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/k-yamada/jruby-safe"
|
12
|
+
s.summary = "Sandbox support for JRuby"
|
13
|
+
s.description = "A version of _why's Freaky Freaky Sandbox for JRuby."
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n") + ["lib/sandbox/sandbox.jar"]
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency "fakefs"
|
21
|
+
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "rake-compiler"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "yard"
|
26
|
+
end
|
data/lib/jruby-safe.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'sandbox'
|
data/lib/sandbox.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "sandbox/binding"
|
2
|
+
require "sandbox/sandbox"
|
3
|
+
require "sandbox/version"
|
4
|
+
require "sandbox/safe"
|
5
|
+
|
6
|
+
module Sandbox
|
7
|
+
PRELUDE = File.expand_path("../sandbox/prelude.rb", __FILE__).freeze # :nodoc:
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def new
|
11
|
+
Full.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def safe
|
15
|
+
Safe.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Binding
|
2
|
+
# Returns the value of some variable.
|
3
|
+
#
|
4
|
+
# a = 2
|
5
|
+
# binding["a"] #=> 2
|
6
|
+
#
|
7
|
+
def [](x)
|
8
|
+
if RUBY_VERSION.to_f > 2.1
|
9
|
+
local_variable_get(x)
|
10
|
+
else
|
11
|
+
eval(x.to_s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Set the value of a local variable.
|
16
|
+
#
|
17
|
+
# b = binding
|
18
|
+
# b["a"] = 4
|
19
|
+
# eval("a", b) #=> 4
|
20
|
+
def []=(l, v)
|
21
|
+
if RUBY_VERSION.to_f > 2.1
|
22
|
+
local_variable_set(l, v)
|
23
|
+
else
|
24
|
+
eval("#{l} = nil; lambda {|v| #{l} = v}").call(v)
|
25
|
+
end
|
26
|
+
end
|
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
|
data/lib/sandbox/safe.rb
ADDED
@@ -0,0 +1,397 @@
|
|
1
|
+
require "fakefs/safe"
|
2
|
+
|
3
|
+
module Sandbox
|
4
|
+
TimeoutError = Class.new(Exception)
|
5
|
+
|
6
|
+
class Safe < Full
|
7
|
+
def activate!
|
8
|
+
activate_fakefs
|
9
|
+
|
10
|
+
keep_singleton_methods(:Kernel, KERNEL_S_METHODS)
|
11
|
+
keep_singleton_methods(:Symbol, SYMBOL_S_METHODS)
|
12
|
+
keep_singleton_methods(:String, STRING_S_METHODS)
|
13
|
+
keep_singleton_methods(:IO, IO_S_METHODS)
|
14
|
+
|
15
|
+
keep_methods(:Kernel, KERNEL_METHODS)
|
16
|
+
keep_methods(:NilClass, NILCLASS_METHODS)
|
17
|
+
keep_methods(:Symbol, SYMBOL_METHODS)
|
18
|
+
keep_methods(:TrueClass, TRUECLASS_METHODS)
|
19
|
+
keep_methods(:FalseClass, FALSECLASS_METHODS)
|
20
|
+
keep_methods(:Enumerable, ENUMERABLE_METHODS)
|
21
|
+
keep_methods(:String, STRING_METHODS)
|
22
|
+
|
23
|
+
Kernel.class_eval do
|
24
|
+
def `(*args) # `<- fix highlight by editor
|
25
|
+
raise NoMethodError, "` is unavailable"
|
26
|
+
end
|
27
|
+
|
28
|
+
def system(*args)
|
29
|
+
raise NoMethodError, "system is unavailable"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def activate_fakefs
|
35
|
+
require "fileutils"
|
36
|
+
|
37
|
+
# unfortunately, the authors of FakeFS used `extend self` in FileUtils, instead of `module_function`.
|
38
|
+
# I fixed it for them
|
39
|
+
(FakeFS::FileUtils.methods - Module.methods - Kernel.methods).each do |module_method_name|
|
40
|
+
FakeFS::FileUtils.send(:module_function, module_method_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
import FakeFS
|
44
|
+
ref FakeFS::Dir
|
45
|
+
ref FakeFS::File
|
46
|
+
ref FakeFS::FileTest
|
47
|
+
import FakeFS::FileUtils #import FileUtils because it is a module
|
48
|
+
|
49
|
+
# this is basically what FakeFS.activate! does, but we want to do it in the sandbox
|
50
|
+
# so we have to live with this:
|
51
|
+
eval <<-RUBY
|
52
|
+
Object.class_eval do
|
53
|
+
remove_const(:Dir)
|
54
|
+
remove_const(:File)
|
55
|
+
remove_const(:FileTest)
|
56
|
+
remove_const(:FileUtils)
|
57
|
+
|
58
|
+
const_set(:Dir, FakeFS::Dir)
|
59
|
+
const_set(:File, FakeFS::File)
|
60
|
+
const_set(:FileUtils, FakeFS::FileUtils)
|
61
|
+
const_set(:FileTest, FakeFS::FileTest)
|
62
|
+
end
|
63
|
+
|
64
|
+
[Dir, File, FileUtils, FileTest].each do |fake_class|
|
65
|
+
fake_class.class_eval do
|
66
|
+
def self.class_eval
|
67
|
+
raise NoMethodError, "class_eval is unavailable"
|
68
|
+
end
|
69
|
+
def self.instance_eval
|
70
|
+
raise NoMethodError, "instance_eval is unavailable"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
RUBY
|
75
|
+
|
76
|
+
FakeFS::FileSystem.clear
|
77
|
+
end
|
78
|
+
|
79
|
+
def eval(code, options={})
|
80
|
+
if options.is_a?(Binding)
|
81
|
+
eval_with_binding(code, options)
|
82
|
+
elsif seconds = options[:timeout]
|
83
|
+
sandbox_timeout(code, seconds) do
|
84
|
+
super code
|
85
|
+
end
|
86
|
+
else
|
87
|
+
super code
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def sandbox_timeout(name, seconds)
|
94
|
+
val, exc = nil
|
95
|
+
|
96
|
+
thread = Thread.start(name) do
|
97
|
+
begin
|
98
|
+
val = yield
|
99
|
+
rescue Exception => exc
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
thread.join(seconds)
|
104
|
+
|
105
|
+
if thread.alive?
|
106
|
+
if thread.respond_to? :kill!
|
107
|
+
thread.kill!
|
108
|
+
else
|
109
|
+
thread.kill
|
110
|
+
end
|
111
|
+
|
112
|
+
timed_out = true
|
113
|
+
end
|
114
|
+
|
115
|
+
if timed_out
|
116
|
+
raise TimeoutError, "#{self.class} timed out"
|
117
|
+
elsif exc
|
118
|
+
raise exc
|
119
|
+
else
|
120
|
+
val
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
IO_S_METHODS = %w[
|
125
|
+
new
|
126
|
+
foreach
|
127
|
+
open
|
128
|
+
]
|
129
|
+
|
130
|
+
KERNEL_S_METHODS = %w[
|
131
|
+
Array
|
132
|
+
binding
|
133
|
+
block_given?
|
134
|
+
catch
|
135
|
+
chomp
|
136
|
+
chomp!
|
137
|
+
chop
|
138
|
+
chop!
|
139
|
+
eval
|
140
|
+
fail
|
141
|
+
Float
|
142
|
+
format
|
143
|
+
global_variables
|
144
|
+
gsub
|
145
|
+
gsub!
|
146
|
+
Integer
|
147
|
+
iterator?
|
148
|
+
lambda
|
149
|
+
local_variables
|
150
|
+
loop
|
151
|
+
method_missing
|
152
|
+
proc
|
153
|
+
raise
|
154
|
+
scan
|
155
|
+
split
|
156
|
+
sprintf
|
157
|
+
String
|
158
|
+
sub
|
159
|
+
sub!
|
160
|
+
throw
|
161
|
+
].freeze
|
162
|
+
|
163
|
+
SYMBOL_S_METHODS = %w[
|
164
|
+
all_symbols
|
165
|
+
].freeze
|
166
|
+
|
167
|
+
STRING_S_METHODS = %w[
|
168
|
+
new
|
169
|
+
].freeze
|
170
|
+
|
171
|
+
KERNEL_METHODS = %w[
|
172
|
+
==
|
173
|
+
===
|
174
|
+
=~
|
175
|
+
Array
|
176
|
+
binding
|
177
|
+
block_given?
|
178
|
+
catch
|
179
|
+
chomp
|
180
|
+
chomp!
|
181
|
+
chop
|
182
|
+
chop!
|
183
|
+
class
|
184
|
+
clone
|
185
|
+
dup
|
186
|
+
eql?
|
187
|
+
equal?
|
188
|
+
eval
|
189
|
+
fail
|
190
|
+
Float
|
191
|
+
format
|
192
|
+
freeze
|
193
|
+
frozen?
|
194
|
+
global_variables
|
195
|
+
gsub
|
196
|
+
gsub!
|
197
|
+
hash
|
198
|
+
id
|
199
|
+
initialize_copy
|
200
|
+
inspect
|
201
|
+
instance_eval
|
202
|
+
instance_of?
|
203
|
+
instance_variables
|
204
|
+
instance_variable_get
|
205
|
+
instance_variable_set
|
206
|
+
instance_variable_defined?
|
207
|
+
Integer
|
208
|
+
is_a?
|
209
|
+
iterator?
|
210
|
+
kind_of?
|
211
|
+
lambda
|
212
|
+
local_variables
|
213
|
+
loop
|
214
|
+
methods
|
215
|
+
method_missing
|
216
|
+
nil?
|
217
|
+
private_methods
|
218
|
+
print
|
219
|
+
proc
|
220
|
+
protected_methods
|
221
|
+
public_methods
|
222
|
+
raise
|
223
|
+
remove_instance_variable
|
224
|
+
respond_to?
|
225
|
+
respond_to_missing?
|
226
|
+
scan
|
227
|
+
send
|
228
|
+
singleton_methods
|
229
|
+
singleton_method_added
|
230
|
+
singleton_method_removed
|
231
|
+
singleton_method_undefined
|
232
|
+
split
|
233
|
+
sprintf
|
234
|
+
String
|
235
|
+
sub
|
236
|
+
sub!
|
237
|
+
taint
|
238
|
+
tainted?
|
239
|
+
throw
|
240
|
+
to_a
|
241
|
+
to_s
|
242
|
+
type
|
243
|
+
untaint
|
244
|
+
__send__
|
245
|
+
].freeze
|
246
|
+
|
247
|
+
NILCLASS_METHODS = %w[
|
248
|
+
&
|
249
|
+
inspect
|
250
|
+
nil?
|
251
|
+
to_a
|
252
|
+
to_f
|
253
|
+
to_i
|
254
|
+
to_s
|
255
|
+
^
|
256
|
+
|
|
257
|
+
].freeze
|
258
|
+
|
259
|
+
SYMBOL_METHODS = %w[
|
260
|
+
===
|
261
|
+
id2name
|
262
|
+
inspect
|
263
|
+
to_i
|
264
|
+
to_int
|
265
|
+
to_s
|
266
|
+
to_sym
|
267
|
+
].freeze
|
268
|
+
|
269
|
+
TRUECLASS_METHODS = %w[
|
270
|
+
&
|
271
|
+
to_s
|
272
|
+
^
|
273
|
+
|
|
274
|
+
].freeze
|
275
|
+
|
276
|
+
FALSECLASS_METHODS = %w[
|
277
|
+
&
|
278
|
+
to_s
|
279
|
+
^
|
280
|
+
|
|
281
|
+
].freeze
|
282
|
+
|
283
|
+
ENUMERABLE_METHODS = %w[
|
284
|
+
all?
|
285
|
+
any?
|
286
|
+
collect
|
287
|
+
detect
|
288
|
+
each_with_index
|
289
|
+
entries
|
290
|
+
find
|
291
|
+
find_all
|
292
|
+
grep
|
293
|
+
include?
|
294
|
+
inject
|
295
|
+
map
|
296
|
+
max
|
297
|
+
member?
|
298
|
+
min
|
299
|
+
partition
|
300
|
+
reject
|
301
|
+
select
|
302
|
+
sort
|
303
|
+
sort_by
|
304
|
+
to_a
|
305
|
+
zip
|
306
|
+
].freeze
|
307
|
+
|
308
|
+
STRING_METHODS = %w[
|
309
|
+
%
|
310
|
+
*
|
311
|
+
+
|
312
|
+
<<
|
313
|
+
<=>
|
314
|
+
==
|
315
|
+
=~
|
316
|
+
capitalize
|
317
|
+
capitalize!
|
318
|
+
casecmp
|
319
|
+
center
|
320
|
+
chomp
|
321
|
+
chomp!
|
322
|
+
chop
|
323
|
+
chop!
|
324
|
+
concat
|
325
|
+
count
|
326
|
+
crypt
|
327
|
+
delete
|
328
|
+
delete!
|
329
|
+
downcase
|
330
|
+
downcase!
|
331
|
+
dump
|
332
|
+
each
|
333
|
+
each_byte
|
334
|
+
each_line
|
335
|
+
empty?
|
336
|
+
eql?
|
337
|
+
gsub
|
338
|
+
gsub!
|
339
|
+
hash
|
340
|
+
hex
|
341
|
+
include?
|
342
|
+
index
|
343
|
+
initialize
|
344
|
+
initialize_copy
|
345
|
+
insert
|
346
|
+
inspect
|
347
|
+
intern
|
348
|
+
length
|
349
|
+
ljust
|
350
|
+
lines
|
351
|
+
lstrip
|
352
|
+
lstrip!
|
353
|
+
match
|
354
|
+
next
|
355
|
+
next!
|
356
|
+
oct
|
357
|
+
replace
|
358
|
+
reverse
|
359
|
+
reverse!
|
360
|
+
rindex
|
361
|
+
rjust
|
362
|
+
rstrip
|
363
|
+
rstrip!
|
364
|
+
scan
|
365
|
+
size
|
366
|
+
slice
|
367
|
+
slice!
|
368
|
+
split
|
369
|
+
squeeze
|
370
|
+
squeeze!
|
371
|
+
strip
|
372
|
+
strip!
|
373
|
+
start_with?
|
374
|
+
sub
|
375
|
+
sub!
|
376
|
+
succ
|
377
|
+
succ!
|
378
|
+
sum
|
379
|
+
swapcase
|
380
|
+
swapcase!
|
381
|
+
to_f
|
382
|
+
to_i
|
383
|
+
to_s
|
384
|
+
to_str
|
385
|
+
to_sym
|
386
|
+
tr
|
387
|
+
tr!
|
388
|
+
tr_s
|
389
|
+
tr_s!
|
390
|
+
upcase
|
391
|
+
upcase!
|
392
|
+
upto
|
393
|
+
[]
|
394
|
+
[]=
|
395
|
+
].freeze
|
396
|
+
end
|
397
|
+
end
|