rufus-treechecker 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt ADDED
@@ -0,0 +1,96 @@
1
+
2
+ = 'rufus-treechecker'
3
+
4
+ == what is it ?
5
+
6
+ Initialize a Rufus::TreeChecker and pass some ruby code to make sure it's safe before calling eval().
7
+
8
+
9
+ == getting it
10
+
11
+ sudo gem install -y rufus-treechecker
12
+
13
+ or download[http://rubyforge.org/frs/?group_id=4812] it from RubyForge.
14
+
15
+
16
+ == usage
17
+
18
+ The treechecker uses ruby_parser (http://rubyforge.org/projects/parsetree)
19
+ to turn Ruby code into s-expressions, the treechecker then
20
+ checks this sexp tree and raises a Rufus::SecurityError if an excluded pattern
21
+ is spotted.
22
+
23
+ The excluded patterns are defined at the initialization of the TreeChecker
24
+ instance by listing rules.
25
+
26
+ require 'rubygems'
27
+ require 'rufus-treechecker'
28
+
29
+ tc = Rufus::TreeChecker.new do
30
+ exclude_fvcall :abort
31
+ exclude_fvcall :exit, :exit!
32
+ end
33
+
34
+ tc.check("1 + 1; abort") # will raise a SecurityError
35
+ tc.check("puts (1..10).to_a.inspect") # OK
36
+
37
+
38
+ Nice, but how do I know what to exclude ?
39
+
40
+ require 'rubygems'
41
+ require 'rufus-treechecker'
42
+
43
+ Rufus::TreeChecker.new.ptree('a = 5 + 6; puts a')
44
+
45
+ will yield
46
+
47
+ "a = 5 + 6; puts a"
48
+ =>
49
+ [:block,
50
+ [:lasgn, :a, [:call, [:lit, 5], :+, [:array, [:lit, 6]]]],
51
+ [:fcall, :puts, [:array, [:lvar, :a]]]
52
+ ]
53
+
54
+
55
+ For more documentation, see http://github.com/jmettraux/rufus-treechecker/tree/master/lib/rufus/treechecker.rb
56
+
57
+
58
+ == dependencies
59
+
60
+ the 'rogue-parser' gem
61
+
62
+
63
+ == mailing list
64
+
65
+ On the Rufus-Ruby list[http://groups.google.com/group/rufus-ruby] :
66
+
67
+ http://groups.google.com/group/rufus-ruby
68
+
69
+
70
+ == issue tracker
71
+
72
+ http://rubyforge.org/tracker/?atid=18584&group_id=4812&func=browse
73
+
74
+
75
+ == source
76
+
77
+ http://github.com/jmettraux/rufus-treechecker
78
+
79
+ git clone git://github.com/jmettraux/rufus-treechecker.git
80
+
81
+
82
+ == author
83
+
84
+ John Mettraux, jmettraux@gmail.com,
85
+ http://jmettraux.wordpress.com
86
+
87
+
88
+ == the rest of Rufus
89
+
90
+ http://rufus.rubyforge.org
91
+
92
+
93
+ == license
94
+
95
+ MIT
96
+
@@ -0,0 +1,529 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2008, John Mettraux, jmettraux@gmail.com
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+ #++
23
+ #
24
+
25
+ #
26
+ # "made in Japan" (as opposed to "swiss made")
27
+ #
28
+
29
+ require 'ruby_parser' # gem 'rogue_parser'
30
+
31
+
32
+ module Rufus
33
+
34
+ #
35
+ # Instances of this error class are thrown when the ruby code being
36
+ # checked contains exclude stuff
37
+ #
38
+ class SecurityError < RuntimeError
39
+ end
40
+
41
+ #
42
+ # TreeChecker relies on ruby_parser to turns a piece of ruby code (a string)
43
+ # into a bunch of sexpression and then TreeChecker will check that
44
+ # sexpression tree and raise a Rufus::SecurityException if an excluded
45
+ # pattern is spotted.
46
+ #
47
+ # The TreeChecker is meant to be useful for people writing DSLs directly
48
+ # in Ruby (not via their own parser) that want to check and prevent
49
+ # bad things from happening in this code.
50
+ #
51
+ # tc = Rufus::TreeChecker.new do
52
+ # exclude_fvcall :abort
53
+ # exclude_fvcall :exit, :exit!
54
+ # end
55
+ #
56
+ # tc.check("1 + 1; abort") # will raise a SecurityError
57
+ # tc.check("puts (1..10).to_a.inspect") # OK
58
+ #
59
+ #
60
+ # == featured exclusion methods
61
+ #
62
+ # === call / vcall / fcall ?
63
+ #
64
+ # What the difference between those ? Well, here is how those various piece
65
+ # of code look like :
66
+ #
67
+ # "exit" => [:vcall, :exit]
68
+ # "Kernel.exit" => [:call, [:const, :Kernel], :exit]
69
+ # "Kernel::exit" => [:call, [:const, :Kernel], :exit]
70
+ # "k.exit" => [:call, [:vcall, :k], :exit]
71
+ # "exit -1" => [:fcall, :exit, [:array, [:lit, -1]]]
72
+ #
73
+ # Obviously :fcall could be labelled as "function call", :call is a call
74
+ # on to some instance, while vcall might either be a variable dereference
75
+ # or a function call with no arguments.
76
+ #
77
+ # === low-level rules
78
+ #
79
+ # - exclude_symbol : bans the usage of a given symbol (very low-level,
80
+ # mostly used by other rules
81
+ # - exclude_head
82
+ # - exclude_fcall
83
+ # - exclude_vcall
84
+ # - exclude_fvcall
85
+ # - exclude_fvkcall
86
+ # - exclude_call_on
87
+ # - exclude_call_to
88
+ # - exclude_def
89
+ # - exclude_class_tinkering
90
+ # - exclude_module_tinkering
91
+ #
92
+ # - at_root
93
+ #
94
+ # === higher level rules
95
+ #
96
+ # Those rules take no arguments
97
+ #
98
+ # - exclude_eval : bans eval, module_eval and instance_eval
99
+ # - exclude_global_vars : bans calling or modifying global vars
100
+ # - exclude_alias : bans calls to alias and alias_method
101
+ # - exclude_vm_exiting : bans exit, abort, ...
102
+ # - exclude_raise : bans calls to raise or throw
103
+ #
104
+ #
105
+ # == a bit further
106
+ #
107
+ # It's possible to clone a TreeChecker and to add some more rules to it :
108
+ #
109
+ # tc0 = Rufus::TreeChecker.new do
110
+ # #
111
+ # # calls to eval, module_eval and instance_eval are not allowed
112
+ # #
113
+ # exclude_eval
114
+ # end
115
+ #
116
+ # tc1 = tc0.clone
117
+ # tc1.add_rules do
118
+ # #
119
+ # # calls to any method on File and FileUtils classes are not allowed
120
+ # #
121
+ # exclude_call_on File, FileUtils
122
+ # end
123
+ #
124
+ class TreeChecker
125
+
126
+ VERSION = '1.0'
127
+
128
+ #
129
+ # pretty-prints the sexp tree of the given rubycode
130
+ #
131
+ def ptree (rubycode)
132
+ puts
133
+ puts rubycode.inspect
134
+ puts " => "
135
+ puts parse(rubycode).inspect
136
+ end
137
+
138
+ #
139
+ # initializes the TreeChecker, expects a block
140
+ #
141
+ def initialize (&block)
142
+
143
+ @root_checks = []
144
+ @checks = []
145
+
146
+ @current_checks = @checks
147
+
148
+ add_rules(&block)
149
+ end
150
+
151
+ def to_s
152
+ p self.class
153
+ puts ' root_checks :'
154
+ puts @root_checks.collect { |r| r.inspect }.join("\n")
155
+ puts ' checks :'
156
+ puts @checks.collect { |r| r.inspect }.join("\n")
157
+ end
158
+
159
+ #
160
+ # Performs the check on the given String of ruby code. Will raise a
161
+ # Rufus::SecurityError if there is something excluded by the rules
162
+ # specified at the initialization of the TreeChecker instance.
163
+ #
164
+ def check (rubycode)
165
+
166
+ sexp = parse(rubycode)
167
+
168
+ @root_checks.each do |meth, *args|
169
+ send meth, sexp, args
170
+ end
171
+
172
+ do_check(sexp)
173
+ end
174
+
175
+ #
176
+ # return a copy of this TreeChecker instance
177
+ #
178
+ def clone
179
+
180
+ copy = TreeChecker.new
181
+ copy.instance_variable_set(:@checks, @checks.dup)
182
+ copy
183
+ end
184
+
185
+ #
186
+ # adds a set of checks (rules) to this treechecker. Returns self.
187
+ #
188
+ def add_rules (&block)
189
+
190
+ instance_eval(&block) if block
191
+
192
+ self
193
+ end
194
+
195
+ #
196
+ # freezes the treechecker instance "in depth"
197
+ #
198
+ def freeze
199
+ super
200
+ @root_checks.freeze
201
+ @root_checks.each { |c| c.freeze }
202
+ @checks.freeze
203
+ @checks.each { |c| c.freeze }
204
+ end
205
+
206
+ #
207
+ # generates a 'classic' tree checker
208
+ #
209
+ # Here is how it's built :
210
+ #
211
+ # return TreeChecker.new do
212
+ # exclude_fvkcall :abort
213
+ # exclude_fvkcall :exit, :exit!
214
+ # exclude_fvkcall :system
215
+ # exclude_eval
216
+ # exclude_alias
217
+ # exclude_global_vars
218
+ # exclude_call_on File, FileUtils
219
+ # exclude_class_tinkering
220
+ # exclude_module_tinkering
221
+ # end
222
+ #
223
+ def self.new_classic_tree_checker
224
+
225
+ return TreeChecker.new do
226
+ exclude_fvkcall :abort
227
+ exclude_fvkcall :exit, :exit!
228
+ exclude_fvkcall :system
229
+ exclude_eval
230
+ exclude_alias
231
+ exclude_global_vars
232
+ exclude_call_on File, FileUtils
233
+ exclude_class_tinkering
234
+ exclude_module_tinkering
235
+ end
236
+ end
237
+
238
+ protected
239
+
240
+ #--
241
+ # the methods used to define the checks
242
+ #++
243
+
244
+ #
245
+ # within the 'at_root' block, rules are added to the @root_checks, ie
246
+ # they are evaluated only for the toplevel (root) sexp.
247
+ #
248
+ def at_root (&block)
249
+
250
+ @current_checks = @root_checks
251
+ add_rules(&block)
252
+ @current_checks = @checks
253
+ end
254
+
255
+ #
256
+ # adds a rule that will forbid sexps that begin with the given head
257
+ #
258
+ # tc = TreeChecker.new do
259
+ # exclude_head [ :block ]
260
+ # end
261
+ #
262
+ # tc.check('a = 2') # ok
263
+ # tc.check('a = 2; b = 5') # will raise an error as it's a block
264
+ #
265
+ def exclude_head (head, message=nil)
266
+
267
+ @current_checks << [ :do_exclude_head, head, message ]
268
+ end
269
+
270
+ [
271
+ :exclude_symbol,
272
+ :exclude_fcall,
273
+ :exclude_vcall,
274
+ :exclude_fvcall,
275
+ :exclude_fvkcall,
276
+ :exclude_call_on,
277
+ :exclude_call_to
278
+
279
+ ].each do |m|
280
+ class_eval <<-EOS
281
+ def #{m} (*args)
282
+
283
+ message = args.last.is_a?(String) ? args.pop : nil
284
+
285
+ args.each do |a|
286
+
287
+ a = [ Class, Module ].include?(a.class) ? \
288
+ parse(a.to_s) : a.to_sym
289
+
290
+ @current_checks << [ :do_#{m}, a, message ]
291
+ end
292
+ end
293
+ EOS
294
+ end
295
+
296
+ #
297
+ # bans method definitions
298
+ #
299
+ def exclude_def
300
+
301
+ @current_checks << [
302
+ :do_exclude_symbol, :defn, 'method definitions are forbidden' ]
303
+ end
304
+
305
+ #
306
+ # bans the defintion and the [re]openening of classes
307
+ #
308
+ # a list of exceptions (classes) can be passed. Subclassing those
309
+ # exceptions is permitted.
310
+ #
311
+ def exclude_class_tinkering (*exceptions)
312
+
313
+ #
314
+ # :class
315
+
316
+ @current_checks << [
317
+ :do_exclude_class_tinkering
318
+ ] + exceptions.collect { |e| parse(e.to_s) }
319
+
320
+ #
321
+ # :sclass
322
+
323
+ @current_checks << [
324
+ :do_exclude_symbol,
325
+ :sclass,
326
+ 'opening the metaclass of an instance is forbidde'
327
+ ]
328
+ end
329
+
330
+ #
331
+ # bans the definition or the opening of modules
332
+ #
333
+ def exclude_module_tinkering
334
+
335
+ @current_checks << [
336
+ :do_exclude_symbol, :module, 'defining or opening a module is forbidden'
337
+ ]
338
+ end
339
+
340
+ #
341
+ # bans referencing or setting the value of global variables
342
+ #
343
+ def exclude_global_vars
344
+
345
+ @current_checks << [
346
+ :do_exclude_symbol, :gvar, "global vars are forbidden" ]
347
+ @current_checks << [
348
+ :do_exclude_symbol, :gasgn, "global vars are forbidden" ]
349
+ end
350
+
351
+ #
352
+ # bans the usage of 'alias'
353
+ #
354
+ def exclude_alias
355
+
356
+ @current_checks << [
357
+ :do_exclude_symbol, :alias, "'alias' is forbidden" ]
358
+ @current_checks << [
359
+ :do_exclude_symbol, :alias_method, "'alias_method' is forbidden" ]
360
+ end
361
+
362
+ #
363
+ # bans the use of 'eval', 'module_eval' and 'instance_eval'
364
+ #
365
+ def exclude_eval
366
+
367
+ @current_checks << [
368
+ :do_exclude_fcall,
369
+ :eval,
370
+ "eval() is forbidden" ]
371
+ @current_checks << [
372
+ :do_exclude_call_to,
373
+ :instance_eval,
374
+ "instance_eval() is forbidden" ]
375
+ @current_checks << [
376
+ :do_exclude_call_to,
377
+ :module_eval,
378
+ "module_eval() is forbidden" ]
379
+ end
380
+
381
+ #
382
+ # bans the use of backquotes
383
+ #
384
+ def exclude_backquotes
385
+ @current_checks << [
386
+ :do_exclude_symbol, :xstr, "backquotes are forbidden" ]
387
+ end
388
+
389
+ #
390
+ # bans raise and throw
391
+ #
392
+ def exclude_raise
393
+
394
+ @current_checks << [ :do_exclude_fvkcall, :raise, "raise is forbidden" ]
395
+ @current_checks << [ :do_exclude_fvkcall, :throw, "throw is forbidden" ]
396
+ end
397
+
398
+ #
399
+ # the actual check method, check() is rather a bootstrap one...
400
+ #
401
+ def do_check (sexp)
402
+
403
+ @checks.each do |exclusion_method, *args|
404
+ send exclusion_method, sexp, args
405
+ end
406
+
407
+ return unless sexp.is_a?(Array) # check over, seems fine...
408
+
409
+ # check children
410
+
411
+ sexp.each { |c| do_check c }
412
+ end
413
+
414
+ #
415
+ # the methods that actually perform the checks
416
+ # (and potentially raise security exceptions)
417
+
418
+ #
419
+ # constructs a new set of arguments by inserting the newhead at the
420
+ # beginning of the arguments
421
+ #
422
+ def cons (newhead, args)
423
+
424
+ newhead = Array(newhead)
425
+ newhead << args[0]
426
+
427
+ [ newhead ] + (args[1, -1] || [])
428
+ end
429
+
430
+ def do_exclude_fcall (sexp, args)
431
+
432
+ do_exclude_head(sexp, cons(:fcall, args))
433
+ end
434
+
435
+ def do_exclude_vcall (sexp, args)
436
+
437
+ do_exclude_head(sexp, cons(:vcall, args))
438
+ end
439
+
440
+ #
441
+ # excludes :fcall and :vcall
442
+ #
443
+ def do_exclude_fvcall (sexp, args)
444
+
445
+ do_exclude_fcall(sexp, args)
446
+ do_exclude_vcall(sexp, args)
447
+ end
448
+
449
+ #
450
+ # excludes :fcall and :vcall and :call on Kernel
451
+ #
452
+ def do_exclude_fvkcall (sexp, args)
453
+
454
+ do_exclude_fvcall(sexp, args)
455
+ do_exclude_head(sexp, cons([ :call, [ :const, :Kernel ] ], args))
456
+ end
457
+
458
+ #
459
+ # raises a Rufus::SecurityError if the sexp is a reference to
460
+ # a certain symbol (like :gvar or :alias).
461
+ #
462
+ def do_exclude_symbol (sexp, args)
463
+
464
+ raise SecurityError.new(
465
+ args[1] || "symbol :#{excluded_symbol} is forbidden"
466
+ ) if sexp == args[0]
467
+ end
468
+
469
+ #
470
+ # raises a security error if the sexp is a call on a given constant or
471
+ # module (class)
472
+ #
473
+ def do_exclude_call_on (sexp, args)
474
+
475
+ do_exclude_head(sexp, [ [:call, args[0]] ] + (args[1, -1] || []))
476
+ end
477
+
478
+ #
479
+ # raises a security error if a call to a given method of any instance
480
+ # is found
481
+ #
482
+ def do_exclude_call_to (sexp, args)
483
+
484
+ return unless sexp.is_a?(Array)
485
+
486
+ raise SecurityError.new(
487
+ args[1] || "calls to '#{args[0]}' are forbidden"
488
+ ) if sexp[0] == :call and sexp[2] == args[0]
489
+ end
490
+
491
+ def do_exclude_head (sexp, args)
492
+
493
+ return unless sexp.is_a?(Array)
494
+
495
+ head = args[0]
496
+
497
+ raise SecurityError.new(
498
+ args[1] || "#{head.inspect} is forbidden"
499
+ ) if sexp[0, head.length] == head
500
+ end
501
+
502
+ def do_exclude_class_tinkering (sexp, args)
503
+
504
+ return unless sexp.is_a?(Array) # lonely symbols are not class definitions
505
+
506
+ return unless sexp[0] == :class
507
+
508
+ raise SecurityError.new(
509
+ 'defining or opening a class is forbidden'
510
+ ) if args.length == 0 or ( ! args.include?(sexp[2]))
511
+ #
512
+ # raise error if there are no exceptions or
513
+ # if the parent class is not a member of the exception list
514
+ end
515
+
516
+ #
517
+ # a simple parse (relies on ruby_parser currently)
518
+ #
519
+ def parse (rubycode)
520
+
521
+ #(@parser ||= RubyParser.new).parse(rubycode).to_a
522
+ #
523
+ # parser goes ballistic after a while, seems having a new parser
524
+ # each is not heavy at all
525
+
526
+ RubyParser.new.parse(rubycode).to_a
527
+ end
528
+ end
529
+ end
@@ -0,0 +1,241 @@
1
+
2
+ #
3
+ # Testing rufus-treechecker
4
+ #
5
+ # jmettraux at gmail.org
6
+ #
7
+ # Fri Aug 29 10:13:33 JST 2008
8
+ #
9
+
10
+ require 'testmixin'
11
+
12
+
13
+ class BasicTest < Test::Unit::TestCase
14
+ include TestMixin
15
+
16
+
17
+ def test_0
18
+
19
+ tc = Rufus::TreeChecker.new do
20
+ exclude_vcall :abort
21
+ exclude_fcall :abort
22
+ exclude_fvcall :exit, :exit!
23
+ exclude_call_to :exit
24
+ end
25
+
26
+ assert_nok(tc, 'exit')
27
+ assert_nok(tc, 'exit()')
28
+ assert_nok(tc, 'exit!')
29
+ assert_nok(tc, 'abort')
30
+ assert_nok(tc, 'abort()')
31
+ assert_nok(tc, 'Kernel.exit')
32
+ assert_nok(tc, 'Kernel.exit()')
33
+ assert_nok(tc, 'Kernel::exit')
34
+ assert_nok(tc, 'Kernel::exit()')
35
+ assert_nok(tc, '::Kernel.exit')
36
+
37
+ assert_ok(tc, '1 + 1')
38
+ end
39
+
40
+ def test_0b_vm_exiting
41
+
42
+ # TODO : implement me !
43
+ end
44
+
45
+ def test_1_global_vars
46
+
47
+ tc = Rufus::TreeChecker.new do
48
+ exclude_global_vars
49
+ end
50
+
51
+ assert_nok(tc, '$ENV')
52
+ assert_nok(tc, '$ENV = {}')
53
+ assert_nok(tc, "$ENV['HOME'] = 'away'")
54
+ end
55
+
56
+ def test_2_aliases
57
+
58
+ tc = Rufus::TreeChecker.new do
59
+ exclude_alias
60
+ end
61
+
62
+ assert_nok(tc, 'alias :a :b')
63
+ end
64
+
65
+ def test_3_exclude_calls_on
66
+
67
+ tc = Rufus::TreeChecker.new do
68
+ exclude_call_on File, FileUtils
69
+ exclude_call_on IO
70
+ end
71
+
72
+ assert_nok(tc, 'data = File.read("surf.txt")')
73
+ assert_nok(tc, 'f = File.new("surf.txt")')
74
+ assert_nok(tc, 'FileUtils.rm_f("bondzoi.txt")')
75
+ assert_nok(tc, 'IO.foreach("testfile") {|x| print "GOT ", x }')
76
+ end
77
+
78
+ def test_4_exclude_def
79
+
80
+ tc = Rufus::TreeChecker.new do
81
+ exclude_def
82
+ end
83
+
84
+ assert_nok(tc, 'def drink; "water"; end')
85
+ assert_nok(tc, 'class Toto; def drink; "water"; end; end')
86
+ end
87
+
88
+ def test_5_exclude_class_tinkering
89
+
90
+ tc = Rufus::TreeChecker.new do
91
+ exclude_class_tinkering
92
+ end
93
+
94
+ assert_nok(tc, 'class << instance; def length; 3; end; end')
95
+ assert_nok(tc, 'class Toto; end')
96
+ assert_nok(tc, 'class Alpha::Toto; end')
97
+ end
98
+
99
+ def test_5b_exclude_class_tinkering_with_exceptions
100
+
101
+ tc = Rufus::TreeChecker.new do
102
+ exclude_class_tinkering String, Rufus::TreeChecker
103
+ end
104
+
105
+ assert_nok(tc, 'class String; def length; 3; end; end')
106
+
107
+ assert_ok(tc, 'class S2 < String; def length; 3; end; end')
108
+ assert_ok(tc, 'class Toto < Rufus::TreeChecker; def length; 3; end; end')
109
+
110
+ assert_nok(tc, 'class Toto; end')
111
+ assert_nok(tc, 'class Alpha::Toto; end')
112
+ end
113
+
114
+ def test_5c_exclude_class_tinkering_with_exceptions
115
+
116
+ tc = Rufus::TreeChecker.new do
117
+ exclude_class_tinkering 'String', 'Rufus::TreeChecker'
118
+ end
119
+
120
+ assert_nok(tc, 'class String; def length; 3; end; end')
121
+
122
+ assert_ok(tc, 'class S2 < String; def length; 3; end; end')
123
+ assert_ok(tc, 'class Toto < Rufus::TreeChecker; def length; 3; end; end')
124
+
125
+ assert_nok(tc, 'class Toto; end')
126
+ assert_nok(tc, 'class Alpha::Toto; end')
127
+ end
128
+
129
+ def test_6_exclude_module_tinkering
130
+
131
+ tc = Rufus::TreeChecker.new do
132
+ exclude_module_tinkering
133
+ end
134
+
135
+ assert_nok(tc, 'module Alpha; end')
136
+ assert_nok(tc, 'module Momo::Alpha; end')
137
+ end
138
+
139
+ def test_7_exclude_eval
140
+
141
+ tc = Rufus::TreeChecker.new do
142
+ exclude_eval
143
+ end
144
+
145
+ assert_nok(tc, 'eval("code")')
146
+ assert_nok(tc, 'toto.instance_eval("code")')
147
+ assert_nok(tc, 'Toto.module_eval("code")')
148
+ end
149
+
150
+ def test_8_exclude_backquotes
151
+
152
+ tc = Rufus::TreeChecker.new do
153
+ exclude_backquotes
154
+ end
155
+
156
+ assert_nok(tc, '`kill -9 whatever`')
157
+ end
158
+
159
+ def test_9_exclude_raise_and_throw
160
+
161
+ tc = Rufus::TreeChecker.new do
162
+ exclude_raise
163
+ end
164
+
165
+ assert_nok(tc, 'raise')
166
+ assert_nok(tc, 'raise "error"')
167
+ assert_nok(tc, 'Kernel.raise')
168
+ assert_nok(tc, 'Kernel.raise "error"')
169
+ assert_ok(tc, 'Kernel.puts "error"')
170
+ assert_nok(tc, 'throw')
171
+ assert_nok(tc, 'throw :halt')
172
+ end
173
+
174
+ def test_10_exclude_public
175
+
176
+ tc = Rufus::TreeChecker.new do
177
+ exclude_fvcall :public
178
+ exclude_fvcall :protected
179
+ exclude_fvcall :private
180
+ end
181
+
182
+ assert_nok(tc, 'public')
183
+ assert_nok(tc, 'public :surf')
184
+ assert_nok(tc, 'class Toto; public :car; end')
185
+ assert_nok(tc, 'private')
186
+ assert_nok(tc, 'private :surf')
187
+ assert_nok(tc, 'class Toto; private :car; end')
188
+ end
189
+
190
+ def test_11_is_not
191
+
192
+ tc = Rufus::TreeChecker.new do
193
+ exclude_head [ :block ]
194
+ exclude_head [ :lasgn ]
195
+ exclude_head [ :dasgn_curr ]
196
+ end
197
+
198
+ assert_nok(tc, 'a; b; c')
199
+ assert_nok(tc, 'lambda { a; b; c }')
200
+
201
+ assert_nok(tc, 'a = 2')
202
+ assert_nok(tc, 'lambda { a = 2 }')
203
+ end
204
+
205
+ def test_12_at_root
206
+
207
+ tc = Rufus::TreeChecker.new do
208
+ at_root do
209
+ exclude_head [ :block ]
210
+ exclude_head [ :lasgn ]
211
+ end
212
+ end
213
+
214
+ assert_nok(tc, 'a; b; c')
215
+ assert_ok(tc, 'lambda { a; b; c }')
216
+
217
+ assert_nok(tc, 'a = 2')
218
+ assert_ok(tc, 'lambda { a = 2 }')
219
+ end
220
+
221
+ def test_12_freeze
222
+
223
+ # TODO : are there some rules with composite values ?
224
+ # can't remember of any :(
225
+ end
226
+
227
+ #def test_X
228
+ # tc = Rufus::TreeChecker.new do
229
+ # end
230
+ # #tc.ptree 'load "surf"'
231
+ # tc.ptree 'class Toto; load "nada"; end'
232
+ # tc.ptree 'class Toto; def m; load "nada"; end; end'
233
+ # tc.ptree 'class << toto; def m; load "nada"; end; end'
234
+ # #tc.ptree 'lambda { a; b; c }'
235
+ # #tc.ptree 'lambda { a = c }'
236
+ # #tc.ptree 'c = 0; a = c'
237
+ # #tc.ptree 'c = a = 0'
238
+ # tc.ptree 'a = 5 + 6; puts a'
239
+ #end
240
+ end
241
+
@@ -0,0 +1,72 @@
1
+
2
+ #
3
+ # Testing rufus-treechecker
4
+ #
5
+ # jmettraux at gmail.org
6
+ #
7
+ # Fri Aug 29 10:13:33 JST 2008
8
+ #
9
+
10
+ require 'testmixin'
11
+
12
+ module Testy
13
+ class Tasty
14
+ end
15
+ end
16
+
17
+ class OldTreeCheckerTest < Test::Unit::TestCase
18
+ include TestMixin
19
+
20
+
21
+ def test_0
22
+
23
+ tc = Rufus::TreeChecker.new do
24
+ exclude_fvkcall :abort
25
+ exclude_fvkcall :exit, :exit!
26
+ exclude_fvkcall :system
27
+ exclude_eval
28
+ exclude_alias
29
+ exclude_global_vars
30
+ exclude_call_on File, FileUtils
31
+ exclude_class_tinkering Testy::Tasty
32
+ exclude_module_tinkering
33
+
34
+ exclude_fvcall :public
35
+ exclude_fvcall :protected
36
+ exclude_fvcall :private
37
+ exclude_fcall :load
38
+ exclude_fcall :require
39
+ end
40
+
41
+ assert_nocompile tc, "def surf }"
42
+
43
+ assert_ok tc, "puts 'toto'"
44
+
45
+ assert_nok tc, "exit"
46
+ assert_nok tc, "puts $BATEAU"
47
+ assert_nok tc, "abort"
48
+ assert_nok tc, "abort; puts 'ok'"
49
+ assert_nok tc, "puts 'ok'; abort"
50
+
51
+ assert_nok tc, "exit 0"
52
+ assert_nok tc, "system('whatever')"
53
+
54
+ assert_nok tc, "alias :a :b"
55
+ assert_nok tc, "alias_method :a, :b"
56
+
57
+ assert_nok tc, "File.open('x')"
58
+ assert_nok tc, "FileUtils.rm('x')"
59
+
60
+ assert_nok tc, "eval 'nada'"
61
+ assert_nok tc, "M.module_eval 'nada'"
62
+ assert_nok tc, "o.instance_eval 'nada'"
63
+
64
+ assert_ok tc, "puts 'toto'"
65
+
66
+ assert_ok tc, "class Toto < Testy::Tasty\nend"
67
+ assert_nok tc, "class String\nend"
68
+ assert_nok tc, "module Whatever\nend"
69
+ assert_nok tc, "class << e\nend"
70
+ end
71
+ end
72
+
data/test/test.rb ADDED
@@ -0,0 +1,4 @@
1
+
2
+ require 'ft_0_basic'
3
+ require 'ft_1_old_treechecker'
4
+
data/test/testmixin.rb ADDED
@@ -0,0 +1,31 @@
1
+
2
+ #
3
+ # Testing rufus-treechecker
4
+ #
5
+ # jmettraux at gmail.org
6
+ #
7
+ # Fri Aug 29 18:30:03 JST 2008
8
+ #
9
+
10
+ require 'test/unit'
11
+ require 'rubygems'
12
+ require 'rufus/treechecker'
13
+
14
+
15
+ module TestMixin
16
+
17
+ def assert_ok (tc, rubycode)
18
+ tc.check(rubycode)
19
+ end
20
+ def assert_nok (tc, rubycode)
21
+ assert_raise Rufus::SecurityError do
22
+ tc.check(rubycode)
23
+ end
24
+ end
25
+ def assert_nocompile (tc, rubycode)
26
+ assert_raise Racc::ParseError do
27
+ tc.check(rubycode)
28
+ end
29
+ end
30
+ end
31
+
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rufus-treechecker
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.0"
5
+ platform: ruby
6
+ authors:
7
+ - John Mettraux
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-01 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rogue_parser
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description:
26
+ email: john at openwfe dot org
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.txt
33
+ files:
34
+ - lib/rufus
35
+ - lib/rufus/treechecker.rb
36
+ - test/ft_0_basic.rb
37
+ - test/ft_1_old_treechecker.rb
38
+ - test/test.rb
39
+ - test/testmixin.rb
40
+ - README.txt
41
+ has_rdoc: true
42
+ homepage: http://rufus.rubyforge.org/rufus-treechecker
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements:
61
+ - rogue_parser
62
+ rubyforge_project: rufus
63
+ rubygems_version: 1.2.0
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: checking ruby code before eval()
67
+ test_files:
68
+ - test/test.rb