Class/Module Index [+]

Quicksearch

Brakeman::AliasProcessor

Returns an s-expression with aliases replaced with their value. This does not preserve semantics (due to side effects, etc.), but it makes processing easier when searching for various things.

Attributes

result[R]

Public Class Methods

new(tracker = nil) click to toggle source

Returns a new AliasProcessor with an empty environment.

The recommended usage is:

AliasProcessor.new.process_safely src
# File lib/brakeman/processors/alias_processor.rb, line 19
def initialize tracker = nil
  super()
  @env = SexpProcessor::Environment.new
  @inside_if = false
  @ignore_ifs = false
  @exp_context = []
  @current_module = nil
  @tracker = tracker #set in subclass as necessary
  @helper_method_cache = {}
  @helper_method_info = Hash.new({})
  set_env_defaults
end

Public Instance Methods

assign_args(method_exp, args, meth_env = SexpProcessor::Environment.new) click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 592
def assign_args method_exp, args, meth_env = SexpProcessor::Environment.new
  formal_args = method_exp.formal_args

  formal_args.each_with_index do |arg, index|
    next if index == 0

    if arg.is_a? Symbol and sexp? args[index - 1]
      meth_env[Sexp.new(:lvar, arg)] = args[index - 1]
    end
  end

  meth_env
end
duplicate?(exp) click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 629
def duplicate? exp
  @exp_context[0..-2].reverse_each do |e|
    return true if exp == e 
  end

  false
end
find_method(*args) click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 637
def find_method *args
  nil
end
find_push_target(exp) click to toggle source

Finds the inner most call target which is not the target of a call to <<

# File lib/brakeman/processors/alias_processor.rb, line 621
def find_push_target exp
  if call? exp and exp.method == :<<
    find_push_target exp.target
  else
    exp
  end
end
get_call_value(call) click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 507
def get_call_value call
  method_name = call.method

  #Look for helper methods and see if we can get a return value
  if found_method = find_method(method_name, @current_class)
    helper = found_method[:method]

    if sexp? helper
      value = process_helper_method helper, call.args
      value.line(call.line)
      return value
    else
      raise "Unexpected value for method: #{found_method}"
    end
  else
    call
  end
end
join_arrays(array1, array2) click to toggle source

Join two array literals into one.

# File lib/brakeman/processors/alias_processor.rb, line 452
def join_arrays array1, array2
  result = Sexp.new(:array)
  result.concat array1[1..-1]
  result.concat array2[1..-1]
end
join_strings(string1, string2) click to toggle source

Join two string literals into one.

# File lib/brakeman/processors/alias_processor.rb, line 459
def join_strings string1, string2
  result = Sexp.new(:str)
  result.value = string1.value + string2.value

  if result.value.length > 50
    string1
  else
    result
  end
end
only_ivars(include_request_vars = false, lenv = nil) click to toggle source

Returns a new SexpProcessor::Environment containing only instance variables. This is useful, for example, when processing views.

# File lib/brakeman/processors/alias_processor.rb, line 472
def only_ivars include_request_vars = false, lenv = nil
  lenv ||= env
  res = SexpProcessor::Environment.new

  if include_request_vars
    lenv.all.each do |k, v|
      #TODO Why would this have nil values?
      if (k.node_type == :ivar or request_value? k) and not v.nil?
        res[k] = v.dup
      end
    end
  else
    lenv.all.each do |k, v|
      #TODO Why would this have nil values?
      if k.node_type == :ivar and not v.nil?
        res[k] = v.dup
      end
    end
  end

  res
end
only_request_vars() click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 495
def only_request_vars
  res = SexpProcessor::Environment.new

  env.all.each do |k, v|
    if request_value? k and not v.nil?
      res[k] = v.dup
    end
  end

  res
end
process_array_access(target, args) click to toggle source

Process single integer access to an array.

Returns the value inside the array, if possible.

# File lib/brakeman/processors/alias_processor.rb, line 434
def process_array_access target, args
  if args.length == 1 and integer? args.first
    index = args.first.value

    #Have to do this because first element is :array and we have to skip it
    target[1..-1][index]
  else
    nil
  end
end
process_attrasgn(exp) click to toggle source

'Attribute' assignment

x.y = 1

or

