class Safemode::Parser

Public Class Methods

jail(code, allowed_fcalls = []) click to toggle source
# File lib/safemode/parser.rb, line 7
def jail(code, allowed_fcalls = [])
  @@allowed_fcalls = allowed_fcalls
  tree = parse code
  self.new.process(tree)
end
parse(code) click to toggle source
# 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
parser=(parser) click to toggle source
# File lib/safemode/parser.rb, line 24
def parser=(parser)
  @@parser = parser
end

Public Instance Methods

jail(str, parentheses = false) click to toggle source
# File lib/safemode/parser.rb, line 29
def jail(str, parentheses = false)
  str = parentheses ? "(#{str})." : "#{str}." if str
  "#{str}to_jail"
end
process_call(exp) click to toggle source

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
process_call_args(exp) click to toggle source
# File lib/safemode/parser.rb, line 139
def process_call_args(exp)
  args = []
  while not exp.empty? do
    args_exp = exp.shift
    if args_exp && args_exp.first == :array # FIX
      processed = "#{process(args_exp)[1..-2]}"
    else
      processed = process args_exp
    end
    args << processed unless (processed.nil? or processed.empty?)
  end
  args.empty? ? nil : args.join(", ")
end
process_call_code(receiver, name, args) click to toggle source
# File lib/safemode/parser.rb, line 153
def process_call_code(receiver, name, args)
  case name
  when :<=>, :==, "!=".to_sym, :<, :>, :<=, :>=, :-, :+, :*, :/, :%, :<<, :>>, :** 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
process_call_receiver(exp) click to toggle source

split up Ruby2Ruby#process_call monster method so we can hook into it in a more readable manner

# File lib/safemode/parser.rb, line 131
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
process_const(arg) click to toggle source

handling of Encoding constants in ruby 1.9. Note: ruby_parser evaluates __ENCODING__ to s(:colon2, s(:const, :Encoding), :UTF_8)

Calls superclass method
# File lib/safemode/parser.rb, line 119
def process_const(arg)
  raise_security_error("constant", super(arg)) unless (RUBY_VERSION >= "1.9" and arg.sexp_type == :Encoding)
  "#{super(arg).gsub('-', '_')}"
end
process_fcall(exp) click to toggle source
# File lib/safemode/parser.rb, line 43
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
process_iasgn(exp) click to toggle source
Calls superclass method
# File lib/safemode/parser.rb, line 63
def process_iasgn(exp)
  code = super
  if code != '@output_buffer = ""'
    raise_security_error(:iasgn, code)
  else
    code
  end
end
process_if(exp) click to toggle source

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 179
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 =~ /\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
process_vcall(exp) click to toggle source
# File lib/safemode/parser.rb, line 53
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
raise_security_error(type, info) click to toggle source
# File lib/safemode/parser.rb, line 124
def raise_security_error(type, info)
  raise Safemode::SecurityError.new(type, info)
end