class Brakeman::CallIndex

Stores call sites to look up later.

Public Class Methods

new(calls) click to toggle source

Initialize index with calls from FindAllCalls

# File lib/brakeman/call_index.rb, line 7
def initialize calls
  @calls_by_method = {}
  @calls_by_target = {}

  index_calls calls
end

Public Instance Methods

find_calls(options) click to toggle source

Find calls matching specified option hash.

Options:

* :target - symbol, array of symbols, or regular expression to match target(s)
* :method - symbol, array of symbols, or regular expression to match method(s)
* :chained - boolean, whether or not to match against a whole method chain (false by default)
* :nested - boolean, whether or not to match against a method call that is a target itself (false by default)
# File lib/brakeman/call_index.rb, line 22
def find_calls options
  target = options[:target] || options[:targets]
  method = options[:method] || options[:methods]
  nested = options[:nested]

  if options[:chained]
    return find_chain options
  #Find by narrowest category
  elsif target.is_a? Array and method.is_a? Array
    if target.length > method.length
      calls = filter_by_target calls_by_methods(method), target
    else
      calls = calls_by_targets(target)
      calls = filter_by_method calls, method
    end

  elsif target.is_a? Regexp and method
    calls = filter_by_target(calls_by_method(method), target)

  elsif method.is_a? Regexp and target
    calls = filter_by_method(calls_by_target(target), method)

  #Find by target, then by methods, if provided
  elsif target
    calls = calls_by_target target

    if calls and method
      calls = filter_by_method calls, method
    end

  #Find calls with no explicit target
  #with either :target => nil or :target => false
  elsif (options.key? :target or options.key? :targets) and not target and method
    calls = calls_by_method method
    calls = filter_by_target calls, nil

  #Find calls by method
  elsif method
    calls = calls_by_method method
  else
    raise "Invalid arguments to CallCache#find_calls: #{options.inspect}"
  end

  return [] if calls.nil?

  #Remove calls that are actually targets of other calls
  #Unless those are explicitly desired
  calls = filter_nested calls unless nested

  calls
end
index_calls(calls) click to toggle source
# File lib/brakeman/call_index.rb, line 104
def index_calls calls
  calls.each do |call|
    @calls_by_method[call[:method]] ||= []
    @calls_by_method[call[:method]] << call

    target = call[:target]

    if not target.is_a? Sexp
      @calls_by_target[target] ||= []
      @calls_by_target[target] << call
    elsif target.node_type == :params or target.node_type == :session
      @calls_by_target[target.node_type] ||= []
      @calls_by_target[target.node_type] << call
    end
  end
end
remove_indexes_by_class(classes) click to toggle source
# File lib/brakeman/call_index.rb, line 84
def remove_indexes_by_class classes
  [@calls_by_method, @calls_by_target].each do |calls_by|
    calls_by.each do |_name, calls|
      calls.delete_if do |call|
        call[:location][:type] == :class and classes.include? call[:location][:class]
      end
    end
  end
end
remove_indexes_by_file(file) click to toggle source
# File lib/brakeman/call_index.rb, line 94
def remove_indexes_by_file file
  [@calls_by_method, @calls_by_target].each do |calls_by|
    calls_by.each do |_name, calls|
      calls.delete_if do |call|
        call[:location][:file] == file
      end
    end
  end
end
remove_template_indexes(template_name = nil) click to toggle source
# File lib/brakeman/call_index.rb, line 74
def remove_template_indexes template_name = nil
  [@calls_by_method, @calls_by_target].each do |calls_by|
    calls_by.each do |_name, calls|
      calls.delete_if do |call|
        from_template call, template_name
      end
    end
  end
end

Private Instance Methods

calls_by_method(method) click to toggle source
# File lib/brakeman/call_index.rb, line 168
def calls_by_method method
  case method
  when Array
    calls_by_methods method
  when Regexp
    calls_by_methods_regex method
  else
    @calls_by_method[method.to_sym] || []
  end
end
calls_by_methods(methods) click to toggle source
# File lib/brakeman/call_index.rb, line 179
def calls_by_methods methods
  methods = methods.map { |m| m.to_sym }
  calls = []

  methods.each do |method|
    calls.concat @calls_by_method[method] if @calls_by_method.key? method
  end

  calls
end
calls_by_methods_regex(methods_regex) click to toggle source
# File lib/brakeman/call_index.rb, line 190
def calls_by_methods_regex methods_regex
  calls = []

  @calls_by_method.each do |key, value|
    calls.concat value if key.match methods_regex
  end

  calls
end
calls_by_target(target) click to toggle source
# File lib/brakeman/call_index.rb, line 134
def calls_by_target target
  case target
  when Array
    calls_by_targets target
  when Regexp
    calls_by_targets_regex target
  else
    @calls_by_target[target] || []
  end
end
calls_by_targets(targets) click to toggle source
# File lib/brakeman/call_index.rb, line 145
def calls_by_targets targets
  calls = []

  targets.each do |target|
    calls.concat @calls_by_target[target] if @calls_by_target.key? target
  end

  calls
end
calls_by_targets_regex(targets_regex) click to toggle source
# File lib/brakeman/call_index.rb, line 155
def calls_by_targets_regex targets_regex
  calls = []

  @calls_by_target.each do |key, value|
    case key
    when String, Symbol
      calls.concat value if key.match targets_regex
    end
  end

  calls
end
filter(calls, key, value) click to toggle source
# File lib/brakeman/call_index.rb, line 200
def filter calls, key, value
  case value
  when Array
    values = Set.new value

    calls.select do |call|
      values.include? call[key]
    end
  when Regexp
    calls.select do |call|
      case call[key]
      when String, Symbol
        call[key].match value
      end
    end
  else
    calls.select do |call|
      call[key] == value
    end
  end
end
filter_by_chain(calls, target) click to toggle source
# File lib/brakeman/call_index.rb, line 234
def filter_by_chain calls, target
  case target
  when Array
    targets = Set.new target

    calls.select do |call|
      targets.include? call[:chain].first
    end
  when Regexp
    calls.select do |call|
      case call[:chain].first
      when String, Symbol
        call[:chain].first.match target
      end
    end
  else
    calls.select do |call|
      call[:chain].first == target
    end
  end
end
filter_by_method(calls, method) click to toggle source
# File lib/brakeman/call_index.rb, line 222
def filter_by_method calls, method
  filter calls, :method, method
end
filter_by_target(calls, target) click to toggle source
# File lib/brakeman/call_index.rb, line 226
def filter_by_target calls, target
  filter calls, :target, target
end
filter_nested(calls) click to toggle source
# File lib/brakeman/call_index.rb, line 230
def filter_nested calls
  filter calls, :nested, false
end
find_chain(options) click to toggle source
# File lib/brakeman/call_index.rb, line 123
def find_chain options
  target = options[:target] || options[:targets]
  method = options[:method] || options[:methods]

  calls = calls_by_method method

  return [] if calls.nil?

  calls = filter_by_chain calls, target
end
from_template(call, template_name) click to toggle source
# File lib/brakeman/call_index.rb, line 256
def from_template call, template_name
  return false unless call[:location][:type] == :template
  return true if template_name.nil?
  call[:location][:template] == template_name
end