Brakeman.load_brakeman_dependency 'terminal-table' class Brakeman::Report::Table < Brakeman::Report::Base def initialize *args super @table = Terminal::Table end def generate_report summary_option = tracker.options[:summary_only] out = "" unless summary_option == :no_summary out << text_header << "\n\n+SUMMARY+\n\n" << truncate_table(generate_overview.to_s) << "\n\n" << truncate_table(generate_warning_overview.to_s) << "\n" end #Return output early if only summarizing if summary_option == :summary_only or summary_option == true return out end if tracker.options[:report_routes] or tracker.options[:debug] out << "\n+CONTROLLERS+\n" << truncate_table(generate_controllers.to_s) << "\n" end if tracker.options[:debug] out << "\n+TEMPLATES+\n\n" << truncate_table(generate_templates.to_s) << "\n" end output_table("+Obsolete Ignore Entries+", generate_obsolete, out) output_table("+Errors+", generate_errors, out) output_table("+SECURITY WARNINGS+", generate_warnings, out) output_table("Controller Warnings:", generate_controller_warnings, out) output_table("Model Warnings:", generate_model_warnings, out) output_table("View Warnings:", generate_template_warnings, out) out << "\n" out end def output_table title, result, output return unless result output << "\n\n#{title}\n\n#{truncate_table(result.to_s)}" end def generate_overview num_warnings = all_warnings.length @table.new(:headings => ['Scanned/Reported', 'Total']) do |t| t.add_row ['Controllers', tracker.controllers.length] t.add_row ['Models', tracker.models.length - 1] t.add_row ['Templates', number_of_templates(@tracker)] t.add_row ['Errors', tracker.errors.length] t.add_row ['Security Warnings', "#{num_warnings} (#{warnings_summary[:high_confidence]})"] t.add_row ['Ignored Warnings', ignored_warnings.length] unless ignored_warnings.empty? end end #Generate table of how many warnings of each warning type were reported def generate_warning_overview types = warnings_summary.keys types.delete :high_confidence values = types.sort.collect{|warning_type| [warning_type, warnings_summary[warning_type]] } locals = {:types => types, :warnings_summary => warnings_summary} render_array('warning_overview', ['Warning Type', 'Total'], values, locals) end #Generate table of controllers and routes found for those controllers def generate_controllers controller_rows = controller_information cols = ['Name', 'Parent', 'Includes', 'Routes'] locals = {:controller_rows => controller_rows} values = controller_rows.collect{|row| row.values_at(*cols) } render_array('controller_overview', cols, values, locals) end #Generate table of errors or return nil if no errors def generate_errors values = tracker.errors.collect{|error| [error[:error], error[:backtrace][0]]} render_array('error_overview', ['Error', 'Location'], values, {:tracker => tracker}) end def generate_obsolete values = tracker.unused_fingerprints.collect{|fingerprint| [fingerprint] } render_array('obsolete_ignore_entries', ['fingerprint'], values, {:tracker => tracker}) end def generate_warnings render_warnings generic_warnings, :warning, 'security_warnings', ["Confidence", "Class", "Method", "Warning Type", "CWE ID", "Message"], 'Class' end #Generate table of template warnings or return nil if no warnings def generate_template_warnings render_warnings template_warnings, :template, 'view_warnings', ['Confidence', 'Template', 'Warning Type', "CWE ID", 'Message'], 'Template' end #Generate table of model warnings or return nil if no warnings def generate_model_warnings render_warnings model_warnings, :model, 'model_warnings', ['Confidence', 'Model', 'Warning Type', "CWE ID", 'Message'], 'Model' end #Generate table of controller warnings or nil if no warnings def generate_controller_warnings render_warnings controller_warnings, :controller, 'controller_warnings', ['Confidence', 'Controller', 'Warning Type', "CWE ID", 'Message'], 'Controller' end def generate_ignored_warnings render_warnings ignored_warnings, :ignored, 'ignored_warnings', ['Confidence', 'Warning Type', "CWE ID", 'File', 'Message'], 'Warning Type' end def render_warnings warnings, type, template, cols, sort_col unless warnings.empty? rows = sort(convert_to_rows(warnings, type), sort_col) values = rows.collect { |row| row.values_at(*cols) } locals = { :warnings => rows } render_array(template, cols, values, locals) else nil end end #Generate listings of templates and their output def generate_templates out_processor = Brakeman::OutputProcessor.new template_rows = {} tracker.templates.each do |name, template| template.each_output do |out| out = out_processor.format out template_rows[name] ||= [] template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ") end end template_rows = template_rows.sort_by{|name, value| name.to_s} output = '' template_rows.each do |template| output << template.first.to_s << "\n\n" table = @table.new(:headings => ['Output']) do |t| # template[1] is an array of calls template[1].each do |v| t.add_row [v] end end output << table.to_s << "\n\n" end output end def convert_to_rows warnings, type = :warning warnings.map do |warning| w = warning.to_row type case type when :warning convert_warning w, warning when :ignored convert_ignored_warning w, warning when :template convert_template_warning w, warning else convert_warning w, warning end end end def convert_ignored_warning warning, original convert_warning warning, original end def convert_template_warning warning, original convert_warning warning, original end def sort rows, sort_col stabilizer = 0 rows.sort_by do |row| stabilizer += 1 row.values_at("Confidence", "Warning Type", sort_col) << stabilizer end end def render_array template, headings, value_array, locals return if value_array.empty? @table.new(:headings => headings) do |t| value_array.each { |value_row| t.add_row value_row } end end def convert_warning warning, original warning["Message"] = text_message original, warning["Message"] warning end #Escape warning message and highlight user input in text output def text_message warning, message message = message.to_s if warning.line message << " near line #{warning.line}" end if warning.code if @highlight_user_input and warning.user_input code = warning.format_with_user_input do |user_input, user_input_string| "+#{user_input_string}+" end else code = warning.format_code end message << ": #{code}" end message end #Generate header for text output def text_header <<-HEADER +BRAKEMAN REPORT+ Application path: #{tracker.app_path} Rails version: #{rails_version} Brakeman version: #{Brakeman::Version} Started at #{tracker.start_time} Duration: #{tracker.duration} seconds Checks run: #{checks.checks_run.sort.join(", ")} HEADER end def truncate_table str @terminal_width ||= if @tracker.options[:table_width] @tracker.options[:table_width] elsif $stdin && $stdin.tty? Brakeman.load_brakeman_dependency 'highline' ::HighLine.default_instance.terminal.terminal_size[0] else 80 end lines = str.lines lines.map do |line| if line.chomp.length > @terminal_width line[0..(@terminal_width - 3)] + ">>\n" else line end end.join end end