# File lib/safemode/parser.rb, line 7 def jail(code, allowed_fcalls = []) @@allowed_fcalls = allowed_fcalls tree = parse code self.new.process(tree) end
# File lib/safemode/parser.rb, line 13 def parse(code) case @@parser # when 'ParseTree' # ParseTree.translate(code) when 'RubyParser' RubyParser.new.parse(code) else raise "unknown parser #{@@parser}" end end
# File lib/safemode/parser.rb, line 24 def parser=(parser) @@parser = parser end
# File lib/safemode/parser.rb, line 29 def jail(str, parentheses = false) str = parentheses ? "(#{str})." : "#{str}." if str "#{str}to_jail" end
split up process_call. see below ...
# File lib/safemode/parser.rb, line 35 def process_call(exp) receiver = jail process_call_receiver(exp) name = exp.shift args = process_call_args(exp) process_call_code(receiver, name, args) end
# File lib/safemode/parser.rb, line 126 def process_call_args(exp) args_exp = exp.shift rescue nil if args_exp && args_exp.first == :array # FIX args = "#{process(args_exp)[1..-2]}" else args = process args_exp args = nil if args.empty? end args end
# File lib/safemode/parser.rb, line 137 def process_call_code(receiver, name, args) case name when :<=>, :==, :<, :>, :<=, :>=, :-, :+, :*, :/, :%, :<<, :>>, :** then "(#{receiver} #{name} #{args})" when :[] then "#{receiver}[#{args}]" when :"-@" then "-#{receiver}" when :"+@" then "+#{receiver}" else unless receiver.nil? then "#{receiver}.#{name}#{args ? "(#{args})" : args}" else "#{name}#{args ? "(#{args})" : args}" end end end
split up Ruby2Ruby#process_call monster method so we can hook into it in a more readable manner
# File lib/safemode/parser.rb, line 118 def process_call_receiver(exp) receiver_node_type = exp.first.nil? ? nil : exp.first.first receiver = process exp.shift receiver = "(#{receiver})" if Ruby2Ruby::ASSIGN_NODES.include? receiver_node_type receiver end
# File lib/safemode/parser.rb, line 42 def process_fcall(exp) # using haml we probably never arrive here because :lasgn'ed :fcalls # somehow seem to change to :calls somewhere during processing # unless @@allowed_fcalls.include?(exp.first) # code = Ruby2Ruby.new.process([:fcall, exp[1], exp[2]]) # wtf ... # raise_security_error(exp.first, code) # end "to_jail.#{super}" end
# File lib/safemode/parser.rb, line 62 def process_iasgn(exp) code = super if code != '@output_buffer = ""' raise_security_error(:iasgn, code) else code end end
Ruby2Ruby #process_if rewrites if and unless statements in a way that makes the result unusable for evaluation in, e.g. ERB which appends a call to to_s when using <%= %> tags. We'd need to either enclose the result from #process_if into parentheses like (1 if true) and (true ? (1) : (2)) or just use the plain if-then-else-end syntax (so that ERB can safely append to_s to the resulting block).
# File lib/safemode/parser.rb, line 163 def process_if(exp) expand = Ruby2Ruby::ASSIGN_NODES.include? exp.first.first c = process exp.shift t = process exp.shift f = process exp.shift c = "(#{c.chomp})" if c =~ %r\n/ if t then # unless expand then # if f then # r = "#{c} ? (#{t}) : (#{f})" # r = nil if r =~ /return/ # HACK - need contextual awareness or something # else # r = "#{t} if #{c}" # end # return r if r and (@indent+r).size < LINE_LENGTH and r !~ /\n/ # end r = "if #{c} then\n#{indent(t)}\n" r << "else\n#{indent(f)}\n" if f r << "end" r else # unless expand then # r = "#{f} unless #{c}" # return r if (@indent+r).size < LINE_LENGTH and r !~ /\n/ # end "unless #{c} then\n#{indent(f)}\nend" end end
# File lib/safemode/parser.rb, line 52 def process_vcall(exp) # unless @@allowed_fcalls.include?(exp.first) # code = Ruby2Ruby.new.process([:fcall, exp[1], exp[2]]) # wtf ... # raise_security_error(exp.first, code) # end name = exp[1] exp.clear "to_jail.#{name}" end
# File lib/safemode/parser.rb, line 111 def raise_security_error(type, info) raise Safemode::SecurityError.new(type, info) end