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)
  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
process_call_args(exp) click to toggle source
# File lib/safemode/parser.rb, line 151
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 165
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 143
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
Calls superclass method
# File lib/safemode/parser.rb, line 122
def process_const(arg)
  sexp_type = arg.sexp_body.sexp_type # constants are encoded as: "s(:const, :Encoding)"
  if sexp_type == :Encoding
    # handling of Encoding constants.
    # Note: ruby_parser evaluates __ENCODING__ to s(:colon2, s(:const, :Encoding), :UTF_8)
    "#{super(arg).gsub('-', '_')}"
  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
process_fcall(exp) click to toggle source
# File lib/safemode/parser.rb, line 44
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 64
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 191
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

  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 54
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 136
def raise_security_error(type, info)
  raise Safemode::SecurityError.new(type, info)
end