lib/brakeman/processors/alias_processor.rb in brakeman-1.9.4 vs lib/brakeman/processors/alias_processor.rb in brakeman-1.9.5

- old
+ new

@@ -17,11 +17,11 @@ # # AliasProcessor.new.process_safely src def initialize tracker = nil super() @env = SexpProcessor::Environment.new - @inside_if = [] + @inside_if = false @ignore_ifs = nil @exp_context = [] @current_module = nil @tracker = tracker #set in subclass as necessary @helper_method_cache = {} @@ -165,10 +165,45 @@ end exp end + def process_call_with_block exp + exp[1] = process exp.block_call + + env.scope do + exp.block_args.each do |e| + #Force block arg(s) to be local + if node_type? e, :lasgn + env.current[Sexp.new(:lvar, e.lhs)] = e.rhs + elsif node_type? e, :masgn + e[1..-1].each do |var| + local = Sexp.new(:lvar, var) + env.current[local] = local + end + elsif e.is_a? Symbol + local = Sexp.new(:lvar, e) + env.current[local] = local + else + raise "Unexpected value in block args: #{e.inspect}" + end + end + + block = exp.block + + if block? block + process_all! block + else + exp[3] = process block + end + end + + exp + end + + alias process_iter process_call_with_block + #Process a new scope. def process_scope exp env.scope do process exp.block end @@ -230,23 +265,24 @@ #Global assignment # $x = 1 def process_gasgn exp match = Sexp.new(:gvar, exp.lhs) value = exp.rhs = process(exp.rhs) + value.line = exp.line - set_value match, value, exp.line + set_value match, value exp end #Class variable assignment # @@x = 1 def process_cvdecl exp match = Sexp.new(:cvar, exp.lhs) value = exp.rhs = process(exp.rhs) - set_value match, value, exp.line + set_value match, value exp end #'Attribute' assignment @@ -263,21 +299,21 @@ if method == :[]= index = exp.first_arg = process(index_arg) value = exp.second_arg = process(value_arg) match = Sexp.new(:call, target, :[], index) - set_value match, value, exp.line + set_value 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) - set_value match, value, exp.line + set_value match, value else raise "Unrecognized assignment: #{exp}" end exp end @@ -375,38 +411,73 @@ @ignore_ifs = @tracker && @tracker.options[:ignore_ifs] end condition = process exp.condition + #Check if a branch is obviously going to be taken if true? condition no_branch = true - exps = [exp.then_clause] + exps = [exp.then_clause, nil] elsif false? condition no_branch = true - exps = [exp.else_clause] + exps = [nil, exp.else_clause] else no_branch = false exps = [exp.then_clause, exp.else_clause] end - exps.each do |e| - @inside_if << [] unless no_branch or @ignore_ifs + if @ignore_ifs or no_branch + exps.each_with_index do |branch, i| + exp[2 + i] = process_if_branch branch + end + else + was_inside = @inside_if + @inside_if = true - if sexp? e - if e.node_type == :block - process_default e #avoid creating new scope - else - process e + branch_scopes = [] + exps.each_with_index do |branch, i| + scope do + branch_index = 2 + i # s(:if, condition, then_branch, else_branch) + exp[branch_index] = process_if_branch branch + branch_scopes << env.current end end - @inside_if.pop unless no_branch or @ignore_ifs + @inside_if = was_inside + + branch_scopes.each do |s| + merge_if_branch s + end end exp end + def process_if_branch exp + if sexp? exp + if block? exp + process_default exp + else + process exp + end + end + end + + def merge_if_branch branch_env + branch_env.each do |k, v| + current_val = env[k] + + if current_val + unless same_value? current_val, v + env[k] = Sexp.new(:or, current_val, v).line(k.line || -2) + end + else + env[k] = v + end + end + end + #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 @@ -599,16 +670,10 @@ 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 @@ -617,35 +682,44 @@ else false end end + def value_from_if exp + if block? exp.else_clause or block? exp.then_clause + #If either clause is more than a single expression, just use entire + #if expression for now + exp + elsif exp.else_clause.nil? + exp.then_clause + elsif exp.then_clause.nil? + exp.else_clause + else + condition = exp.condition + + if true? condition + exp.then_clause + elsif false? condition + exp.else_clause + else + Sexp.new(:or, exp.then_clause, exp.else_clause).line(exp.line) + end + 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 + def set_value var, value + if node_type? value, :if + value = value_from_if(value) + end - 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 + if @ignore_ifs or not @inside_if env[var] = value + else + env.current[var] = value end end #If possible, distribute operation over both sides of an or. #For example,