x[:y] = 1
# File lib/brakeman/processors/alias_processor.rb, line 278
def process_attrasgn exp
  tar_variable = exp.target
  target = exp.target = process(exp.target)
  method = exp.method
  index_arg = exp.first_arg
  value_arg = exp.second_arg

  if method == :[]=
    index = exp.first_arg = process(index_arg)
    value = exp.second_arg = process(value_arg)
    match = Sexp.new(:call, target, :[], index)
    env[match] = value

    if hash? target
      env[tar_variable] = hash_insert target.deep_clone, index, value
    end
  elsif method.to_s[-1,1] == "="
    value = exp.first_arg = process(index_arg)
    #This is what we'll replace with the value
    match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)

    if @inside_if and val = env[match]
      if val != value
        env[match] = Sexp.new(:or, env[match], value)
      end
    else
      env[match] = value
    end
  else
    raise "Unrecognized assignment: #{exp}"
  end
  exp
end
process_block(exp) click to toggle source

Start new scope for block.

# File lib/brakeman/processors/alias_processor.rb, line 174
def process_block exp
  env.scope do
    process_default exp
  end
end
process_call(exp) click to toggle source

Process a method call.

# File lib/brakeman/processors/alias_processor.rb, line 77
def process_call exp
  target_var = exp.target
  exp = process_default exp

  #In case it is replaced with something else
  unless call? exp
    return exp
  end

  target = exp.target
  method = exp.method
  first_arg = exp.first_arg

  #See if it is possible to simplify some basic cases
  #of addition/concatenation.
  case method
  when :+
    if array? target and array? first_arg
      joined = join_arrays target, first_arg 
      joined.line(exp.line)
      exp = joined
    elsif string? first_arg
      if string? target # "blah" + "blah"
        joined = join_strings target, first_arg
        joined.line(exp.line)
        exp = joined
      elsif call? target and target.method == :+ and string? target.first_arg
        joined = join_strings target.first_arg, first_arg
        joined.line(exp.line)
        target.first_arg = joined
        exp = target
      end
    elsif number? first_arg
      if number? target
        exp = Sexp.new(:lit, target.value + first_arg.value)
      elsif call? target and target.method == :+ and number? target.first_arg
        target.first_arg = Sexp.new(:lit, target.first_arg.value + first_arg.value)
        exp = target
      end
    end
  when :-
    if number? target and number? first_arg
      exp = Sexp.new(:lit, target.value - first_arg.value)
    end
  when :*
    if number? target and number? first_arg
      exp = Sexp.new(:lit, target.value * first_arg.value)
    end
  when :/
    if number? target and number? first_arg
      exp = Sexp.new(:lit, target.value / first_arg.value)
    end
  when :[]
    if array? target
      temp_exp = process_array_access target, exp.args
      exp = temp_exp if temp_exp
    elsif hash? target
      temp_exp = process_hash_access target, first_arg
      exp = temp_exp if temp_exp
    end
  when :merge!, :update
    if hash? target and hash? first_arg
       target = process_hash_merge! target, first_arg
       env[target_var] = target
       return target
    end
  when :merge
    if hash? target and hash? first_arg
      return process_hash_merge(target, first_arg)
    end
  when :<<
    if string? target and string? first_arg
      target.value << first_arg.value
      env[target_var] = target
      return target
    elsif array? target
      target << first_arg
      env[target_var] = target
      return target
    else
      target = find_push_target exp
      env[target] = exp unless target.nil? #Happens in TemplateAliasProcessor
    end
  end

  exp
end
process_cdecl(exp) click to toggle source

Constant assignments like

BIG_CONSTANT = 234810983
# File lib/brakeman/processors/alias_processor.rb, line 383
def process_cdecl exp
  if sexp? exp.rhs
    exp.rhs = process exp.rhs
  end

  if exp.lhs.is_a? Symbol
    match = Sexp.new(:const, exp.lhs)
  else
    match = exp.lhs
  end

  env[match] = exp.rhs

  exp
end
process_cvdecl(exp) click to toggle source

Class variable assignment

@@x = 1
# File lib/brakeman/processors/alias_processor.rb, line 259
def process_cvdecl exp
  match = Sexp.new(:cvar, exp.lhs)
  value = exp.rhs = process(exp.rhs)
  
  if @inside_if and val = env[match]
    if val != value
      env[match] = Sexp.new(:or, env[match], value)
    end
  else
    env[match] = value
  end

  exp
end
process_default(exp) click to toggle source

Process a Sexp. If the Sexp has a value associated with it in the environment, that value will be returned.

