class Brakeman::Rescanner

Class for rescanning changed files after an initial scan

Constants

KNOWN_TEMPLATE_EXTENSIONS
SCAN_ORDER

Public Class Methods

new(options, processor, changed_files) click to toggle source

Create new Rescanner to scan changed files

Calls superclass method Brakeman::Scanner::new
# File lib/brakeman/rescanner.rb, line 13
def initialize options, processor, changed_files
  super(options, processor)

  @paths = changed_files.map {|f| tracker.app_tree.file_path(f) }
  @old_results = tracker.filtered_warnings  #Old warnings from previous scan
  @changes = nil                 #True if files had to be rescanned
  @reindex = Set.new
end

Public Instance Methods

file_type(path) click to toggle source

Guess at what kind of file the path contains

# File lib/brakeman/rescanner.rb, line 319
def file_type path
  case path
  when /\/app\/controllers/
    :controller
  when /\/app\/views/
    :template
  when /\/app\/models/
    :model
  when /\/lib/
    :lib
  when /\/config\/initializers/
    :initializer
  when /config\/routes\.rb/
    :routes
  when /\/config\/.+\.(rb|yml)/
    :config
  when /\.ruby-version/
    :config
  when /Gemfile|gems\./
    :gemfile
  else
    :unknown
  end
end
parse_ruby_files(list) click to toggle source
# File lib/brakeman/rescanner.rb, line 394
def parse_ruby_files list
  paths = list.select(&:exists?)
  file_parser = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks])
  file_parser.parse_files paths
  tracker.add_errors(file_parser.errors)
  file_parser.file_list
end
recheck() click to toggle source

Runs checks. Will rescan files if they have not already been scanned

# File lib/brakeman/rescanner.rb, line 24
def recheck
  rescan if @changes.nil?

  tracker.run_checks if @changes

  Brakeman::RescanReport.new @old_results, tracker
end
remove_deleted_file(path) click to toggle source

Check controllers, templates, models and libs for data from file and delete it.

# File lib/brakeman/rescanner.rb, line 296
def remove_deleted_file path
  deleted = false

  [:controllers, :models, :libs].each do |collection|
    tracker.send(collection).delete_if do |_name, data|
      if data.files.include?(path)
        deleted = true
        true
      end
    end
  end

  tracker.templates.delete_if do |_name, data|
    if data.file == path
      deleted = true
      true
    end
  end

  deleted
end
rescan() click to toggle source

Rescans changed files

# File lib/brakeman/rescanner.rb, line 33
def rescan
  tracker.template_cache.clear

  paths_by_type = {}

  SCAN_ORDER.each do |type|
    paths_by_type[type] = []
  end

  @paths.each do |path|
    type = file_type(path)
    paths_by_type[type] << path unless type == :unknown
  end

  @changes = false

  SCAN_ORDER.each do |type|
    paths_by_type[type].each do |path|
      Brakeman.debug "Rescanning #{path} as #{type}"

      if rescan_file path, type
        @changes = true
      end
    end
  end

  if @changes and not @reindex.empty?
    tracker.reindex_call_sites @reindex
  end

  self
