require 'rubygems' require 'cgi' require 'set' require 'ruport' require 'processors/output_processor' require 'util' #Fix for Ruport under 1.9 #as reported here: https://github.com/ruport/ruport/pull/7 module Ruport class Formatter::CSV < Formatter def csv_writer @csv_writer ||= options.formatter || FCSV.instance(output, options.format_options || {}) end end end #Generates a report based on the Tracker and the results of #Tracker#run_checks. Be sure to +run_checks+ before generating #a report. class Report include Util attr_reader :tracker, :checks TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ] HTML_CONFIDENCE = [ "High", "Medium", "Weak" ] def initialize tracker @tracker = tracker @checks = tracker.checks @element_id = 0 #Used for HTML ids @warnings_summary = nil end #Generate summary table of what was parsed def generate_overview templates = Set.new(@tracker.templates.map {|k,v| v[:name].to_s[/[^.]+/]}).length warnings = checks.warnings.length + checks.controller_warnings.length + checks.model_warnings.length + checks.template_warnings.length #Add number of high confidence warnings in summary. #Skipping for CSV because it makes the cell text instead of #a number. unless OPTIONS[:output_format] == :to_csv summary = warnings_summary if OPTIONS[:output_format] == :to_html warnings = "#{warnings} (#{summary[:high_confidence]})" else warnings = "#{warnings} (#{summary[:high_confidence]})" end end table = Ruport::Data::Table(["Scanned/Reported", "Total"]) table << { "Scanned/Reported" => "Controllers", "Total" => tracker.controllers.length } #One less because of the 'fake' one used for unknown models table << { "Scanned/Reported" => "Models", "Total" => tracker.models.length - 1 } table << { "Scanned/Reported" => "Templates", "Total" => templates } table << { "Scanned/Reported" => "Errors", "Total" => tracker.errors.length } table << { "Scanned/Reported" => "Security Warnings", "Total" => warnings} end #Generate table of how many warnings of each warning type were reported def generate_warning_overview table = Ruport::Data::Table(["Warning Type", "Total"]) types = warnings_summary.keys types.delete :high_confidence types.sort.each do |warning_type| table << { "Warning Type" => warning_type, "Total" => warnings_summary[warning_type] } end table end #Generate table of errors or return nil if no errors def generate_errors unless tracker.errors.empty? table = Ruport::Data::Table(["Error", "Location"]) tracker.errors.each do |w| p w if OPTIONS[:debug] if OPTIONS[:output_format] == :to_html w[:error] = CGI.escapeHTML w[:error] end table << { "Error" => w[:error], "Location" => w[:backtrace][0] } end table else nil end end #Generate table of general security warnings def generate_warnings table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"]) checks.warnings.each do |warning| next if warning.confidence > OPTIONS[:min_confidence] w = warning.to_row if OPTIONS[:output_format] == :to_html w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]] w["Message"] = with_context warning, w["Message"] else w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] end table << w end table.sort_rows_by! "Class" table.sort_rows_by! "Warning Type" table.sort_rows_by! "Confidence" if table.empty? table = Ruport::Data::Table("General Warnings") table << { "General Warnings" => "[NONE]" } end table end #Generate table of template warnings or return nil if no warnings def generate_template_warnings unless checks.template_warnings.empty? table = Ruport::Data::Table(["Confidence", "Template", "Warning Type", "Message"]) checks.template_warnings.each do |warning| next if warning.confidence > OPTIONS[:min_confidence] w = warning.to_row :template if OPTIONS[:output_format] == :to_html w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]] w["Message"] = with_context warning, w["Message"] else w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] end table << w end if table.empty? nil else table.sort_rows_by! "Template" table.sort_rows_by! "Warning Type" table.sort_rows_by! "Confidence" table.to_group "View Warnings" end else nil end end #Generate table of model warnings or return nil if no warnings def generate_model_warnings unless checks.model_warnings.empty? table = Ruport::Data::Table(["Confidence", "Model", "Warning Type", "Message"]) checks.model_warnings.each do |warning| next if warning.confidence > OPTIONS[:min_confidence] w = warning.to_row :model if OPTIONS[:output_format] == :to_html w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]] w["Message"] = with_context warning, w["Message"] else w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] end table << w end if table.empty? nil else table.sort_rows_by! "Model" table.sort_rows_by! "Warning Type" table.sort_rows_by! "Confidence" table.to_group "Model Warnings" end else nil end end #Generate table of controller warnings or nil if no warnings def generate_controller_warnings unless checks.controller_warnings.empty? table = Ruport::Data::Table(["Confidence", "Controller", "Warning Type", "Message"]) checks.controller_warnings.each do |warning| next if warning.confidence > OPTIONS[:min_confidence] w = warning.to_row :controller if OPTIONS[:output_format] == :to_html w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]] w["Message"] = with_context warning, w["Message"] else w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] end table << w end if table.empty? nil else table.sort_rows_by! "Controller" table.sort_rows_by! "Warning Type" table.sort_rows_by! "Confidence" table.to_group "Controller Warnings" end else nil end end #Generate table of controllers and routes found for those controllers def generate_controllers table = Ruport::Data::Table(["Name", "Parent", "Includes", "Routes"]) tracker.controllers.keys.map{|k| k.to_s}.sort.each do |name| name = name.to_sym c = tracker.controllers[name] if tracker.routes[:allow_all_actions] or tracker.routes[name] == :allow_all_actions routes = c[:public].keys.map{|e| e.to_s}.sort.join(", ") elsif tracker.routes[name].nil? #No routes defined for this controller. #This can happen when it is only a parent class #for other controllers, for example. routes = "[None]" else routes = (Set.new(c[:public].keys) & tracker.routes[name.to_sym]). to_a. map {|e| e.to_s}. sort. join(", ") end if routes == "" routes = "[None]" end table << { "Name" => name.to_s, "Parent" => c[:parent].to_s, "Includes" => c[:includes].join(", "), "Routes" => routes } end table.sort_rows_by "Name" end #Generate listings of templates and their output def generate_templates out_processor = OutputProcessor.new table = Ruport::Data::Table(["Name", "Output"]) tracker.templates.each do |name, template| unless template[:outputs].empty? template[:outputs].each do |out| out = out_processor.format out out = CGI.escapeHTML(out) if OPTIONS[:output_format] == :to_html table << { "Name" => name, "Output" => out.gsub("\n", ";").gsub(/\s+/, " ") } end end end Ruport::Data::Grouping(table, :by => "Name") end #Generate HTML output def to_html out = html_header << "