lib/brakeman/processors/alias_processor.rb in brakeman-5.0.4 vs lib/brakeman/processors/alias_processor.rb in brakeman-5.1.0

- old
+ new

@@ -206,10 +206,19 @@ return Sexp.new(:hash).line(exp.line) elsif exp == RAILS_TEST or exp == RAILS_DEV return Sexp.new(:false).line(exp.line) end + # For the simplest case of `Foo.thing` + if node_type? target, :const and first_arg.nil? + if tracker and (klass = tracker.find_class(class_name(target.value))) + if return_value = klass.get_simple_method_return_value(:class, method) + return return_value.deep_clone(exp.line) + end + end + end + #See if it is possible to simplify some basic cases #of addition/concatenation. case method when :+ if array? target and array? first_arg @@ -218,17 +227,32 @@ exp = join_strings(target, first_arg, exp) elsif number? first_arg exp = math_op(:+, target, first_arg, exp) end when :-, :*, :/ - exp = math_op(method, target, first_arg, exp) + if method == :* and array? target + if string? first_arg + exp = process_array_join(target, first_arg) + end + else + exp = math_op(method, target, first_arg, exp) + end when :[] if array? target exp = process_array_access(target, exp.args, exp) elsif hash? target exp = process_hash_access(target, first_arg, exp) end + when :fetch + if array? target + # Not dealing with default value + # so just pass in first argument, but process_array_access expects + # an array of arguments. + exp = process_array_access(target, [first_arg], exp) + elsif hash? target + exp = process_hash_access(target, first_arg, 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 @@ -264,10 +288,16 @@ return target else target = find_push_target(target_var) env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor end + when :push + if array? target + target << first_arg + env[target_var] = target + return target + end when :first if array? target and first_arg.nil? and sexp? target[1] exp = target[1] end when :freeze @@ -277,35 +307,51 @@ when :dup unless target.nil? exp = target end when :join - if array? target and target.length > 2 and (string? first_arg or first_arg.nil?) + if array? target and (string? first_arg or first_arg.nil?) exp = process_array_join(target, first_arg) end when :! # Convert `!!a` to boolean if call? target and target.method == :! exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line) end + when :values + # Hash literal + if node_type? target, :hash + exp = hash_values(target) + end + when :values_at + if node_type? target, :hash + exp = hash_values_at target, exp.args + end end exp end # Painful conversion of Array#join into string interpolation def process_array_join array, join_str + # Empty array + if array.length == 1 + return s(:str, '').line(array.line) + end + result = s().line(array.line) join_value = if string? join_str join_str.value else nil end - array[1..-2].each do |e| - result << join_item(e, join_value) + if array.length > 2 + array[1..-2].each do |e| + result << join_item(e, join_value) + end end result << join_item(array.last, nil) # Combine the strings at the beginning because that's what RubyParser does @@ -330,19 +376,27 @@ end result.unshift combined_first # Have to fix up strings that follow interpolation - result.reduce(s(:dstr).line(array.line)) do |memo, e| + string = result.reduce(s(:dstr).line(array.line)) do |memo, e| if string? e and node_type? memo.last, :evstr e.value = "#{join_value}#{e.value}" elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr memo << s(:str, join_value).line(e.line) end memo << e end + + # Convert (:dstr, "hello world") + # to (:str, "hello world") + if string.length == 2 and string.last.is_a? String + string[0] = :str + end + + string end def join_item item, join_value if item.is_a? String "#{item}#{join_value}" @@ -747,10 +801,22 @@ env[match] = get_rhs(exp) exp end + def hash_or_array_include_all_literals? exp + return unless call? exp and sexp? exp.target + target = exp.target + + case target.node_type + when :hash + hash_include_all_literals? exp + else + array_include_all_literals? exp + end + end + # Check if exp is a call to Array#include? on an array literal # that contains all literal values. For example: # # [1, 2, "a"].include? x # @@ -765,10 +831,20 @@ [:detect, :find].include? exp.method and exp.first_arg.nil? and (all_literals? exp.target or dir_glob? exp.target) end + # Check if exp is a call to Hash#include? on a hash literal + # that contains all literal values. For example: + # + # {x: 1}.include? x + def hash_include_all_literals? exp + call? exp and + exp.method == :include? and + all_literals? exp.target, :hash + end + #Sets @inside_if = true def process_if exp if @ignore_ifs.nil? @ignore_ifs = @tracker && @tracker.options[:ignore_ifs] end @@ -805,19 +881,19 @@ branch_scopes = [] exps.each_with_index do |branch, i| scope do @branch_env = env.current branch_index = 2 + i # s(:if, condition, then_branch, else_branch) - if i == 0 and array_include_all_literals? condition + if i == 0 and hash_or_array_include_all_literals? condition # If the condition is ["a", "b"].include? x # set x to "a" inside the true branch var = condition.first_arg previous_value = env.current[var] env.current[var] = safe_literal(var.line) exp[branch_index] = process_if_branch branch env.current[var] = previous_value - elsif i == 1 and array_include_all_literals? condition and early_return? branch + elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch var = condition.first_arg env.current[var] = safe_literal(var.line) exp[branch_index] = process_if_branch branch else exp[branch_index] = process_if_branch branch @@ -1011,11 +1087,11 @@ 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 found_method = tracker.find_method(method_name, @current_class) + helper = found_method.src if sexp? helper value = process_helper_method helper, call.args value.line(call.line) return value