# File lib/brakeman/processors/alias_processor.rb, line 49
def process_default exp
  @exp_context.push exp

  begin
    exp.map! do |e|
      if sexp? e and not e.empty?
        process e
      else
        e
      end
    end
  rescue Exception => err
    @tracker.error err if @tracker
  end

  #Generic replace
  if replacement = env[exp] and not duplicate? replacement
    result = set_line replacement.deep_clone, exp.line
  else
    result = exp
  end

  @exp_context.pop

  result
end
process_defn(exp) click to toggle source
Alias for: process_methdef
process_defs(exp) click to toggle source
Alias for: process_selfdef
process_gasgn(exp) click to toggle source

Global assignment

$x = 1
# File lib/brakeman/processors/alias_processor.rb, line 242
def process_gasgn exp
  match = Sexp.new(:gvar, exp.lhs)
  value = exp.rhs = process(exp.rhs)

  if @inside_if and val = env[match]
    if val != value
      env[match] = Sexp.new(:or, env[match], value)
    end
  else
    env[match] = value
  end

  exp
end
process_hash_access(target, index) click to toggle source

Process hash access by returning the value associated with the given argument.

# File lib/brakeman/processors/alias_processor.rb, line 447
def process_hash_access target, index
  hash_access(target, index)
end
process_hash_merge(hash, args) click to toggle source

Return a new hash Sexp with the given values merged into it.

args should be a hash Sexp as well.

# File lib/brakeman/processors/alias_processor.rb, line 328
def process_hash_merge hash, args
  hash = hash.deep_clone
  hash_iterate args do |key, replacement|
    hash_insert hash, key, replacement
  end
  hash
end
process_hash_merge!(hash, args) click to toggle source

Merge values into hash when processing

h.merge! :something => "value"
# File lib/brakeman/processors/alias_processor.rb, line 315
def process_hash_merge! hash, args
  hash = hash.deep_clone
  hash_iterate args do |key, replacement|
    hash_insert hash, key, replacement
    match = Sexp.new(:call, hash, :[], key)
    env[match] = replacement
  end
  hash
end
process_helper_method(method_exp, args) click to toggle source
# File lib/brakeman/processors/alias_processor.rb, line 526
def process_helper_method method_exp, args
  method_name = method_exp.method_name
  Brakeman.debug "Processing method #{method_name}"

  info = @helper_method_info[method_name]

  #If method uses instance variables, then include those and request
  #variables (params, etc) in the method environment. Otherwise,
  #only include request variables.
  if info[:uses_ivars]
    meth_env = only_ivars(:include_request_vars)
  else
    meth_env = only_request_vars
  end

  #Add arguments to method environment
  assign_args method_exp, args, meth_env


  #Find return values if method does not depend on environment/args
  values = @helper_method_cache[method_name]

  unless values
    #Serialize environment for cache key
    meth_values = meth_env.instance_variable_get(:@env).to_a
    meth_values.sort!
    meth_values = meth_values.to_s

    digest = Digest::SHA1.new.update(meth_values << method_name.to_s).to_s.to_sym

    values = @helper_method_cache[digest]
  end

  if values
    #Use values from cache
    values[:ivar_values].each do |var, val|
      env[var] = val
    end

    values[:return_value]
  else
    #Find return value for method
    frv = Brakeman::FindReturnValue.new
    value = frv.get_return_value(method_exp.body_list, meth_env)

    ivars = {}

    only_ivars(false, meth_env).all.each do |var, val|
      env[var] = val
      ivars[var] = val
    end

    if not frv.uses_ivars? and args.length == 0
      #Store return value without ivars and args if they are not used
      @helper_method_cache[method_exp.method_name] = { :return_value => value, :ivar_values => ivars }
    else
      @helper_method_cache[digest] = { :return_value => value, :ivar_values => ivars }
    end

    #Store information about method, just ivar usage for now
    @helper_method_info[method_name] = { :uses_ivars => frv.uses_ivars? }

    value
  end
end
process_iasgn(exp) click to toggle source

Instance variable assignment

@x = 1
# File lib/brakeman/processors/alias_processor.rb, line 225
def process_iasgn exp
  exp.rhs = process exp.rhs
  ivar = Sexp.new(:ivar, exp.lhs).line(exp.line)

  if @inside_if and val = env[ivar]
    if val != exp.rhs
      env[ivar] = Sexp.new(:or, val, exp.rhs).line(exp.line)
    end
  else
    env[ivar] = exp.rhs
  end

  exp
