rufus-treechecker 1.0

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.
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