class Brakeman::Scanner
Scans the Rails application.
Attributes
Public Class Methods
Pass in path to the root of the Rails application
# File lib/brakeman/scanner.rb, line 21 def initialize options, processor = nil @options = options @app_tree = Brakeman::AppTree.from_options(options) if (!@app_tree.root || !@app_tree.exists?("app")) && !options[:force_scan] message = "Please supply the path to a Rails application (looking in #{@app_tree.root}).\n" << " Use `--force` to run a scan anyway." raise Brakeman::NoApplication, message end @processor = processor || Brakeman::Processor.new(@app_tree, options) @show_timing = tracker.options[:debug] || tracker.options[:show_timing] end
Public Instance Methods
# File lib/brakeman/scanner.rb, line 144 def detect_file_types @file_list = { controllers: [], initializers: [], libs: [], models: [], templates: [], } detector = Brakeman::FileTypeDetector.new @parsed_files.each do |file| if file.is_a? Brakeman::TemplateParser::TemplateFile @file_list[:templates] << file else type = detector.detect_type(file) unless type == :skip if @file_list[type].nil? raise type.to_s else @file_list[type] << file end end end end end
Set :rails3/:rails4 option if version was not determined from Gemfile
# File lib/brakeman/scanner.rb, line 249 def guess_rails_version unless tracker.options[:rails3] or tracker.options[:rails4] if @app_tree.exists?("script/rails") tracker.options[:rails3] = true Brakeman.notify "[Notice] Detected Rails 3 application" elsif @app_tree.exists?("app/channels") tracker.options[:rails3] = true tracker.options[:rails4] = true tracker.options[:rails5] = true Brakeman.notify "[Notice] Detected Rails 5 application" elsif not @app_tree.exists?("script") tracker.options[:rails3] = true tracker.options[:rails4] = true Brakeman.notify "[Notice] Detected Rails 4 application" end end end
# File lib/brakeman/scanner.rb, line 412 def index_call_sites tracker.index_call_sites end
# File lib/brakeman/scanner.rb, line 127 def parse_files fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks], tracker.options[:use_prism]) fp.parse_files tracker.app_tree.ruby_file_paths template_parser = Brakeman::TemplateParser.new(tracker, fp) fp.read_files(@app_tree.template_paths) do |path, contents| template_parser.parse_template path, contents end # Collect errors raised during parsing tracker.add_errors(fp.errors) @parsed_files = fp.file_list end
# File lib/brakeman/scanner.rb, line 416 def parse_ruby_file file fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], false, tracker.options[:use_prism]) fp.parse_ruby(file.read, file) rescue Exception => e tracker.error(e) nil end
Process everything in the Rails application
# File lib/brakeman/scanner.rb, line 70 def process process_step 'Processing gems' do process_gems end process_step 'Processing configuration' do guess_rails_version process_config end process_step 'Parsing files' do parse_files end process_step 'Detecting file types' do detect_file_types end process_step 'Processing initializers' do process_initializers end process_step 'Processing libs' do process_libs end process_step 'Processing routes' do process_routes end process_step 'Processing templates' do process_templates end process_step 'Processing data flow in templates' do process_template_data_flows end process_step 'Processing models' do process_models end process_step 'Processing controllers' do process_controllers end process_step 'Processing data flow in controllers' do process_controller_data_flows end process_step 'Indexing call sites' do index_call_sites end tracker end
Process config/environment.rb and config/gems.rb
Stores parsed information in tracker.config
# File lib/brakeman/scanner.rb, line 174 def process_config # Sometimes folks like to put constants in environment.rb # so let's always process it even for newer Rails versions process_config_file "environment.rb" if options[:rails3] or options[:rails4] or options[:rails5] or options[:rails6] process_config_file "application.rb" process_config_file "environments/production.rb" else process_config_file "gems.rb" end if @app_tree.exists?("vendor/plugins/rails_xss") or options[:rails3] or options[:escape_html] tracker.config.escape_html = true Brakeman.notify "[Notice] Escaping HTML by default" end if @app_tree.exists? ".ruby-version" if version = @app_tree.file_path(".ruby-version").read[/(\d\.\d.\d+)/] tracker.config.set_ruby_version version, @app_tree.file_path(".ruby-version"), 1 end end tracker.config.load_rails_defaults end
# File lib/brakeman/scanner.rb, line 347 def process_controller astfile begin @processor.process_controller(astfile.ast, astfile.path) rescue => e tracker.error e.exception(e.message + "\nWhile processing #{astfile.path}"), e.backtrace end end
# File lib/brakeman/scanner.rb, line 332 def process_controller_data_flows controllers = tracker.controllers.sort_by { |name, _| name.to_s } track_progress controllers, "controllers" do |name, controller| process_step_file name do controller.src.each do |file, src| @processor.process_controller_alias name, src, nil, file end end end #No longer need these processed filter methods tracker.filter_cache.clear end
Process all .rb files in controllers/
Adds processed controllers to tracker.controllers
# File lib/brakeman/scanner.rb, line 324 def process_controllers track_progress @file_list[:controllers] do |controller| process_step_file controller.path do process_controller controller end end end
Process Gemfile
# File lib/brakeman/scanner.rb, line 217 def process_gems gem_files = {} if @app_tree.exists? "Gemfile" file = @app_tree.file_path("Gemfile") gem_files[:gemfile] = { :src => parse_ruby_file(file), :file => file } elsif @app_tree.exists? "gems.rb" file = @app_tree.file_path("gems.rb") gem_files[:gemfile] = { :src => parse_ruby_file(file), :file => file } end if @app_tree.exists? "Gemfile.lock" file = @app_tree.file_path("Gemfile.lock") gem_files[:gemlock] = { :src => file.read, :file => file } elsif @app_tree.exists? "gems.locked" file = @app_tree.file_path("gems.locked") gem_files[:gemlock] = { :src => file.read, :file => file } end if @app_tree.gemspec gem_files[:gemspec] = { :src => parse_ruby_file(@app_tree.gemspec), :file => @app_tree.gemspec } end if not gem_files.empty? @processor.process_gems gem_files end rescue => e Brakeman.notify "[Notice] Error while processing Gemfile." tracker.error e.exception(e.message + "\nWhile processing Gemfile"), e.backtrace end
Process an initializer
# File lib/brakeman/scanner.rb, line 279 def process_initializer init @processor.process_initializer(init.path, init.ast) end
Process all the .rb files in config/initializers/
Adds parsed information to tracker.initializers
# File lib/brakeman/scanner.rb, line 270 def process_initializers track_progress @file_list[:initializers] do |init| process_step_file init[:path] do process_initializer init end end end
Process a library
# File lib/brakeman/scanner.rb, line 300 def process_lib lib @processor.process_lib lib.ast, lib.path end
Process all .rb in lib/
Adds parsed information to tracker.libs.
# File lib/brakeman/scanner.rb, line 286 def process_libs if options[:skip_libs] Brakeman.notify '[Skipping]' return end track_progress @file_list[:libs] do |lib| process_step_file lib.path do process_lib lib end end end
# File lib/brakeman/scanner.rb, line 393 def process_model path, ast @processor.process_model(ast, path) end
Process all the .rb files in models/
Adds the processed models to tracker.models
# File lib/brakeman/scanner.rb, line 385 def process_models track_progress @file_list[:models] do |model| process_step_file model[:path] do process_model model[:path], model[:ast] end end end
Process config/routes.rb
Adds parsed information to tracker.routes
# File lib/brakeman/scanner.rb, line 307 def process_routes if @app_tree.exists?("config/routes.rb") file = @app_tree.file_path("config/routes.rb") if routes_sexp = parse_ruby_file(file) @processor.process_routes routes_sexp else Brakeman.notify "[Notice] Error while processing routes - assuming all public controller methods are actions." options[:assume_all_routes] = true end else Brakeman.notify "[Notice] No route information found" end end
# File lib/brakeman/scanner.rb, line 41 def process_step description Brakeman.notify "#{description}...".ljust(40) if @show_timing start_t = Time.now yield duration = Time.now - start_t Brakeman.notify "(#{description}) Duration: #{duration} seconds" else yield end end
# File lib/brakeman/scanner.rb, line 55 def process_step_file description if @show_timing Brakeman.notify "Processing #{description}" start_t = Time.now yield duration = Time.now - start_t Brakeman.notify "(#{description}) Duration: #{duration} seconds" else yield end end
# File lib/brakeman/scanner.rb, line 368 def process_template template @processor.process_template(template.name, template.ast, template.type, nil, template.path) end
# File lib/brakeman/scanner.rb, line 372 def process_template_data_flows templates = tracker.templates.sort_by { |name, _| name.to_s } track_progress templates, "templates" do |name, template| process_step_file name do @processor.process_template_alias template end end end
Process all views and partials in views/
Adds processed views to tracker.views
# File lib/brakeman/scanner.rb, line 358 def process_templates templates = @file_list[:templates].sort_by { |t| t[:path] } track_progress templates, "templates" do |template| process_step_file template[:path] do process_template template end end end
# File lib/brakeman/scanner.rb, line 407 def report_progress(current, total, type = "files") return unless @options[:report_progress] $stderr.print " #{current}/#{total} #{type} processed\r" end
# File lib/brakeman/scanner.rb, line 397 def track_progress list, type = "files" total = list.length current = 0 list.each do |item| report_progress current, total, type current += 1 yield item end end
Returns the Tracker generated from the scan
# File lib/brakeman/scanner.rb, line 37 def tracker @processor.tracked_events end
Private Instance Methods
# File lib/brakeman/scanner.rb, line 202 def process_config_file file path = @app_tree.file_path("config/#{file}") if path.exists? @processor.process_config(parse_ruby_file(path), path) end rescue => e Brakeman.notify "[Notice] Error while processing #{path}" tracker.error e.exception(e.message + "\nwhile processing #{path}"), e.backtrace end