end
process_if(exp) click to toggle source

Sets @inside_if = true

# File lib/brakeman/processors/alias_processor.rb, line 400
def process_if exp
  @ignore_ifs ||= @tracker && @tracker.options[:ignore_ifs]

  condition = process exp.condition

  if true? condition
    exps = [exp.then_clause]
  elsif false? condition
    exps = [exp.else_clause]
  else
    exps = [exp.then_clause, exp.else_clause]
  end

  was_inside = @inside_if
  @inside_if = !@ignore_ifs

  exps.each do |e|
    if sexp? e
      if e.node_type == :block
        process_default e #avoid creating new scope
      else
        process e
      end
    end
  end

  @inside_if = was_inside

  exp
end
process_lasgn(exp) click to toggle source

Local assignment

x = 1
# File lib/brakeman/processors/alias_processor.rb, line 203
def process_lasgn exp
  exp.rhs = process exp.rhs if sexp? exp.rhs
  return exp if exp.rhs.nil?

  local = Sexp.new(:lvar, exp.lhs).line(exp.line || -2)

  if @inside_if and val = env[local]
    #avoid setting to value it already is (e.g. "1 or 1")
    if val != exp.rhs
      unless node_type?(val, :or) and (val.rhs == exp.rhs or val.lhs == exp.rhs)
        env[local] = Sexp.new(:or, val, exp.rhs).line(exp.line || -2)
      end
    end
  else
    env[local] = exp.rhs
  end

  exp
end
process_methdef(exp) click to toggle source

Process a method definition.

# File lib/brakeman/processors/alias_processor.rb, line 181
def process_methdef exp
  env.scope do
    set_env_defaults
    exp.body = process_all! exp.body
  end
  exp
end
Also aliased as: process_defn
process_op_asgn1(exp) click to toggle source

Assignments like this

x[:y] ||= 1
# File lib/brakeman/processors/alias_processor.rb, line 338
def process_op_asgn1 exp
  return process_default(exp) if exp[3] != :"||"

  target = exp[1] = process(exp[1])
  index = exp[2][1] = process(exp[2][1])
  value = exp[4] = process(exp[4])
  match = Sexp.new(:call, target, :[], index)

  unless env[match]
    if request_value? target
      env[match] = Sexp.new(:or, match, value)
    else
      env[match] = value
    end
  end

  exp
end
process_op_asgn2(exp) click to toggle source

Assignments like this

x.y ||= 1
# File lib/brakeman/processors/alias_processor.rb, line 359
def process_op_asgn2 exp
  return process_default(exp) if exp[3] != :"||"

  target = exp[1] = process(exp[1])
  value = exp[4] = process(exp[4])
  method = exp[2]

  match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)

  unless env[match]
    env[match] = value
  end

  exp
end
process_safely(src, set_env = nil) click to toggle source

This method processes the given Sexp, but copies it first so the original argument will not be modified.

set_env should be an instance of SexpProcessor::Environment. If provided, it will be used as the starting environment.

This method returns a new Sexp with variables replaced with their values, where possible.

# File lib/brakeman/processors/alias_processor.rb, line 40
def process_safely src, set_env = nil
  @env = set_env || SexpProcessor::Environment.new
  @result = src.deep_clone
  process @result
  @result
end
process_scope(exp) click to toggle source

Process a new scope.

# File lib/brakeman/processors/alias_processor.rb, line 166
def process_scope exp
  env.scope do
    process exp.block
  end
  exp
end
process_selfdef(exp) click to toggle source

Process a method definition on self.

# File lib/brakeman/processors/alias_processor.rb, line 190
def process_selfdef exp
  env.scope do
    set_env_defaults
    exp.body = process_all! exp.body
  end
  exp
end
Also aliased as: process_defs
process_svalue(exp) click to toggle source

This is the right hand side value of a multiple assignment, like `x = y, z`

# File lib/brakeman/processors/alias_processor.rb, line 377
def process_svalue exp
  exp.value
end
set_line(exp, line_number) click to toggle source

Set line nunber for exp and every Sexp it contains. Used when replacing expressions, so warnings indicate the correct line.

# File lib/brakeman/processors/alias_processor.rb, line 608
def set_line exp, line_number
  if sexp? exp
    exp.original_line(exp.original_line || exp.line)
    exp.line line_number
    exp.each do |e|
      set_line e, line_number
    end
  end

  exp
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.