class Dawn::Reporter
Public Class Methods
new(options={})
click to toggle source
# File lib/dawn/reporter.rb, line 6 def initialize(options={}) @engine = nil @ret = false @filename = options[:filename] @ret = options[:apply_all_code] unless options[:apply_all_code].nil? @format = options[:format] unless options[:format].nil? @engine = options[:engine] unless options[:engine].nil? @format = :tabular unless is_valid_format?(@format) end
Public Instance Methods
report()
click to toggle source
# File lib/dawn/reporter.rb, line 18 def report ascii_tabular_report if @format == :tabular json_report if @format == :json ascii_plain_report if @format == :console html_report if @format == :html end
Private Instance Methods
ascii_plain_report()
click to toggle source
# File lib/dawn/reporter.rb, line 282 def ascii_plain_report result = sprintf "%s\n", "scanning #{@engine.target}" result += sprintf "%s\n", "#{@engine.name} v#{@engine.get_mvc_version} detected" unless @engine.name == "Gemfile.lock" result += sprintf "%s\n", "#{@engine.force} v#{@engine.get_mvc_version} detected" if @engine.name == "Gemfile.lock" result += sprintf "%s\n", "applying all security checks" if @ret result += sprintf "%s\n", "#{@engine.applied_checks} security checks applied - #{@engine.skipped_checks} security checks skipped" else result += sprintf "%s\n", "no security checks in the knowledge base" end if @engine.count_vulnerabilities != 0 result += sprintf "%s\n", "#{@engine.count_vulnerabilities} vulnerabilities found" @engine.vulnerabilities.each do |vuln| result += sprintf "%s\n", "#{vuln[:name]} check failed\n" result += sprintf "%s\n", "#{vuln[:message].justify(70)}" result += sprintf "%s\n", "Evidence:" vuln[:evidences].each do |evidence| result += sprintf "%s\n", "\t#{evidence}" end result += sprintf "\n\n" end if @engine.has_reflected_xss? result += sprintf "%s\n", "#{@engine.reflected_xss.count} reflected XSS found" @engine.reflected_xss.each do |vuln| result += sprintf "%s\n", "request parameter \"#{vuln[:sink_source]}\" is used without escaping in #{vuln[:sink_view]}. It was read here: #{vuln[:sink_file]}@#{vuln[:sink_line]}" result += sprintf "%s\n", "evidence: #{vuln[:sink_evidence]}" end end else result += sprintf "%s\n", "no vulnerabilities found." end if @engine.mitigated_issues.count != 0 result += sprintf "%s\n", "#{@engine.mitigated_issues.count} mitigated vulnerabilities found" @engine.mitigated_issues.each do |vuln| result += sprintf "%s\n", "#{vuln[:name]} mitigated" vuln[:evidences].each do |evidence| result += sprintf "%s\n", evidence end end end write(result) true end
ascii_tabular_report()
click to toggle source
# File lib/dawn/reporter.rb, line 162 def ascii_tabular_report output = @engine.create_output_dir # 0_First table: executive summary rows = [] rows << ['Dawn version', Dawn::VERSION] unless Dawn::RELEASE == "(development)" rows << ['Dawn development version', Dawn::VERSION] if Dawn::RELEASE == "(development)" rows << ['Scan started', @engine.scan_start] rows << ['Scan duration', "#{@engine.scan_time.round(3)} sec"] rows << ['Target', @engine.target] rows << ['Framework', "#{@engine.name} v#{@engine.get_mvc_version}" ] unless @engine.name == "Gemfile.lock" rows << ['Framework', "#{@engine.force} v#{@engine.get_mvc_version}" ] if @engine.name == "Gemfile.lock" if @ret rows << ['Applied checks', "#{@engine.applied_checks} security checks"] rows << ['Skipped checks', "#{@engine.skipped_checks} security checks"] else rows << ['Applied checks', "No security checks in the knowledge base"] end rows << ['Vulnerabilities found in dependencies', @engine.count_vulnerabilities] rows << ['Vulnerabilities mitigated by external factors', @engine.mitigated_issues.count] unless @engine.mitigated_issues.count == 0 rows << ['Reflected XSS found', @engine.reflected_xss.count] table = Terminal::Table.new :title=>'Scan summary', :rows => rows write_table(File.join(output, 'summary.txt'), table) # 0_a) Application structure rows = [] rows << ['Lines of code', 'to be added soon'] rows << ['Cyclomatic complexity index', 'to be added soon'] rows << ['Models', @engine.models.count] rows << ['Views', @engine.views.count] rows << ['Controllers', @engine.controllers.count] table = Terminal::Table.new :title=>'Application stats', :rows => rows write_table(File.join(output, 'statistics.txt'), table) if @engine.count_vulnerabilities > 0 # 1) Vulnerabilities # 1_a) Third party gem vulnerabilities rows = [] @engine.vulnerabilities.each do |vuln| $logger.error(vuln) rows << [vuln[:name]&.justify(10), vuln[:severity], vuln[:message]&.justify(30), vuln[:remediation]&.justify(15), vuln[:evidences].join&.justify(15)] rows << :separator end table = Terminal::Table.new :title=>"Vulnerabilities", :headings=>['Issue', 'Severity', 'Description', 'Solution', 'Evidences'], :rows=>rows write_table(File.join(output, 'third_party_vulnerabilities.txt'), table) # 1_b) Refleted XXS rows = [] if @engine.has_reflected_xss? @engine.reflected_xss.each do |vuln| rows << [vuln[:sink_source], vuln[:sink_view], "#{vuln[:sink_file]}@#{vuln[:sink_line]}",vuln[:sink_evidence]] rows << :separator end table = Terminal::Table.new :title=>"Reflected Cross Site Scripting", :headings=>['Sink name', 'View', 'Location the sink was read', 'Evidences'], :rows=>rows write_table(File.join(output, 'reflected_xss.txt'), table) end end if @engine.mitigated_issues.count > 0 # 2_Mitigated issues rows = [] @engine.mitigated_issues.each do |vuln| rows << [vuln[:name].justify(10), vuln[:message].justify(50), vuln[:evidences].join.justify(15)] rows << :separator end table = Terminal::Table.new :title=>"Mitigated issues", :headings=>['Issue', 'Description', 'Evidences'], :rows=>rows write_table(File.join(output, 'mitigated_issues.txt'), table) end true end
html_report()
click to toggle source
# File lib/dawn/reporter.rb, line 71 def html_report output = @engine.create_output_dir if @filename.nil? html_head = "<!doctype html>\n<html>\n<head>\n<title>Dawnscanner report for #{File.basename(@engine.target)}</title>" html_head +=" <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css\" integrity=\"sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7\" crossorigin=\"anonymous\">" html_head += "<script src=\"https://code.jquery.com/jquery-2.2.0.min.js\"></script>" html_head += "<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css\" integrity=\"sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r\" crossorigin=\"anonymous\">" html_head += "<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js\" integrity=\"sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS\" crossorigin=\"anonymous\"></script>" html_head += "<style type=\"text/css\">body{padding-top:20px;padding-bottom:40px}.container-narrow{margin:0 auto;max-width:700px}.container-narrow>hr{margin:30px 0}.jumbotron,.marketing{margin:60px 0}.jumbotron{text-align:center}.jumbotron h1{font-size:72px;line-height:1}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing p+h4{margin-top:28px}#wrap{min-height:100%;height:auto!important;height:100%;margin:0 auto -60px}#footer,#push{height:60px}#footer{background-color:#f5f5f5}@media (max-width:767px){#footer{margin-left:-20px;margin-right:-20px;padding-left:20px;padding-right:20px}} </style>" html_head += "</head>\n" html_body = "<body>\n" html_body += "" html_body += "<div id=\"wrap\">\n" html_body += "<div class=\"container-narrow\">\n" html_body += "<div class=\"masthead\">\n" html_body += "<ul class=\"nav nav-pills pull-right\">\n" html_body += "<li class=\"\"><a href=\"http://dawnscanner.org\">Home</a></li>\n" html_body += "<li class=\"active\"><a href=\"https://github.com/thesp0nge/dawnscanner\">Github repo</a></li>\n" html_body += "</ul>\n" html_body += "<h3 class=\"muted\">Dawnscanner</h3>\n" html_body += "</div>\n" html_body += "<h1>Security code review results for \"#{File.basename(@engine.target)}\"</h1>\n" html_body += "<hr />\n" html_body += "<h2>Scan detail</h2>\n" html_body += "<div class=\"row\">\n" html_body += "<p>The scan was last executed #{@engine.scan_start.strftime("%d %b %Y - %T")} and Dawn founds #{@engine.count_vulnerabilities} vulnerabilities</p>\n" html_body += "</div>\n" html_body += "<hr />\n" html_body += "<h2>Scan details</h2>\n" html_body += "<table class=\"table-striped table-bordered table\">\n" html_body += "<thead><tr><td>Key</td><td>Value</td></tr></thead>\n" html_body += "<tbody>\n" html_body += "<tr><td>Dawn version</td><td>#{Dawn::VERSION}</td></tr>" unless Dawn::RELEASE == "(development)\n" html_body += "<tr><td>Dawn development version</td><td>#{Dawn::VERSION}</td></tr>" if Dawn::RELEASE == "(development)\n" html_body += "<tr><td>Scan duration</td><td>#{@engine.scan_time.round(3)} sec</td></tr>\n" html_body += "<tr><td>Target</td><td>#{@engine.target}</td></tr>\n" html_body += "<tr><td>MVC detected framework</td><td>#{@engine.name} v#{@engine.get_mvc_version}</td></tr>" unless @engine.name == "Gemfile.lock\n" html_body += "<tr><td>MVC detected framework</td><td>#{@engine.force} v#{@engine.get_mvc_version}</td></tr>" if @engine.name == "Gemfile.lock\n" if @ret html_body += "<tr><td>Applied checks</td><td>#{@engine.applied_checks} security checks</td></tr>\n" html_body += "<tr><td>Skipped checks</td><td>#{@engine.skipped_checks} security checks</td></tr>\n" else html_body += "<tr><td>Applied checks</td><td>No security checks in the knowledge base</td></tr>\n" end html_body+="<tr><td>Vulnerabilities found</td><td>#{@engine.count_vulnerabilities}</td></tr>\n" html_body+="<tr><td>Mitigated issues found</td><td>#{@engine.mitigated_issues.count}</td></tr>\n" html_body+="<tr><td>Reflected XSS</td><td>#{@engine.reflected_xss.count}</td></tr>\n" html_body += "</tbody>\n" html_body += "</table>\n" if @engine.count_vulnerabilities > 0 html_body += "<hr />\n" html_body += "<h2>Vulnerabilities found</h2>\n" html_body += "<table class=\"table-striped table-bordered table\">\n" html_body += "<thead><tr><td>Name</td><td>Severity</td><td>CVSS score</td><td>Description</td><td>Remediation</td></tr></thead>\n" html_body += "<tbody>" @engine.vulnerabilities.each do |vuln| html_body += "<tr><td><a href=\"#{vuln[:cve_link]}\">#{vuln[:name]}</a></td><td>#{vuln[:severity]}</td><td>#{vuln[:cvss_score]}</td><td>#{vuln[:message]}</td><td>#{vuln[:remediation]}</td></tr>\n" end html_body += "</tbody>\n" html_body += "</table>\n" end html_body += "<div id=\"push\"></div>\n" html_body += "<div id=\"footer\">\n" html_body += "<div class=\"container\">\n" html_body += "<p class=\"muted credit\">Proudly generated with <a href=\"http://dawnscanner.org\">Dawnscanner</a> — #{Time.now.strftime("%Y")} — engine v#{Dawn::VERSION} (#{Dawn::RELEASE})</p>\n" html_body += "</div>\n" html_body += "</div>\n" html_body += "</div>\n" html_body += "</div>\n" html_body += "</body>\n" html_body += "</html>" html = html_head + html_body unless @filename.nil? write(html) else write_html(output, html) end true end
is_valid_format?(format)
click to toggle source
# File lib/dawn/reporter.rb, line 65 def is_valid_format?(format) return false if format.nil? return true if (format == :console) || (format == :tabular) || (format == :json) || (format == :html) || (format == :csv) return false # otherwise end
json_report()
click to toggle source
# File lib/dawn/reporter.rb, line 239 def json_report result = {} return {:status=>"KO", :message=>"BUG at #{__FILE__}@#{__LINE__}: target is empty or engine is nil."}.to_json if @engine.target.empty? or @engine.nil? return {:status=>"KO", :message=>"#{target} doesn't exist"}.to_json if ! Dir.exist?(@engine.target) return {:status=>"KO", :message=>"no security checks applied"}.to_json unless @ret result[:status]="OK" result[:dawn_version] = Dawn::VERSION result[:dawn_status] = "Develoment version" if Dawn::RELEASE == "(development)" result[:scan_started] = @engine.scan_start result[:scan_duration] = "#{@engine.scan_time.round(3)} sec" result[:target]=@engine.target result[:mvc]=@engine.name unless @engine.name == "Gemfile.lock" result[:mvc]=@engine.force if @engine.name == "Gemfile.lock" result[:mvc_version]=@engine.get_mvc_version result[:applied_checks_count] = @engine.applied_checks result[:skipped_checks_count] = @engine.skipped_checks result[:vulnerabilities_count]=@engine.count_vulnerabilities result[:mitigated_issues_count] = @engine.mitigated_issues.count result[:reflected_xss_count] = @engine.reflected_xss.count result[:vulnerabilities]=[] @engine.vulnerabilities.each do |v| result[:vulnerabilities] << { :name => v[:name], :cve_link => v[:cve_link], :severity => v[:severity], :cvss_score => v[:cvss_score], :message => v[:message], :remediation => v[:remediation] } end result[:mitigated_vuln] = @engine.mitigated_issues result[:reflected_xss] = [] @engine.reflected_xss.each do |r| result[:reflected_xss] << "request parameter \"#{r[:sink_source]}\"" end write(result.to_json) true end
write(output)
click to toggle source
# File lib/dawn/reporter.rb, line 26 def write(output) puts output if @filename.nil? unless @filename.nil? # $logger.warn "I will use codesake.css, bootstrap.min.css and bootstrap.js stored in ./support/ directory" if @format == :html File.open(@filename, "w") do |f| f.puts output end $logger.info "#{@filename} created (#{output.length} bytes)" end end
write_html(path, content)
click to toggle source
# File lib/dawn/reporter.rb, line 39 def write_html(path, content) css_path = File.join(path, 'css') js_path = File.join(path, 'js') support_path = File.join(File.dirname(__FILE__), '..', '..', 'support') FileUtils.mkdir_p(File.join(path, 'css')) FileUtils.mkdir_p(File.join(path, 'js')) FileUtils.cp(File.join(support_path, 'bootstrap.js'), js_path) FileUtils.cp(File.join(support_path, 'bootstrap.min.css'), css_path) FileUtils.cp(File.join(support_path, 'codesake.css'), css_path) File.open(File.join(path, 'report.html'), "w") do |f| f.puts content end $logger.info "#{File.join(path, 'report.html')} created (#{File.stat(File.join(path, 'report.html')).size} bytes)" end
write_table(path, content)
click to toggle source
# File lib/dawn/reporter.rb, line 58 def write_table(path, content) File.open(path, "w") do |f| f.puts content end $logger.info "#{path} created (#{File.stat(path).size} bytes)" end