brakeman-lib 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +872 -0
- data/FEATURES +16 -0
- data/README.md +169 -0
- data/WARNING_TYPES +95 -0
- data/bin/brakeman +89 -0
- data/lib/brakeman.rb +495 -0
- data/lib/brakeman/app_tree.rb +161 -0
- data/lib/brakeman/brakeman.rake +17 -0
- data/lib/brakeman/call_index.rb +219 -0
- data/lib/brakeman/checks.rb +191 -0
- data/lib/brakeman/checks/base_check.rb +518 -0
- data/lib/brakeman/checks/check_basic_auth.rb +88 -0
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/brakeman/checks/check_content_tag.rb +160 -0
- data/lib/brakeman/checks/check_create_with.rb +75 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
- data/lib/brakeman/checks/check_default_routes.rb +86 -0
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +36 -0
- data/lib/brakeman/checks/check_execute.rb +167 -0
- data/lib/brakeman/checks/check_file_access.rb +63 -0
- data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
- data/lib/brakeman/checks/check_header_dos.rb +31 -0
- data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_encoding.rb +47 -0
- data/lib/brakeman/checks/check_json_parsing.rb +107 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +115 -0
- data/lib/brakeman/checks/check_mail_to.rb +49 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
- data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
- data/lib/brakeman/checks/check_model_attributes.rb +119 -0
- data/lib/brakeman/checks/check_model_serialize.rb +67 -0
- data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +215 -0
- data/lib/brakeman/checks/check_regex_dos.rb +69 -0
- data/lib/brakeman/checks/check_render.rb +92 -0
- data/lib/brakeman/checks/check_render_dos.rb +37 -0
- data/lib/brakeman/checks/check_render_inline.rb +54 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_route_dos.rb +42 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
- data/lib/brakeman/checks/check_secrets.rb +40 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
- data/lib/brakeman/checks/check_send.rb +48 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
- data/lib/brakeman/checks/check_session_settings.rb +170 -0
- data/lib/brakeman/checks/check_simple_format.rb +59 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
- data/lib/brakeman/checks/check_sql.rb +660 -0
- data/lib/brakeman/checks/check_sql_cves.rb +101 -0
- data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
- data/lib/brakeman/checks/check_strip_tags.rb +89 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/brakeman/checks/check_translate_bug.rb +45 -0
- data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
- data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
- data/lib/brakeman/checks/check_validation_regex.rb +116 -0
- data/lib/brakeman/checks/check_weak_hash.rb +151 -0
- data/lib/brakeman/checks/check_without_protection.rb +80 -0
- data/lib/brakeman/checks/check_xml_dos.rb +51 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/brakeman/file_parser.rb +50 -0
- data/lib/brakeman/format/style.css +133 -0
- data/lib/brakeman/options.rb +301 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
- data/lib/brakeman/parsers/template_parser.rb +89 -0
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +1013 -0
- data/lib/brakeman/processors/base_processor.rb +277 -0
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
- data/lib/brakeman/processors/controller_processor.rb +326 -0
- data/lib/brakeman/processors/erb_template_processor.rb +80 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
- data/lib/brakeman/processors/gem_processor.rb +57 -0
- data/lib/brakeman/processors/haml_template_processor.rb +190 -0
- data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
- data/lib/brakeman/processors/lib/find_call.rb +183 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/brakeman/processors/lib/render_helper.rb +181 -0
- data/lib/brakeman/processors/lib/render_path.rb +107 -0
- data/lib/brakeman/processors/lib/route_helper.rb +68 -0
- data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
- data/lib/brakeman/processors/library_processor.rb +119 -0
- data/lib/brakeman/processors/model_processor.rb +191 -0
- data/lib/brakeman/processors/output_processor.rb +171 -0
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +107 -0
- data/lib/brakeman/processors/template_alias_processor.rb +116 -0
- data/lib/brakeman/processors/template_processor.rb +74 -0
- data/lib/brakeman/report.rb +78 -0
- data/lib/brakeman/report/config/remediation.yml +71 -0
- data/lib/brakeman/report/ignore/config.rb +135 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +286 -0
- data/lib/brakeman/report/report_codeclimate.rb +70 -0
- data/lib/brakeman/report/report_csv.rb +55 -0
- data/lib/brakeman/report/report_hash.rb +23 -0
- data/lib/brakeman/report/report_html.rb +216 -0
- data/lib/brakeman/report/report_json.rb +42 -0
- data/lib/brakeman/report/report_markdown.rb +156 -0
- data/lib/brakeman/report/report_table.rb +107 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
- data/lib/brakeman/report/templates/header.html.erb +58 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/overview.html.erb +38 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
- data/lib/brakeman/rescanner.rb +483 -0
- data/lib/brakeman/scanner.rb +317 -0
- data/lib/brakeman/tracker.rb +347 -0
- data/lib/brakeman/tracker/collection.rb +93 -0
- data/lib/brakeman/tracker/config.rb +101 -0
- data/lib/brakeman/tracker/constants.rb +101 -0
- data/lib/brakeman/tracker/controller.rb +161 -0
- data/lib/brakeman/tracker/library.rb +17 -0
- data/lib/brakeman/tracker/model.rb +90 -0
- data/lib/brakeman/tracker/template.rb +33 -0
- data/lib/brakeman/util.rb +481 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +255 -0
- data/lib/brakeman/warning_codes.rb +111 -0
- data/lib/ruby_parser/bm_sexp.rb +610 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +362 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "yaml"
|
|
3
|
+
|
|
4
|
+
class Brakeman::Report::CodeClimate < Brakeman::Report::Base
|
|
5
|
+
DOCUMENTATION_PATH = File.expand_path("../../../../docs/warning_types", __FILE__)
|
|
6
|
+
REMEDIATION_POINTS_CONFIG_PATH = File.expand_path("../config/remediation.yml", __FILE__)
|
|
7
|
+
REMEDIATION_POINTS_DEFAULT = 300_000
|
|
8
|
+
|
|
9
|
+
def generate_report
|
|
10
|
+
all_warnings.map { |warning| issue_json(warning) }.join("\0")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def issue_json(warning)
|
|
16
|
+
warning_code_name = name_for(warning.warning_code)
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
type: "Issue",
|
|
20
|
+
check_name: warning_code_name,
|
|
21
|
+
description: warning.message,
|
|
22
|
+
fingerprint: warning.fingerprint,
|
|
23
|
+
categories: ["Security"],
|
|
24
|
+
severity: severity_level_for(warning.confidence),
|
|
25
|
+
remediation_points: remediation_points_for(warning_code_name),
|
|
26
|
+
location: {
|
|
27
|
+
path: warning.relative_path,
|
|
28
|
+
lines: {
|
|
29
|
+
begin: warning.line || 1,
|
|
30
|
+
end: warning.line || 1,
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
content: {
|
|
34
|
+
body: content_for(warning.warning_code, warning.link)
|
|
35
|
+
}
|
|
36
|
+
}.to_json
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def severity_level_for(confidence)
|
|
40
|
+
if confidence == 0
|
|
41
|
+
"critical"
|
|
42
|
+
else
|
|
43
|
+
"normal"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def remediation_points_for(warning_code)
|
|
48
|
+
@remediation_points ||= YAML.load_file(REMEDIATION_POINTS_CONFIG_PATH)
|
|
49
|
+
@remediation_points.fetch(name_for(warning_code), REMEDIATION_POINTS_DEFAULT)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def name_for(warning_code)
|
|
53
|
+
@warning_codes ||= Brakeman::WarningCodes::Codes.invert
|
|
54
|
+
@warning_codes[warning_code].to_s
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def content_for(warning_code, link)
|
|
58
|
+
@contents ||= {}
|
|
59
|
+
unless link.nil?
|
|
60
|
+
@contents[warning_code] ||= local_content_for(link) || "Read more: #{link}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def local_content_for(link)
|
|
65
|
+
directory = link.split("/").last
|
|
66
|
+
filename = File.join(DOCUMENTATION_PATH, directory, "index.markdown")
|
|
67
|
+
|
|
68
|
+
File.read(filename) if File.exist?(filename)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require "brakeman/report/report_table"
|
|
3
|
+
|
|
4
|
+
class Brakeman::Report::CSV < Brakeman::Report::Table
|
|
5
|
+
def generate_report
|
|
6
|
+
output = csv_header
|
|
7
|
+
output << "\nSUMMARY\n"
|
|
8
|
+
|
|
9
|
+
output << table_to_csv(generate_overview) << "\n"
|
|
10
|
+
|
|
11
|
+
output << table_to_csv(generate_warning_overview) << "\n"
|
|
12
|
+
|
|
13
|
+
#Return output early if only summarizing
|
|
14
|
+
if tracker.options[:summary_only]
|
|
15
|
+
return output
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if tracker.options[:report_routes] or tracker.options[:debug]
|
|
19
|
+
output << "CONTROLLERS\n"
|
|
20
|
+
output << table_to_csv(generate_controllers) << "\n"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if tracker.options[:debug]
|
|
24
|
+
output << "TEMPLATES\n\n"
|
|
25
|
+
output << table_to_csv(generate_templates) << "\n"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
res = generate_errors
|
|
29
|
+
output << "ERRORS\n" << table_to_csv(res) << "\n" if res
|
|
30
|
+
|
|
31
|
+
res = generate_warnings
|
|
32
|
+
output << "SECURITY WARNINGS\n" << table_to_csv(res) << "\n" if res
|
|
33
|
+
|
|
34
|
+
output << "Controller Warnings\n"
|
|
35
|
+
res = generate_controller_warnings
|
|
36
|
+
output << table_to_csv(res) << "\n" if res
|
|
37
|
+
|
|
38
|
+
output << "Model Warnings\n"
|
|
39
|
+
res = generate_model_warnings
|
|
40
|
+
output << table_to_csv(res) << "\n" if res
|
|
41
|
+
|
|
42
|
+
res = generate_template_warnings
|
|
43
|
+
output << "Template Warnings\n"
|
|
44
|
+
output << table_to_csv(res) << "\n" if res
|
|
45
|
+
|
|
46
|
+
output
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Generate header for CSV output
|
|
50
|
+
def csv_header
|
|
51
|
+
header = CSV.generate_line(["Application Path", "Report Generation Time", "Checks Performed", "Rails Version"])
|
|
52
|
+
header << CSV.generate_line([File.expand_path(tracker.app_path), Time.now.to_s, checks.checks_run.sort.join(", "), rails_version])
|
|
53
|
+
"BRAKEMAN REPORT\n\n" + header
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generates a hash table for use in Brakeman tests
|
|
2
|
+
class Brakeman::Report::Hash < Brakeman::Report::Base
|
|
3
|
+
def generate_report
|
|
4
|
+
report = { :errors => tracker.errors,
|
|
5
|
+
:controllers => tracker.controllers,
|
|
6
|
+
:models => tracker.models,
|
|
7
|
+
:templates => tracker.templates
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[:generic_warnings, :controller_warnings, :model_warnings, :template_warnings].each do |meth|
|
|
11
|
+
report[meth] = self.send(meth)
|
|
12
|
+
report[meth].each do |w|
|
|
13
|
+
w.message = w.format_message
|
|
14
|
+
w.context = context_for(@app_tree, w).join("\n")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
report[:config] = tracker.config
|
|
19
|
+
report[:checks_run] = tracker.checks.checks_run
|
|
20
|
+
|
|
21
|
+
report
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
3
|
+
class Brakeman::Report::HTML < Brakeman::Report::Base
|
|
4
|
+
HTML_CONFIDENCE = [ "<span class='high-confidence'>High</span>",
|
|
5
|
+
"<span class='med-confidence'>Medium</span>",
|
|
6
|
+
"<span class='weak-confidence'>Weak</span>" ]
|
|
7
|
+
|
|
8
|
+
def initialize *args
|
|
9
|
+
super
|
|
10
|
+
|
|
11
|
+
@element_id = 0 #Used for HTML ids
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_report
|
|
15
|
+
out = html_header <<
|
|
16
|
+
generate_overview <<
|
|
17
|
+
generate_warning_overview.to_s
|
|
18
|
+
|
|
19
|
+
# Return early if only summarizing
|
|
20
|
+
return out if tracker.options[:summary_only]
|
|
21
|
+
|
|
22
|
+
out << generate_controllers.to_s if tracker.options[:report_routes] or tracker.options[:debug]
|
|
23
|
+
out << generate_templates.to_s if tracker.options[:debug]
|
|
24
|
+
out << generate_errors.to_s
|
|
25
|
+
out << generate_warnings.to_s
|
|
26
|
+
out << generate_controller_warnings.to_s
|
|
27
|
+
out << generate_model_warnings.to_s
|
|
28
|
+
out << generate_template_warnings.to_s
|
|
29
|
+
out << generate_ignored_warnings.to_s
|
|
30
|
+
out << "</body></html>"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_overview
|
|
34
|
+
locals = {
|
|
35
|
+
:tracker => tracker,
|
|
36
|
+
:warnings => all_warnings.length,
|
|
37
|
+
:warnings_summary => warnings_summary,
|
|
38
|
+
:number_of_templates => number_of_templates(@tracker),
|
|
39
|
+
:ignored_warnings => ignored_warnings.length
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Brakeman::Report::Renderer.new('overview', :locals => locals).render
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#Generate listings of templates and their output
|
|
46
|
+
def generate_templates
|
|
47
|
+
out_processor = Brakeman::OutputProcessor.new
|
|
48
|
+
template_rows = {}
|
|
49
|
+
tracker.templates.each do |name, template|
|
|
50
|
+
template.each_output do |out|
|
|
51
|
+
out = CGI.escapeHTML(out_processor.format(out))
|
|
52
|
+
template_rows[name] ||= []
|
|
53
|
+
template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
template_rows = template_rows.sort_by{|name, value| name.to_s}
|
|
58
|
+
|
|
59
|
+
Brakeman::Report::Renderer.new('template_overview', :locals => {:template_rows => template_rows}).render
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def render_array template, headings, value_array, locals
|
|
63
|
+
return if value_array.empty?
|
|
64
|
+
|
|
65
|
+
Brakeman::Report::Renderer.new(template, :locals => locals).render
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def convert_warning warning, original
|
|
69
|
+
warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
|
|
70
|
+
warning["Message"] = with_context original, warning["Message"]
|
|
71
|
+
warning["Warning Type"] = with_link original, warning["Warning Type"]
|
|
72
|
+
warning
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def with_link warning, message
|
|
76
|
+
"<a rel=\"no-referrer\" href=\"#{warning.link}\">#{message}</a>"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def convert_template_warning warning, original
|
|
80
|
+
warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
|
|
81
|
+
warning["Message"] = with_context original, warning["Message"]
|
|
82
|
+
warning["Warning Type"] = with_link original, warning["Warning Type"]
|
|
83
|
+
warning["Called From"] = original.called_from
|
|
84
|
+
warning["Template Name"] = original.template.name
|
|
85
|
+
warning
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def convert_ignored_warning warning, original
|
|
89
|
+
warning = convert_warning(warning, original)
|
|
90
|
+
warning['File'] = original.relative_path
|
|
91
|
+
warning['Note'] = CGI.escapeHTML(@ignore_filter.note_for(original) || "")
|
|
92
|
+
warning
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#Return header for HTML output. Uses CSS from tracker.options[:html_style]
|
|
96
|
+
def html_header
|
|
97
|
+
if File.exist? tracker.options[:html_style]
|
|
98
|
+
css = File.read tracker.options[:html_style]
|
|
99
|
+
else
|
|
100
|
+
raise "Cannot find CSS stylesheet for HTML: #{tracker.options[:html_style]}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
locals = {
|
|
104
|
+
:css => css,
|
|
105
|
+
:tracker => tracker,
|
|
106
|
+
:checks => checks,
|
|
107
|
+
:rails_version => rails_version,
|
|
108
|
+
:brakeman_version => Brakeman::Version
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Brakeman::Report::Renderer.new('header', :locals => locals).render
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
#Generate HTML for warnings, including context show/hidden via Javascript
|
|
115
|
+
def with_context warning, message
|
|
116
|
+
context = context_for(@app_tree, warning)
|
|
117
|
+
full_message = nil
|
|
118
|
+
|
|
119
|
+
if tracker.options[:message_limit] and tracker.options[:message_limit] > 0 and message.length > tracker.options[:message_limit]
|
|
120
|
+
full_message = html_message(warning, message)
|
|
121
|
+
message = message[0..tracker.options[:message_limit]] << "..."
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
message = html_message(warning, message)
|
|
125
|
+
return message if context.empty? and not full_message
|
|
126
|
+
|
|
127
|
+
@element_id += 1
|
|
128
|
+
code_id = "context#@element_id"
|
|
129
|
+
message_id = "message#@element_id"
|
|
130
|
+
full_message_id = "full_message#@element_id"
|
|
131
|
+
alt = false
|
|
132
|
+
output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" <<
|
|
133
|
+
if full_message
|
|
134
|
+
"<span id='#{message_id}' style='display:block' >#{message}</span>" <<
|
|
135
|
+
"<span id='#{full_message_id}' style='display:none'>#{full_message}</span>"
|
|
136
|
+
else
|
|
137
|
+
message
|
|
138
|
+
end <<
|
|
139
|
+
"<table id='#{code_id}' class='context' style='display:none'>" <<
|
|
140
|
+
"<caption>#{CGI.escapeHTML warning_file(warning) || ''}</caption>"
|
|
141
|
+
|
|
142
|
+
output << <<-HTML
|
|
143
|
+
<thead style='display:none'>
|
|
144
|
+
<tr>
|
|
145
|
+
<th>line number</th>
|
|
146
|
+
<th>line content</th>
|
|
147
|
+
</tr>
|
|
148
|
+
</thead>
|
|
149
|
+
<tbody>
|
|
150
|
+
HTML
|
|
151
|
+
|
|
152
|
+
unless context.empty?
|
|
153
|
+
if warning.line - 1 == 1 or warning.line + 1 == 1
|
|
154
|
+
error = " near_error"
|
|
155
|
+
elsif 1 == warning.line
|
|
156
|
+
error = " error"
|
|
157
|
+
else
|
|
158
|
+
error = ""
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
output << <<-HTML
|
|
162
|
+
<tr class='context first#{error}'>
|
|
163
|
+
<td class='context_line'>
|
|
164
|
+
<pre class='context'>#{context.first[0]}</pre>
|
|
165
|
+
</td>
|
|
166
|
+
<td class='context'>
|
|
167
|
+
<pre class='context'>#{CGI.escapeHTML context.first[1].chomp}</pre>
|
|
168
|
+
</td>
|
|
169
|
+
</tr>
|
|
170
|
+
HTML
|
|
171
|
+
|
|
172
|
+
if context.length > 1
|
|
173
|
+
output << context[1..-1].map do |code|
|
|
174
|
+
alt = !alt
|
|
175
|
+
if code[0] == warning.line - 1 or code[0] == warning.line + 1
|
|
176
|
+
error = " near_error"
|
|
177
|
+
elsif code[0] == warning.line
|
|
178
|
+
error = " error"
|
|
179
|
+
else
|
|
180
|
+
error = ""
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
<<-HTML
|
|
184
|
+
<tr class='context#{alt ? ' alt' : ''}#{error}'>
|
|
185
|
+
<td class='context_line'>
|
|
186
|
+
<pre class='context'>#{code[0]}</pre>
|
|
187
|
+
</td>
|
|
188
|
+
<td class='context'>
|
|
189
|
+
<pre class='context'>#{CGI.escapeHTML code[1].chomp}</pre>
|
|
190
|
+
</td>
|
|
191
|
+
</tr>
|
|
192
|
+
HTML
|
|
193
|
+
end.join
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
output << "</tbody></table></div>"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
#Escape warning message and highlight user input in HTML output
|
|
201
|
+
def html_message warning, message
|
|
202
|
+
message = CGI.escapeHTML(message)
|
|
203
|
+
|
|
204
|
+
if warning.file
|
|
205
|
+
github_url = github_url warning.file, warning.line
|
|
206
|
+
message.gsub!(/(near line \d+)/, "<a href=\"#{github_url}\" target='_blank'>\\1</a>") if github_url
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
if @highlight_user_input and warning.user_input
|
|
210
|
+
user_input = CGI.escapeHTML(warning.format_user_input)
|
|
211
|
+
message.gsub!(user_input, "<span class=\"user_input\">#{user_input}</span>")
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
message
|
|
215
|
+
end
|
|
216
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class Brakeman::Report::JSON < Brakeman::Report::Base
|
|
2
|
+
def generate_report
|
|
3
|
+
errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
|
|
4
|
+
|
|
5
|
+
warnings = convert_to_hashes all_warnings
|
|
6
|
+
|
|
7
|
+
ignored = convert_to_hashes ignored_warnings
|
|
8
|
+
|
|
9
|
+
scan_info = {
|
|
10
|
+
:app_path => tracker.app_path,
|
|
11
|
+
:rails_version => rails_version,
|
|
12
|
+
:security_warnings => all_warnings.length,
|
|
13
|
+
:start_time => tracker.start_time.to_s,
|
|
14
|
+
:end_time => tracker.end_time.to_s,
|
|
15
|
+
:duration => tracker.duration,
|
|
16
|
+
:checks_performed => checks.checks_run.sort,
|
|
17
|
+
:number_of_controllers => tracker.controllers.length,
|
|
18
|
+
# ignore the "fake" model
|
|
19
|
+
:number_of_models => tracker.models.length - 1,
|
|
20
|
+
:number_of_templates => number_of_templates(@tracker),
|
|
21
|
+
:ruby_version => RUBY_VERSION,
|
|
22
|
+
:brakeman_version => Brakeman::Version
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
report_info = {
|
|
26
|
+
:scan_info => scan_info,
|
|
27
|
+
:warnings => warnings,
|
|
28
|
+
:ignored_warnings => ignored,
|
|
29
|
+
:errors => errors
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
JSON.pretty_generate report_info
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def convert_to_hashes warnings
|
|
36
|
+
warnings.map do |w|
|
|
37
|
+
hash = w.to_hash
|
|
38
|
+
hash[:file] = warning_file w
|
|
39
|
+
hash
|
|
40
|
+
end.sort_by { |w| "#{w[:fingerprint]}#{w[:line]}" }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Brakeman.load_brakeman_dependency 'terminal-table'
|
|
2
|
+
|
|
3
|
+
class Brakeman::Report::Markdown < Brakeman::Report::Base
|
|
4
|
+
|
|
5
|
+
class MarkdownTable < Terminal::Table
|
|
6
|
+
|
|
7
|
+
def initialize options = {}, &block
|
|
8
|
+
options[:style] ||= {}
|
|
9
|
+
options[:style].merge!({
|
|
10
|
+
:border_x => '-',
|
|
11
|
+
:border_y => '|',
|
|
12
|
+
:border_i => '|'
|
|
13
|
+
})
|
|
14
|
+
super options, &block
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
super.split("\n")[1...-1].join("\n")
|
|
19
|
+
end
|
|
20
|
+
alias :to_s :render
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_report
|
|
25
|
+
out = "# BRAKEMAN REPORT\n\n" <<
|
|
26
|
+
generate_metadata.to_s << "\n\n" <<
|
|
27
|
+
generate_checks.to_s << "\n\n" <<
|
|
28
|
+
"### SUMMARY\n\n" <<
|
|
29
|
+
generate_overview.to_s << "\n\n" <<
|
|
30
|
+
generate_warning_overview.to_s << "\n\n"
|
|
31
|
+
|
|
32
|
+
#Return output early if only summarizing
|
|
33
|
+
return out if tracker.options[:summary_only]
|
|
34
|
+
|
|
35
|
+
if tracker.options[:report_routes] or tracker.options[:debug]
|
|
36
|
+
out << "### CONTROLLERS" << "\n\n" <<
|
|
37
|
+
generate_controllers.to_s << "\n\n"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if tracker.options[:debug]
|
|
41
|
+
out << "### TEMPLATES\n\n" <<
|
|
42
|
+
generate_templates.to_s << "\n\n"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
res = generate_errors
|
|
46
|
+
out << "### Errors\n\n" << res.to_s << "\n\n" if res
|
|
47
|
+
|
|
48
|
+
res = generate_warnings
|
|
49
|
+
out << "### SECURITY WARNINGS\n\n" << res.to_s << "\n\n" if res
|
|
50
|
+
|
|
51
|
+
res = generate_controller_warnings
|
|
52
|
+
out << "### Controller Warnings:\n\n" << res.to_s << "\n\n" if res
|
|
53
|
+
|
|
54
|
+
res = generate_model_warnings
|
|
55
|
+
out << "### Model Warnings:\n\n" << res.to_s << "\n\n" if res
|
|
56
|
+
|
|
57
|
+
res = generate_template_warnings
|
|
58
|
+
out << "### View Warnings:\n\n" << res.to_s << "\n\n" if res
|
|
59
|
+
|
|
60
|
+
out
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_metadata
|
|
64
|
+
MarkdownTable.new(
|
|
65
|
+
:headings =>
|
|
66
|
+
['Application path', 'Rails version', 'Brakeman version', 'Started at', 'Duration']
|
|
67
|
+
) do |t|
|
|
68
|
+
t.add_row([
|
|
69
|
+
tracker.app_path,
|
|
70
|
+
rails_version,
|
|
71
|
+
Brakeman::Version,
|
|
72
|
+
tracker.start_time,
|
|
73
|
+
"#{tracker.duration} seconds",
|
|
74
|
+
])
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def generate_checks
|
|
79
|
+
MarkdownTable.new(:headings => ['Checks performed']) do |t|
|
|
80
|
+
t.add_row([checks.checks_run.sort.join(", ")])
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def generate_overview
|
|
85
|
+
num_warnings = all_warnings.length
|
|
86
|
+
|
|
87
|
+
MarkdownTable.new(:headings => ['Scanned/Reported', 'Total']) do |t|
|
|
88
|
+
t.add_row ['Controllers', tracker.controllers.length]
|
|
89
|
+
t.add_row ['Models', tracker.models.length - 1]
|
|
90
|
+
t.add_row ['Templates', number_of_templates(@tracker)]
|
|
91
|
+
t.add_row ['Errors', tracker.errors.length]
|
|
92
|
+
t.add_row ['Security Warnings', "#{num_warnings} (#{warnings_summary[:high_confidence]})"]
|
|
93
|
+
t.add_row ['Ignored Warnings', ignored_warnings.length] unless ignored_warnings.empty?
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
#Generate listings of templates and their output
|
|
98
|
+
def generate_templates
|
|
99
|
+
out_processor = Brakeman::OutputProcessor.new
|
|
100
|
+
template_rows = {}
|
|
101
|
+
tracker.templates.each do |name, template|
|
|
102
|
+
template.each_output do |out|
|
|
103
|
+
out = out_processor.format out
|
|
104
|
+
template_rows[name] ||= []
|
|
105
|
+
template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
template_rows = template_rows.sort_by{|name, value| name.to_s}
|
|
110
|
+
|
|
111
|
+
output = ''
|
|
112
|
+
template_rows.each do |template|
|
|
113
|
+
output << template.first.to_s << "\n\n"
|
|
114
|
+
table = MarkdownTable.new(:headings => ['Output']) do |t|
|
|
115
|
+
# template[1] is an array of calls
|
|
116
|
+
template[1].each do |v|
|
|
117
|
+
t.add_row [v]
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
output << table.to_s << "\n\n"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
output
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def render_array template, headings, value_array, locals
|
|
128
|
+
return if value_array.empty?
|
|
129
|
+
|
|
130
|
+
MarkdownTable.new(:headings => headings) do |t|
|
|
131
|
+
value_array.each { |value_row| t.add_row value_row }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def convert_warning warning, original
|
|
136
|
+
warning["Confidence"] = TEXT_CONFIDENCE[warning["Confidence"]]
|
|
137
|
+
warning["Message"] = markdown_message original, warning["Message"]
|
|
138
|
+
warning["Warning Type"] = "[#{warning['Warning Type']}](#{original.link})" if original.link
|
|
139
|
+
warning
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Escape and code format warning message
|
|
143
|
+
def markdown_message warning, message
|
|
144
|
+
if warning.file
|
|
145
|
+
github_url = github_url warning.file, warning.line
|
|
146
|
+
message.gsub!(/(near line \d+)/, "[\\1](#{github_url})") if github_url
|
|
147
|
+
end
|
|
148
|
+
if warning.code
|
|
149
|
+
code = warning.format_code
|
|
150
|
+
message.gsub(code, "`#{code.gsub('`','``').gsub(/\A``|``\z/, '` `')}`")
|
|
151
|
+
else
|
|
152
|
+
message
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|