lib/safemode/parser.rb in safemode-1.3.2 vs lib/safemode/parser.rb in safemode-1.3.3

- old
+ new

@@ -1,47 +1,48 @@ module Safemode class Parser < Ruby2Ruby # @@parser = defined?(RubyParser) ? 'RubyParser' : 'ParseTree' @@parser = 'RubyParser' - + class << self def jail(code, allowed_fcalls = []) @@allowed_fcalls = allowed_fcalls tree = parse code self.new.process(tree) end - + def parse(code) case @@parser # when 'ParseTree' # ParseTree.translate(code) when 'RubyParser' RubyParser.new.parse(code) else raise "unknown parser #{@@parser}" end end - + def parser=(parser) @@parser = parser end end - + def jail(str, parentheses = false) str = parentheses ? "(#{str})." : "#{str}." if str "#{str}to_jail" end # split up #process_call. see below ... def process_call(exp) + exp.shift # remove ":call" symbol receiver = jail process_call_receiver(exp) name = exp.shift args = process_call_args(exp) process_call_code(receiver, name, args) end - + 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 ... @@ -105,46 +106,47 @@ # block_pass represents &:method, which would bypass the whitelist e.g. by array.each(&:destroy) # at this point we don't know the receiver so we rather disable it completely, # use array.each { |item| item.destroy } instead :block_pass ] - # SexpProcessor bails when we overwrite these ... but they are listed as + # SexpProcessor bails when we overwrite these ... but they are listed as # "internal nodes that you can't get to" in sexp_processor.rb # :ifunc, :method, :last, :opt_n, :cfunc, :newline, :alloca, :memo, :cref - - disallowed.each do |name| + + disallowed.each do |name| define_method "process_#{name}" do |arg| code = super(arg) raise_security_error(name, code) end end def process_const(arg) - if RUBY_VERSION >= "1.9" && arg.sexp_type == :Encoding + sexp_type = arg.sexp_body.sexp_type # constants are encoded as: "s(:const, :Encoding)" + if RUBY_VERSION >= "1.9" && sexp_type == :Encoding # handling of Encoding constants in ruby 1.9. # Note: ruby_parser evaluates __ENCODING__ to s(:colon2, s(:const, :Encoding), :UTF_8) "#{super(arg).gsub('-', '_')}" - elsif arg.sexp_type == :String + elsif sexp_type == :String # Allow String.new as used in ERB in Ruby 2.4+ to create a string buffer super(arg).to_s else raise_security_error("constant", super(arg)) end end - + def raise_security_error(type, info) raise Safemode::SecurityError.new(type, info) end - + # split up Ruby2Ruby#process_call monster method so we can hook into it # in a more readable manner def process_call_receiver(exp) receiver_node_type = exp.first.nil? ? nil : exp.first.first - receiver = process exp.shift + receiver = process exp.shift receiver = "(#{receiver})" if - Ruby2Ruby::ASSIGN_NODES.include? receiver_node_type + Ruby2Ruby::ASSIGN_NODES.include? receiver_node_type receiver end def process_call_args(exp) args = [] @@ -176,19 +178,20 @@ else "#{name}#{args ? "(#{args})" : args}" end 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 + # 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). def process_if(exp) + exp.shift # remove ":if" symbol from exp expand = Ruby2Ruby::ASSIGN_NODES.include? exp.first.first c = process exp.shift t = process exp.shift f = process exp.shift @@ -214,8 +217,8 @@ # 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 - end + end + end end