lib/brakeman/processors/alias_processor.rb in brakeman-1.9.2 vs lib/brakeman/processors/alias_processor.rb in brakeman-1.9.3
- old
+ new
@@ -17,12 +17,12 @@
#
# AliasProcessor.new.process_safely src
def initialize tracker = nil
super()
@env = SexpProcessor::Environment.new
- @inside_if = false
- @ignore_ifs = false
+ @inside_if = []
+ @ignore_ifs = nil
@exp_context = []
@current_module = nil
@tracker = tracker #set in subclass as necessary
@helper_method_cache = {}
@helper_method_info = Hash.new({})
@@ -43,11 +43,11 @@
process @result
@result
end
#Process a Sexp. If the Sexp has a value associated with it in the
- #environment, that value will be returned.
+ #environment, that value will be returned.
def process_default exp
@exp_context.push exp
begin
exp.map! do |e|
@@ -85,16 +85,21 @@
target = exp.target
method = exp.method
first_arg = exp.first_arg
+ if node_type? target, :or and [:+, :-, :*, :/].include? method
+ res = process_or_simple_operation(exp)
+ return res if res
+ end
+
#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 = 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
@@ -204,72 +209,45 @@
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
+ set_value local, exp.rhs
exp
end
#Instance variable assignment
# @x = 1
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
+ set_value ivar, exp.rhs
exp
end
#Global assignment
# $x = 1
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
+ set_value match, value, exp.line
exp
end
#Class variable assignment
# @@x = 1
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
+ set_value match, value, exp.line
+
exp
end
#'Attribute' assignment
# x.y = 1
@@ -284,27 +262,22 @@
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
+ set_value match, value, exp.line
+
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
+ set_value match, value, exp.line
else
raise "Unrecognized assignment: #{exp}"
end
exp
end
@@ -396,41 +369,45 @@
exp
end
#Sets @inside_if = true
def process_if exp
- @ignore_ifs ||= @tracker && @tracker.options[:ignore_ifs]
+ if @ignore_ifs.nil?
+ @ignore_ifs = @tracker && @tracker.options[:ignore_ifs]
+ end
condition = process exp.condition
if true? condition
+ no_branch = true
exps = [exp.then_clause]
elsif false? condition
+ no_branch = true
exps = [exp.else_clause]
else
+ no_branch = false
exps = [exp.then_clause, exp.else_clause]
end
- was_inside = @inside_if
- @inside_if = !@ignore_ifs
-
exps.each do |e|
+ @inside_if << [] unless no_branch or @ignore_ifs
+
if sexp? e
if e.node_type == :block
process_default e #avoid creating new scope
else
process e
end
end
+
+ @inside_if.pop unless no_branch or @ignore_ifs
end
- @inside_if = was_inside
-
exp
end
- #Process single integer access to an array.
+ #Process single integer access to an array.
#
#Returns the value inside the array, if possible.
def process_array_access target, args
if args.length == 1 and integer? args.first
index = args.first.value
@@ -626,15 +603,103 @@
end
end
def duplicate? exp
@exp_context[0..-2].reverse_each do |e|
- return true if exp == e
+ return true if exp == e
end
false
end
def find_method *args
nil
end
+
+ #Return true if this value should be "branched" - i.e. converted into an or
+ #expression with two or more values
+ def branch_value? exp
+ not (@inside_if.empty? or @inside_if.last.include? exp)
+ end
+
+ #Return true if lhs == rhs or lhs is an or expression and
+ #rhs is one of its values
+ def same_value? lhs, rhs
+ if lhs == rhs
+ true
+ elsif node_type? lhs, :or
+ lhs.rhs == rhs or lhs.lhs == rhs
+ else
+ false
+ end
+ end
+
+ #Set variable to given value.
+ #Creates "branched" versions of values when appropriate.
+ #Avoids creating multiple branched versions inside same
+ #if branch.
+ def set_value var, value, line = nil
+ unless @ignore_ifs
+ current_val = env[var]
+ current_if = @inside_if.last
+
+ if branch_value? var and current_val = env[var]
+ unless same_value? current_val, value
+ env[var] = Sexp.new(:or, current_val, value).line(line || var.line || -2)
+ current_if << var
+ end
+ elsif current_if and current_if.include?(var) and node_type?(current_val, :or)
+ #Replace last value instead of creating another or
+ current_val.rhs = value
+ else
+ env[var] = value
+
+ if current_if
+ current_if << var
+ end
+ end
+ else
+ env[var] = value
+ end
+ end
+
+ #If possible, distribute operation over both sides of an or.
+ #For example,
+ #
+ # (1 or 2) * 5
+ #
+ #Becomes
+ #
+ # (5 or 10)
+ #
+ #Only works for strings and numbers right now.
+ def process_or_simple_operation exp
+ arg = exp.first_arg
+ return nil unless string? arg or number? arg
+
+ target = exp.target
+ lhs = process_or_target(target.lhs, exp.dup)
+ rhs = process_or_target(target.rhs, exp.dup)
+
+ if lhs and rhs
+ if same_value? lhs, rhs
+ lhs
+ else
+ exp.target.lhs = lhs
+ exp.target.rhs = rhs
+ exp.target
+ end
+ else
+ nil
+ end
+ end
+
+ def process_or_target value, copy
+ if string? value or number? value
+ copy.target = value
+ process copy
+ else
+ false
+ end
+ end
+
end