end
rescan_controller(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 102
def rescan_controller path
  controller = tracker.reset_controller path
  paths = controller.nil? ? [path] : controller.files
  parse_ruby_files(paths).each do |astfile|
    process_controller astfile
  end

  #Process data flow and template rendering
  #from the controller
  tracker.controllers.each do |name, controller|
    if controller.files.include?(path)
      tracker.templates.each do |template_name, template|
        next unless template.render_path
        if template.render_path.include_controller? name
          tracker.reset_template template_name
        end
      end

      controller.src.each do |file, src|
        @processor.process_controller_alias controller.name, src, nil, file
      end
    end
  end

  @reindex << :templates << :controllers
end
rescan_deleted_file(path, type) click to toggle source

Handle rescanning when a file is deleted

# File lib/brakeman/rescanner.rb, line 240
def rescan_deleted_file path, type
  case type
  when :controller
    rescan_controller path
  when :template
    rescan_deleted_template path
  when :model
    rescan_model path
  when :lib
    rescan_deleted_lib path
  when :initializer
    rescan_deleted_initializer path
  else
    if remove_deleted_file path
      return true
    else
      Brakeman.notify "Ignoring deleted file: #{path}"
    end
  end

  true
end
rescan_deleted_initializer(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 290
def rescan_deleted_initializer path
  tracker.initializers.delete Pathname.new(path).basename.to_s
end
rescan_deleted_lib(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 277
def rescan_deleted_lib path
  deleted_lib = nil

  tracker.libs.delete_if do |_name, lib|
    if lib.files.include?(path)
      deleted_lib = lib
      true
    end
  end

  rescan_mixin deleted_lib if deleted_lib
end
rescan_deleted_template(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 263
def rescan_deleted_template path
  return unless path.relative.match KNOWN_TEMPLATE_EXTENSIONS

  template_name = template_path_to_name(path)

  #Remove template
  tracker.reset_template template_name

  #Remove any rendered versions, or partials rendered from it
  tracker.templates.delete_if do |_name, template|
    template.file == path or template.name.to_sym == template_name.to_sym
  end
end
rescan_file(path, type = nil) click to toggle source

Rescans a single file

# File lib/brakeman/rescanner.rb, line 67
def rescan_file path, type = nil
  type ||= file_type path

  unless path.exists?
    return rescan_deleted_file path, type
  end

  case type
  when :controller
    rescan_controller path
  when :template
    rescan_template path
  when :model
    rescan_model path
  when :lib
    rescan_lib path
  when :config
    process_config
  when :initializer
    rescan_initializer path
  when :routes
    rescan_routes
  when :gemfile
    if tracker.config.has_gem? :rails_xss and tracker.config.escape_html?
      tracker.config.escape_html = false
    end

    process_gems
  else
    return false #Nothing to do, file hopefully does not need to be rescanned
  end

  true
end
rescan_initializer(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 229
def rescan_initializer path
  tracker.reset_initializer path

  parse_ruby_files([path]).each do |astfile|
    process_initializer astfile
  end

  @reindex << :initializers
end
rescan_lib(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 199
def rescan_lib path
  lib = tracker.reset_lib path
  paths = lib.nil? ? [path] : lib.files
  parse_ruby_files(paths).each do |astfile|
    process_lib astfile
  end

  lib = nil

  tracker.libs.each do |_name, library|
    if library.files.include?(path)
      lib = library
      break
    end
  end

  rescan_mixin lib if lib
end
rescan_mixin(lib) click to toggle source
# File lib/brakeman/rescanner.rb, line 344
def rescan_mixin lib
  method_names = []

  lib.each_method do |name, _meth|
    method_names << name
  end

  to_rescan = []

  #Rescan controllers that mixed in library
  tracker.controllers.each do |_name, controller|
    if controller.includes.include? lib.name
      controller.files.each do |path|
        unless @paths.include? path
          to_rescan << path
        end
      end
    end
  end

  to_rescan.each do |controller|
    tracker.reset_controller controller
    rescan_file controller
  end

  to_rescan = []

  #Check if a method from this mixin was used to render a template.
  #This is not precise, because a different controller might have the
  #same method...
  tracker.templates.each do |name, template|
    next unless template.render_path

    if template.render_path.include_any_method? method_names
      name.to_s.match(/^([^.]+)/)

      original = tracker.templates[$1.to_sym]

      if original
        to_rescan << [name, original.file]
      end
    end
  end

  to_rescan.each do |template|
    tracker.reset_template template[0]
    rescan_file template[1]
  end
end
rescan_model(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 181
def rescan_model path
  num_models = tracker.models.length
  model = tracker.reset_model path
  paths = model.nil? ? [path] : model.files
  parse_ruby_files(paths).each do |astfile|
    process_model astfile.path, astfile.ast
  end

  #Only need to rescan other things if a model is added or removed
  if num_models != tracker.models.length
    process_template_data_flows
    process_controller_data_flows
    @reindex << :templates << :controllers
  end

  @reindex << :models
end
rescan_routes() click to toggle source
# File lib/brakeman/rescanner.rb, line 218
def rescan_routes
  # Routes affect which controller methods are treated as actions
  # which affects which templates are rendered, so routes, controllers,
  # and templates rendered from controllers must be rescanned
  tracker.reset_routes
  tracker.reset_templates :only_rendered => true
  process_routes
  process_controller_data_flows
  @reindex << :controllers << :templates
end
rescan_template(path) click to toggle source
# File lib/brakeman/rescanner.rb, line 129
def rescan_template path
  return unless path.relative.match KNOWN_TEMPLATE_EXTENSIONS and path.exists?

  template_name = template_path_to_name(path)

  tracker.reset_template template_name
  fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
  template_parser = Brakeman::TemplateParser.new(tracker, fp)
  template_parser.parse_template path, path.read
  tracker.add_errors(fp.errors)
  process_template fp.file_list.first

  @processor.process_template_alias tracker.templates[template_name]

  rescan = Set.new

  #Search for processed template and process it.
  #Search for rendered versions of template and re-render (if necessary)
  tracker.templates.each do |_name, template|
    if template.file == path or template.file.nil?
      next unless template.render_path and template.name.to_sym == template_name.to_sym

      template.render_path.each do |from|
        case from[:type]
        when :template
          rescan << [:template, from[:name]]
        when :controller
          rescan << [:controller, from[:class], from[:method]]
        end
      end
    end
  end

  rescan.each do |r|
    if r[0] == :controller
      controller = tracker.controllers[r[1]]

      controller.src.each do |file, src|
        unless @paths.include? file
          @processor.process_controller_alias controller.name, src, r[2], file
        end
      end
    elsif r[0] == :template
      template = tracker.templates[r[1]]

      rescan_template template.file
    end
  end

  @reindex << :templates
end