dia 1.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ module Dia
2
+
3
+ module SharedFeatures
4
+
5
+ # @return [Fixnum, nil] Returns the Process ID(PID) of the last child process that was
6
+ # used to execute a sandbox.
7
+ # Returns nil if #run or #run_nonblock has not been called yet.
8
+ def pid
9
+ @pid
10
+ end
11
+
12
+ # The exit_status method will return the exit status of your sandbox.
13
+ # This method *will* block until the child process(your sandbox) exits
14
+ # when being used with #run_nonblock.
15
+ #
16
+ # @return [Fixnum, nil] Returns the exit status of your sandbox as a
17
+ # Fixnum.
18
+ # Returns nil if #run or #run_nonblock has not
19
+ # been called yet.
20
+ # Returns nil if the process hasn't exited yet and
21
+ # #run is being used.
22
+ # @since 1.5
23
+ def exit_status()
24
+ unless @exit_status.nil?
25
+ Thread === @exit_status ? @exit_status.value().exitstatus() :
26
+ @exit_status.exitstatus()
27
+ end
28
+ end
29
+
30
+ # The terminate method will send the SIGKILL signal to your sandbox.
31
+ #
32
+ # To prevent the possible accumulation of zombies, this method will
33
+ # wait to collect the exit status of your sandbox if it doesn't appear
34
+ # to have left the process table after sending SIGKILL.
35
+ #
36
+ # This is a rare event, and when it does happen #terminate shouldn't block
37
+ # for more than one second.
38
+ #
39
+ # @raise [SystemCallError] It may raise a number of subclasses of
40
+ # SystemCallError if a call to Process.kill
41
+ # was unsuccessful
42
+ #
43
+ # @return [Fixnum, nil] Returns 1 when successful.
44
+ # Returns nil if #run or #run_nonblock has not
45
+ # been called yet.
46
+ def terminate()
47
+ ret = Process.kill('SIGKILL', @pid) unless @pid.nil?
48
+ # precaution against the collection of zombie processes.
49
+ _ , @exit_status = Process.wait2(@pid) if running?
50
+ ret
51
+ end
52
+
53
+ # This method will tell you if your sandbox is still running by returning a boolean.
54
+ #
55
+ # @raise [SystemCallError] It may raise a number of subclasses of SystemCallError
56
+ # if a signal cannot be sent to the process running
57
+ # a sandbox.
58
+ #
59
+ # @return [Boolean,nil] Returns true, false, or nil.
60
+ # Returns nil if #run or #run_nonblock has
61
+ # not been called yet.
62
+ def running?()
63
+ if @pid.nil?
64
+ nil
65
+ else
66
+ begin
67
+ Process.kill(0, @pid)
68
+ true
69
+ rescue Errno::ESRCH
70
+ false
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ # @api private
78
+ def initialize_sandbox
79
+ if Dia::Functions.sandbox_init(FFI::MemoryPointer.from_string(@profile),
80
+ 0x0001,
81
+ err = FFI::MemoryPointer.new(:pointer)) \
82
+ == -1
83
+
84
+ raise(Dia::Exceptions::SandboxException, "Failed to initialize sandbox" \
85
+ "(#{err.read_pointer.read_string})")
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -1,4 +1,5 @@
1
- require 'rubygems'
2
- require 'socket'
3
- require 'baretest'
4
- require File.join(File.dirname(__FILE__), '..', 'lib', 'dia')
1
+ require('rubygems')
2
+ require('socket')
3
+ require('baretest')
4
+ require(File.expand_path('../lib/dia', File.dirname(__FILE__)))
5
+
@@ -0,0 +1,40 @@
1
+ suite('Dia::ExceptionStruct') do
2
+
3
+ setup do
4
+ @sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
5
+ raise(RuntimeError, 'Example')
6
+ end
7
+ @sandbox.rescue_exception = true
8
+ end
9
+
10
+ suite('#klass') do
11
+ exercise('@rescue is set to true. A RuntimeError is raised in a sandbox. ') do
12
+ @sandbox.run
13
+ end
14
+
15
+ verify('#klass returns RuntimeError as a String') do
16
+ @sandbox.exception.klass == 'RuntimeError'
17
+ end
18
+ end
19
+
20
+ suite('#message') do
21
+ exercise('@rescue is set to true. A RuntimeError is raised in a sandbox. ') do
22
+ @sandbox.run
23
+ end
24
+
25
+ verify("#message returns 'Example' as a String") do
26
+ @sandbox.exception.message == 'Example'
27
+ end
28
+ end
29
+
30
+ suite('#backtrace') do
31
+ exercise('@rescue is set to true. A RuntimeError is raised in a sandbox. ') do
32
+ @sandbox.run
33
+ end
34
+
35
+ verify('#backtrace returns a string') do
36
+ @sandbox.exception.backtrace.class == String
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,561 @@
1
+ suite('Dia::RubyBlock') do
2
+
3
+ suite('misc') do
4
+
5
+ exercise("An invalid profile is passed to Dia::RubyBlock.new, and #run is called.") do
6
+ sandbox = Dia::RubyBlock.new('invalid_profile') { }
7
+ sandbox.rescue_exception = true
8
+ sandbox.run
9
+ @result = sandbox.exception.klass
10
+ end
11
+
12
+ verify('Dia::RubyBlock#exception returns an instance of Dia::Exceptions::SandboxException') do
13
+ @result == "Dia::Exceptions::SandboxException"
14
+ end
15
+
16
+ end
17
+
18
+ suite('#initialize') do
19
+
20
+ setup do
21
+ @result = nil
22
+ end
23
+
24
+ exercise('No block is passed to the constructor. ') do
25
+ begin
26
+ Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES)
27
+ rescue ArgumentError => e
28
+ @result = true
29
+ end
30
+ end
31
+
32
+ verify('ArgumentError is raised.') do
33
+ @result
34
+ end
35
+
36
+ end
37
+
38
+ suite('#pid') do
39
+
40
+ exercise('#run is called, then #pid is called. ') do
41
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) { }
42
+ sandbox.run
43
+ @result = sandbox.pid
44
+ end
45
+
46
+ verify('#pid returns a Fixnum object') do
47
+ @result.class == Fixnum
48
+ end
49
+
50
+ end
51
+
52
+ suite('#stderr') do
53
+
54
+ exercise('@rescue set to true, @redirect_stderr set to true, #run called, ' \
55
+ '$stderr written to. ') do
56
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
57
+ $stderr.print "Hello, world $stderr!"
58
+ end
59
+ sandbox.rescue_exception = true
60
+ sandbox.redirect_stderr = true
61
+ sandbox.run
62
+ @result = sandbox.stderr
63
+ end
64
+
65
+ verify('#stderr returns the contents of $stderr.') do
66
+ @result == 'Hello, world $stderr!'
67
+ end
68
+
69
+ exercise('@rescue set to true, @redirect_stderr set to true, #run called, ' \
70
+ 'STDERR written to. ') do
71
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
72
+ STDERR.print "Hello, world STDERR!"
73
+ end
74
+ sandbox.rescue_exception = true
75
+ sandbox.redirect_stderr = true
76
+ sandbox.run
77
+ @result = sandbox.stderr
78
+ end
79
+
80
+ verify('#stderr returns the contents of STDERR.') do
81
+ @result == 'Hello, world STDERR!'
82
+ end
83
+
84
+ exercise('@rescue set to false, @redirect_stderr set to true, #run called, ' \
85
+ '$stderr written to. ') do
86
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
87
+ $stderr.print "Hello, world $stderr!"
88
+ end
89
+ sandbox.redirect_stderr = true
90
+ sandbox.run
91
+ @result = sandbox.stderr
92
+ end
93
+
94
+ verify('#stderr returns the contents of $stderr.') do
95
+ @result == 'Hello, world $stderr!'
96
+ end
97
+
98
+ exercise('@rescue set to false, @redirect_stderr set to true, #run called, ' \
99
+ 'STDOUT written to. ') do
100
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
101
+ STDERR.print "Hello, world STDERR!"
102
+ end
103
+ sandbox.redirect_stderr = true
104
+ sandbox.run
105
+ @result = sandbox.stderr
106
+ end
107
+
108
+ verify('#stderr returns the contents of STDERR.') do
109
+ @result == 'Hello, world STDERR!'
110
+ end
111
+
112
+ exercise('@redirect_stderr set to false, #run called. ') do
113
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
114
+ $stderr.print ""
115
+ STDERR.print ""
116
+ end
117
+ sandbox.run
118
+ @result = sandbox.stderr
119
+ end
120
+
121
+ verify('#stderr returns nil') do
122
+ @result == nil
123
+ end
124
+
125
+ end
126
+
127
+ suite('#stdout') do
128
+
129
+ setup do
130
+ @result = nil
131
+ end
132
+
133
+ exercise('@rescue set to true, @redirect_stdout set to true, #run called, ' \
134
+ '$stdout written to. ') do
135
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
136
+ $stdout.print "Hello, world!"
137
+ end
138
+ sandbox.rescue_exception = true
139
+ sandbox.redirect_stdout = true
140
+ sandbox.run
141
+ @result = sandbox.stdout
142
+ end
143
+
144
+ verify('#stdout returns the contents of $stdout.') do
145
+ @result == 'Hello, world!'
146
+ end
147
+
148
+ exercise('@rescue set to true, @redirect_stdout set to true, #run called, ' \
149
+ 'STDOUT written to.') do
150
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
151
+ STDOUT.print "Goodbye, world!"
152
+ end
153
+ sandbox.rescue_exception = true
154
+ sandbox.redirect_stdout = true
155
+ sandbox.run
156
+ @result = sandbox.stdout
157
+ end
158
+
159
+ verify('#stdout returns the contents of stdout of STDOUT') do
160
+ @result == 'Goodbye, world!'
161
+ end
162
+
163
+ exercise('@rescue set to false, @redirect_stdout set to true, #run called, ' \
164
+ '$stdout written to. ') do
165
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
166
+ $stdout.print "Hello, world!"
167
+ end
168
+ sandbox.redirect_stdout = true
169
+ sandbox.run
170
+ @result = sandbox.stdout
171
+ end
172
+
173
+ verify('#stdout returns the contents of $stdout') do
174
+ @result == 'Hello, world!'
175
+ end
176
+
177
+ exercise('@rescue set to false, @redirect_stdout set to true, #run called, ' \
178
+ 'STDOUT written to. ') do
179
+
180
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
181
+ STDOUT.print "Goodbye, world!"
182
+ end
183
+ sandbox.redirect_stdout = true
184
+ sandbox.run
185
+ @result = sandbox.stdout
186
+ end
187
+
188
+ verify('#stdout returns the contents of STDOUT') do
189
+ @result == 'Goodbye, world!'
190
+ end
191
+
192
+ exercise('@redirect_stdout set to false, #run called. ') do
193
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
194
+ $stdout.print ""
195
+ STDOUT.print ""
196
+ end
197
+ sandbox.run
198
+ @result = sandbox.stdout
199
+ end
200
+
201
+ verify('#stdout returns nil') do
202
+ @result == nil
203
+ end
204
+
205
+ end
206
+
207
+ suite('#rescue_exception?') do
208
+
209
+ setup do
210
+ @result = nil
211
+ @sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) { }
212
+ end
213
+
214
+ exercise('@rescue is set to false. ') do
215
+ @result = @sandbox.rescue_exception?
216
+ end
217
+
218
+ verify('#rescue_exception? returns false') do
219
+ @result == false
220
+ end
221
+
222
+ exercise('@rescue is set to true. ') do
223
+ @sandbox.rescue_exception = true
224
+ @result = @sandbox.rescue_exception?
225
+ end
226
+
227
+ verify('#rescue_exception? returns true') do
228
+ @result == true
229
+ end
230
+
231
+ end
232
+
233
+ suite('#exception', :tags => [ :exception ] ) do
234
+
235
+ setup do
236
+ @result = nil
237
+ @sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) do
238
+ raise
239
+ end
240
+ end
241
+
242
+ exercise('@rescue is set to false, #run is called, no exception is raised. ') do
243
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) do
244
+ begin
245
+ raise
246
+ rescue => e
247
+ end
248
+ end
249
+
250
+ @result = sandbox.exception
251
+ end
252
+
253
+ verify('#exception returns nil to the parent process.') do
254
+ @result == nil
255
+ end
256
+
257
+ exercise('@rescue is set to true, #run is called, an exception is raised. ') do
258
+ @sandbox.rescue_exception = true
259
+ @sandbox.run
260
+ @result = @sandbox.exception.klass
261
+ end
262
+
263
+ verify('#exception returns raised exception to the parent process.') do
264
+ @result == "RuntimeError"
265
+ end
266
+
267
+ exercise('@rescue is set to true, #run is called, an is exception raised, ' \
268
+ '@rescue is set to false. ') do
269
+ @sandbox.rescue_exception = true
270
+ @sandbox.run
271
+ @sandbox.rescue_exception = false
272
+ @result = @sandbox.exception.klass
273
+ end
274
+
275
+ verify('#exception returns raised exception to parent process') do
276
+ @result == "RuntimeError"
277
+ end
278
+
279
+ exercise('Exception object created, re-define #message with code that will cause ' \
280
+ 'another exception when Dia tries to serialize exception data.') do
281
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) do
282
+ e = Exception.new
283
+ def e.message
284
+ raise(StandardError, 'profit')
285
+ end
286
+ raise(e)
287
+ end
288
+ sandbox.rescue_exception = true
289
+ sandbox.run
290
+ @result = sandbox.exception
291
+ end
292
+
293
+ verify('Second exception is captured and returned by #exception') do
294
+ @result
295
+ end
296
+
297
+ end
298
+
299
+ suite('#run') do
300
+
301
+ setup do
302
+ @result = nil
303
+ @reader, @writer = IO.pipe
304
+ end
305
+
306
+ exercise('Confirm the profile ' \
307
+ 'Dia::Profiles::NO_INTERNET ' \
308
+ 'is creating a working sandbox environment.') do
309
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
310
+ begin
311
+ @reader.close
312
+ TCPSocket.open('http://www.google.com', 80)
313
+ @writer.write('false')
314
+ rescue SocketError, SystemCallError => e
315
+ @writer.write('true')
316
+ ensure
317
+ @writer.close
318
+ end
319
+ end
320
+
321
+ sandbox.run
322
+
323
+ @writer.close
324
+ @result = @reader.gets
325
+ @reader.close
326
+ end
327
+
328
+ verify(nil) do
329
+ @result == 'true'
330
+ end
331
+
332
+ exercise('Confirm the profile ' \
333
+ 'Dia::Profiles::NO_FILESYSTEM_WRITE ' \
334
+ 'is creating a working sandbox environment.') do
335
+
336
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE) do
337
+ begin
338
+ @reader.close
339
+ File.open('/tmp/foo.txt', 'w') { |f| f.puts('fail') }
340
+ @writer.write('false')
341
+ rescue SocketError, SystemCallError => e
342
+ @writer.write('true')
343
+ ensure
344
+ @writer.close
345
+ end
346
+ end
347
+
348
+
349
+ sandbox.run
350
+
351
+ @writer.close
352
+ @result = @reader.gets
353
+ @reader.close
354
+ end
355
+
356
+ verify(nil) do
357
+ @result == 'true'
358
+ end
359
+
360
+ exercise('Confirm the profile ' \
361
+ 'Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP ' \
362
+ 'is creating a working sandbox environment. ') do
363
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP) do
364
+ begin
365
+ @reader.close
366
+ out = Time.now.to_s
367
+ File.open('/tmp/%s.dia_test' % [ out ] , 'w') { |f| f.puts('success') }
368
+ File.open(File.join(ENV['HOME'], 'fail.txt')) { |f| f.puts('fail') }
369
+ @writer.write('false')
370
+ rescue SocketError, SystemCallError => e
371
+ if File.exists?('/tmp/%s.dia_test' % [ out ])
372
+ @writer.write('true')
373
+ else
374
+ @writer.write('false')
375
+ end
376
+ ensure
377
+ @writer.close
378
+ end
379
+ end
380
+
381
+ sandbox.run
382
+
383
+ @writer.close
384
+ @result = @reader.gets
385
+ @reader.close
386
+ end
387
+
388
+ verify(nil) do
389
+ @result == 'true'
390
+ end
391
+
392
+ exercise('Confirm the profile ' \
393
+ 'Dia::Profiles::NO_NETWORKING ' \
394
+ 'is creating a working sandbox environment') do
395
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_NETWORKING) do
396
+ begin
397
+ @reader.close
398
+ TCPSocket.open('http://www.youtube.com', 80)
399
+ @writer.write('false')
400
+ rescue SocketError => e
401
+ @writer.write('true')
402
+ end
403
+ end
404
+
405
+ sandbox.run
406
+
407
+ @writer.close
408
+ @result = @reader.gets
409
+ @reader.close
410
+ end
411
+
412
+ verify(nil) do
413
+ @result == 'true'
414
+ end
415
+
416
+ exercise('#run called. ') do
417
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_OS_SERVICES) { }
418
+ @result = sandbox.run
419
+ end
420
+
421
+ verify('returns the Process ID(PID) of spawned process as a Fixnum') do
422
+ @result.class == Fixnum
423
+ end
424
+
425
+ end
426
+
427
+ suite('#run_nonblock') do
428
+
429
+ setup do
430
+ @result = nil
431
+ @reader, @writer = IO.pipe
432
+ end
433
+
434
+ exercise('Confirm the profile ' \
435
+ 'Dia::Profiles::NO_INTERNET ' \
436
+ 'is creating a working sandbox environment.') do
437
+
438
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
439
+ begin
440
+ @reader.close
441
+ TCPSocket.open('http://www.google.com', 80)
442
+ @writer.write('false')
443
+ rescue SocketError, SystemCallError => e
444
+ @writer.write('true')
445
+ ensure
446
+ @writer.close
447
+ end
448
+ end
449
+
450
+ sandbox.run_nonblock
451
+ sleep(1)
452
+
453
+ @writer.close
454
+ @result = @reader.gets
455
+ @reader.close
456
+ end
457
+
458
+ verify(nil) do
459
+ @result == 'true'
460
+ end
461
+
462
+ exercise('Confirm the profile ' \
463
+ 'Dia::Profiles::NO_FILESYSTEM_WRITE ' \
464
+ 'is creating a working sandbox environment.') do
465
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE) do
466
+ begin
467
+ @reader.close
468
+ File.open('/tmp/foo.txt', 'w') { |f| f.puts('fail') }
469
+ @writer.write('false')
470
+ rescue SocketError, SystemCallError => e
471
+ @writer.write('true')
472
+ ensure
473
+ @writer.close
474
+ end
475
+ end
476
+
477
+
478
+ sandbox.run_nonblock
479
+ sleep(1)
480
+
481
+ @writer.close
482
+ @result = @reader.gets
483
+ @reader.close
484
+ end
485
+
486
+ verify(nil) do
487
+ @result == 'true'
488
+ end
489
+
490
+ exercise('Confirm the profile ' \
491
+ 'Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP ' \
492
+ 'is creating a working sandbox environment. ') do
493
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP) do
494
+ begin
495
+ @reader.close
496
+ out = Time.now.to_s
497
+ File.open('/tmp/%s.dia_test' % [ out ] , 'w') { |f| f.puts('success') }
498
+ File.open(File.join(ENV['HOME'], 'fail.txt')) { |f| f.puts('fail') }
499
+ @writer.write('false')
500
+ rescue SocketError, SystemCallError => e
501
+ if File.exists?('/tmp/%s.dia_test' % [ out ])
502
+ @writer.write('true')
503
+ else
504
+ @writer.write('false')
505
+ end
506
+ ensure
507
+ @writer.close
508
+ end
509
+ end
510
+
511
+ sandbox.run_nonblock
512
+ sleep(1)
513
+
514
+ @writer.close
515
+ @result = @reader.gets
516
+ @reader.close
517
+ end
518
+
519
+ verify(nil) do
520
+ @result == 'true'
521
+ end
522
+
523
+ exercise('Confirm the profile ' \
524
+ 'Dia::Profiles::NO_NETWORKING ' \
525
+ 'is creating a working sandbox environment') do
526
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_NETWORKING) do
527
+ begin
528
+ @reader.close
529
+ TCPSocket.open('http://www.youtube.com', 80)
530
+ @writer.write('false')
531
+ rescue SocketError => e
532
+ @writer.write('true')
533
+ end
534
+ end
535
+
536
+ sandbox.run_nonblock
537
+ sleep(1)
538
+
539
+ @writer.close
540
+ @result = @reader.gets
541
+ @reader.close
542
+ end
543
+
544
+ verify(nil) do
545
+ @result == 'true'
546
+ end
547
+
548
+ exercise('#run_nonblock called. ') do
549
+
550
+ sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
551
+ @result = sandbox.run_nonblock
552
+ end
553
+
554
+ verify('returns the Process ID(PID) of spawned process as a Fixnum') do
555
+ @result.class == Fixnum
556
+ end
557
+
558
+ end
559
+
560
+ end
561
+