Class: Rufus::TreeChecker

Inherits:
Object
  • Object
show all
Defined in:
lib/rufus/treechecker.rb

Overview

TreeChecker relies on ruby_parser to turns a piece of ruby code (a string) into a bunch of sexpression and then TreeChecker will check that sexpression tree and raise a Rufus::SecurityException if an excluded pattern is spotted.

The TreeChecker is meant to be useful for people writing DSLs directly in Ruby (not via their own parser) that want to check and prevent bad things from happening in this code.

tc = Rufus::TreeChecker.new do
  exclude_fvcall :abort
  exclude_fvcall :exit, :exit!
end

tc.check("1 + 1; abort")               # will raise a SecurityError
tc.check("puts (1..10).to_a.inspect")  # OK

featured exclusion methods

call / vcall / fcall ?

What the difference between those ? Well, here is how those various piece of code look like :

"exit"          => [:vcall, :exit]
"Kernel.exit"   => [:call, [:const, :Kernel], :exit]
"Kernel::exit"  => [:call, [:const, :Kernel], :exit]
"k.exit"        => [:call, [:vcall, :k], :exit]
"exit -1"       => [:fcall, :exit, [:array, [:lit, -1]]]

Obviously :fcall could be labelled as “function call”, :call is a call on to some instance, while vcall might either be a variable dereference or a function call with no arguments.

low-level rules

  • exclude_symbol : bans the usage of a given symbol (very low-level,

    mostly used by other rules
    
  • exclude_head

  • exclude_fcall

  • exclude_vcall

  • exclude_fvcall

  • exclude_fvccall

  • exclude_call_on

  • exclude_call_to

  • exclude_rebinding

  • exclude_def

  • exclude_class_tinkering

  • exclude_module_tinkering

  • at_root

higher level rules

Those rules take no arguments

  • exclude_access_to : prevents calling or rebinding a list of classes

  • exclude_eval : bans eval, module_eval and instance_eval

  • exclude_global_vars : bans calling or modifying global vars

  • exclude_alias : bans calls to alias and alias_method

  • exclude_vm_exiting : bans exit, abort, …

  • exclude_raise : bans calls to raise or throw

a bit further

It’s possible to clone a TreeChecker and to add some more rules to it :

tc0 = Rufus::TreeChecker.new do
  #
  # calls to eval, module_eval and instance_eval are not allowed
  #
  exclude_eval
end

tc1 = tc0.clone
tc1.add_rules do
  #
  # calls to any method on File and FileUtils classes are not allowed
  #
  exclude_call_on File, FileUtils
end

Defined Under Namespace

Classes: RuleSet

Constant Summary collapse

VERSION =
'1.0.8'

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ TreeChecker

initializes the TreeChecker, expects a block



144
145
146
147
148
149
150
151
# File 'lib/rufus/treechecker.rb', line 144

def initialize(&block)

  @root_set = RuleSet.new
  @set = RuleSet.new
  @current_set = @set

  add_rules(&block)
end

Instance Method Details

#add_rules(&block) ⇒ Object

Adds a set of checks (rules) to this treechecker. Returns self.



187
188
189
190
191
192
# File 'lib/rufus/treechecker.rb', line 187

def add_rules(&block)

  instance_eval(&block) if block

  self
end

#check(rubycode) ⇒ Object

Performs the check on the given String of ruby code. Will raise a Rufus::SecurityError if there is something excluded by the rules specified at the initialization of the TreeChecker instance.



166
167
168
169
170
171
172
173
174
175
176
# File 'lib/rufus/treechecker.rb', line 166

def check(rubycode)

  sexp = parse(rubycode)

  #@root_checks.each do |meth, *args|
  #  send meth, sexp, args
  #end
  @root_set.check(sexp)

  do_check(sexp)
end

#cloneObject

Return a copy of this TreeChecker instance



180
181
182
183
# File 'lib/rufus/treechecker.rb', line 180

def clone

  Marshal.load(Marshal.dump(self))
end

#freezeObject

Freezes the treechecker instance “in depth”



196
197
198
199
200
201
# File 'lib/rufus/treechecker.rb', line 196

def freeze

  super
  @root_set.freeze
  @set.freeze
end

#ptree(rubycode) ⇒ Object

pretty-prints the sexp tree of the given rubycode



129
130
131
132
# File 'lib/rufus/treechecker.rb', line 129

def ptree(rubycode)

  puts stree(rubycode)
end

#stree(rubycode) ⇒ Object

returns the pretty-printed string of the given rubycode (thanks ruby_parser).



137
138
139
140
# File 'lib/rufus/treechecker.rb', line 137

def stree(rubycode)

  "#{rubycode.inspect}\n =>\n#{parse(rubycode).inspect}"
end

#to_sObject



153
154
155
156
157
158
159
160
# File 'lib/rufus/treechecker.rb', line 153

def to_s

  s = "#{self.class} (#{self.object_id})\n"
  s << "root_set :\n"
  s << @root_set.to_s
  s << "set :\n"
  s << @set.to_s
end