brakeman 2.0.0 → 2.1.0

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.
Files changed (39) hide show
  1. data/CHANGES +20 -0
  2. data/README.md +6 -1
  3. data/bin/brakeman +13 -3
  4. data/lib/brakeman.rb +64 -7
  5. data/lib/brakeman/call_index.rb +6 -4
  6. data/lib/brakeman/checks/check_basic_auth.rb +47 -2
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +50 -12
  8. data/lib/brakeman/checks/check_execute.rb +4 -1
  9. data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
  10. data/lib/brakeman/checks/check_sql.rb +101 -154
  11. data/lib/brakeman/options.rb +16 -0
  12. data/lib/brakeman/parsers/rails2_erubis.rb +2 -0
  13. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +2 -0
  14. data/lib/brakeman/parsers/rails3_erubis.rb +2 -0
  15. data/lib/brakeman/processors/alias_processor.rb +19 -4
  16. data/lib/brakeman/processors/controller_alias_processor.rb +2 -3
  17. data/lib/brakeman/processors/gem_processor.rb +5 -4
  18. data/lib/brakeman/processors/lib/find_all_calls.rb +43 -16
  19. data/lib/brakeman/report.rb +39 -640
  20. data/lib/brakeman/report/ignore/config.rb +130 -0
  21. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  22. data/lib/brakeman/report/renderer.rb +2 -0
  23. data/lib/brakeman/report/report_base.rb +279 -0
  24. data/lib/brakeman/report/report_csv.rb +56 -0
  25. data/lib/brakeman/report/report_hash.rb +22 -0
  26. data/lib/brakeman/report/report_html.rb +203 -0
  27. data/lib/brakeman/report/report_json.rb +46 -0
  28. data/lib/brakeman/report/report_table.rb +109 -0
  29. data/lib/brakeman/report/report_tabs.rb +17 -0
  30. data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
  31. data/lib/brakeman/report/templates/overview.html.erb +6 -0
  32. data/lib/brakeman/report/templates/security_warnings.html.erb +1 -1
  33. data/lib/brakeman/scanner.rb +14 -12
  34. data/lib/brakeman/tracker.rb +5 -1
  35. data/lib/brakeman/util.rb +2 -0
  36. data/lib/brakeman/version.rb +1 -1
  37. data/lib/ruby_parser/bm_sexp.rb +12 -1
  38. metadata +179 -90
  39. checksums.yaml +0 -7
@@ -0,0 +1,56 @@
1
+ Brakeman.load_dependency 'csv'
2
+ require "brakeman/report/initializers/faster_csv"
3
+ require "brakeman/report/report_table"
4
+
5
+ class Brakeman::Report::CSV < Brakeman::Report::Table
6
+ def generate_report
7
+ output = csv_header
8
+ output << "\nSUMMARY\n"
9
+
10
+ output << table_to_csv(generate_overview) << "\n"
11
+
12
+ output << table_to_csv(generate_warning_overview) << "\n"
13
+
14
+ #Return output early if only summarizing
15
+ if tracker.options[:summary_only]
16
+ return output
17
+ end
18
+
19
+ if tracker.options[:report_routes] or tracker.options[:debug]
20
+ output << "CONTROLLERS\n"
21
+ output << table_to_csv(generate_controllers) << "\n"
22
+ end
23
+
24
+ if tracker.options[:debug]
25
+ output << "TEMPLATES\n\n"
26
+ output << table_to_csv(generate_templates) << "\n"
27
+ end
28
+
29
+ res = generate_errors
30
+ output << "ERRORS\n" << table_to_csv(res) << "\n" if res
31
+
32
+ res = generate_warnings
33
+ output << "SECURITY WARNINGS\n" << table_to_csv(res) << "\n" if res
34
+
35
+ output << "Controller Warnings\n"
36
+ res = generate_controller_warnings
37
+ output << table_to_csv(res) << "\n" if res
38
+
39
+ output << "Model Warnings\n"
40
+ res = generate_model_warnings
41
+ output << table_to_csv(res) << "\n" if res
42
+
43
+ res = generate_template_warnings
44
+ output << "Template Warnings\n"
45
+ output << table_to_csv(res) << "\n" if res
46
+
47
+ output
48
+ end
49
+
50
+ #Generate header for CSV output
51
+ def csv_header
52
+ header = CSV.generate_line(["Application Path", "Report Generation Time", "Checks Performed", "Rails Version"])
53
+ header << CSV.generate_line([File.expand_path(tracker.options[:app_path]), Time.now.to_s, checks.checks_run.sort.join(", "), rails_version])
54
+ "BRAKEMAN REPORT\n\n" + header
55
+ end
56
+ end
@@ -0,0 +1,22 @@
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
+
20
+ report
21
+ end
22
+ end
@@ -0,0 +1,203 @@
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
+ unless template[:outputs].empty?
51
+ template[:outputs].each do |out|
52
+ out = CGI.escapeHTML(out_processor.format(out))
53
+ template_rows[name] ||= []
54
+ template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
55
+ end
56
+ end
57
+ end
58
+
59
+ template_rows = template_rows.sort_by{|name, value| name.to_s}
60
+
61
+ Brakeman::Report::Renderer.new('template_overview', :locals => {:template_rows => template_rows}).render
62
+ end
63
+
64
+ def render_array template, headings, value_array, locals
65
+ return if value_array.empty?
66
+
67
+ Brakeman::Report::Renderer.new(template, :locals => locals).render
68
+ end
69
+
70
+ def convert_warning warning, original
71
+ warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
72
+ warning["Message"] = with_context original, warning["Message"]
73
+ warning["Warning Type"] = with_link original, warning["Warning Type"]
74
+ warning
75
+ end
76
+
77
+ def with_link warning, message
78
+ "<a rel=\"no-referrer\" href=\"#{warning.link}\">#{message}</a>"
79
+ end
80
+
81
+ def convert_template_warning warning, original
82
+ warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
83
+ warning["Message"] = with_context original, warning["Message"]
84
+ warning["Warning Type"] = with_link original, warning["Warning Type"]
85
+ warning["Called From"] = original.called_from
86
+ warning["Template Name"] = original.template[:name]
87
+ warning
88
+ end
89
+
90
+ def convert_ignored_warning warning, original
91
+ warning = convert_warning(warning, original)
92
+ warning['File'] = original.relative_path
93
+ warning['Note'] = CGI.escapeHTML(@ignore_filter.note_for(original) || "")
94
+ warning
95
+ end
96
+
97
+ #Return header for HTML output. Uses CSS from tracker.options[:html_style]
98
+ def html_header
99
+ if File.exist? tracker.options[:html_style]
100
+ css = File.read tracker.options[:html_style]
101
+ else
102
+ raise "Cannot find CSS stylesheet for HTML: #{tracker.options[:html_style]}"
103
+ end
104
+
105
+ locals = {
106
+ :css => css,
107
+ :tracker => tracker,
108
+ :checks => checks,
109
+ :rails_version => rails_version,
110
+ :brakeman_version => Brakeman::Version
111
+ }
112
+
113
+ Brakeman::Report::Renderer.new('header', :locals => locals).render
114
+ end
115
+
116
+ #Generate HTML for warnings, including context show/hidden via Javascript
117
+ def with_context warning, message
118
+ context = context_for(@app_tree, warning)
119
+ full_message = nil
120
+
121
+ if tracker.options[:message_limit] and tracker.options[:message_limit] > 0 and message.length > tracker.options[:message_limit]
122
+ full_message = html_message(warning, message)
123
+ message = message[0..tracker.options[:message_limit]] << "..."
124
+ end
125
+
126
+ message = html_message(warning, message)
127
+ return message if context.empty? and not full_message
128
+
129
+ @element_id += 1
130
+ code_id = "context#@element_id"
131
+ message_id = "message#@element_id"
132
+ full_message_id = "full_message#@element_id"
133
+ alt = false
134
+ output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" <<
135
+ if full_message
136
+ "<span id='#{message_id}' style='display:block' >#{message}</span>" <<
137
+ "<span id='#{full_message_id}' style='display:none'>#{full_message}</span>"
138
+ else
139
+ message
140
+ end <<
141
+ "<table id='#{code_id}' class='context' style='display:none'>" <<
142
+ "<caption>#{warning_file(warning) || ''}</caption>"
143
+
144
+ unless context.empty?
145
+ if warning.line - 1 == 1 or warning.line + 1 == 1
146
+ error = " near_error"
147
+ elsif 1 == warning.line
148
+ error = " error"
149
+ else
150
+ error = ""
151
+ end
152
+
153
+ output << <<-HTML
154
+ <tr class='context first#{error}'>
155
+ <td class='context_line'>
156
+ <pre class='context'>#{context.first[0]}</pre>
157
+ </td>
158
+ <td class='context'>
159
+ <pre class='context'>#{CGI.escapeHTML context.first[1].chomp}</pre>
160
+ </td>
161
+ </tr>
162
+ HTML
163
+
164
+ if context.length > 1
165
+ output << context[1..-1].map do |code|
166
+ alt = !alt
167
+ if code[0] == warning.line - 1 or code[0] == warning.line + 1
168
+ error = " near_error"
169
+ elsif code[0] == warning.line
170
+ error = " error"
171
+ else
172
+ error = ""
173
+ end
174
+
175
+ <<-HTML
176
+ <tr class='context#{alt ? ' alt' : ''}#{error}'>
177
+ <td class='context_line'>
178
+ <pre class='context'>#{code[0]}</pre>
179
+ </td>
180
+ <td class='context'>
181
+ <pre class='context'>#{CGI.escapeHTML code[1].chomp}</pre>
182
+ </td>
183
+ </tr>
184
+ HTML
185
+ end.join
186
+ end
187
+ end
188
+
189
+ output << "</table></div>"
190
+ end
191
+
192
+ #Escape warning message and highlight user input in HTML output
193
+ def html_message warning, message
194
+ message = CGI.escapeHTML(message)
195
+
196
+ if @highlight_user_input and warning.user_input
197
+ user_input = CGI.escapeHTML(warning.format_user_input)
198
+ message.gsub!(user_input, "<span class=\"user_input\">#{user_input}</span>")
199
+ end
200
+
201
+ message
202
+ end
203
+ end
@@ -0,0 +1,46 @@
1
+ Brakeman.load_dependency 'multi_json'
2
+ require 'brakeman/report/initializers/multi_json'
3
+
4
+ class Brakeman::Report::JSON < Brakeman::Report::Base
5
+ def generate_report
6
+ errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
7
+ app_path = tracker.options[:app_path]
8
+
9
+ warnings = convert_to_hashes all_warnings
10
+
11
+ ignored = convert_to_hashes ignored_warnings
12
+
13
+ scan_info = {
14
+ :app_path => File.expand_path(tracker.options[:app_path]),
15
+ :rails_version => rails_version,
16
+ :security_warnings => all_warnings.length,
17
+ :start_time => tracker.start_time.to_s,
18
+ :end_time => tracker.end_time.to_s,
19
+ :duration => tracker.duration,
20
+ :checks_performed => checks.checks_run.sort,
21
+ :number_of_controllers => tracker.controllers.length,
22
+ # ignore the "fake" model
23
+ :number_of_models => tracker.models.length - 1,
24
+ :number_of_templates => number_of_templates(@tracker),
25
+ :ruby_version => RUBY_VERSION,
26
+ :brakeman_version => Brakeman::Version
27
+ }
28
+
29
+ report_info = {
30
+ :scan_info => scan_info,
31
+ :warnings => warnings,
32
+ :ignored_warnings => ignored,
33
+ :errors => errors
34
+ }
35
+
36
+ MultiJson.dump(report_info, :pretty => true)
37
+ end
38
+
39
+ def convert_to_hashes warnings
40
+ warnings.map do |w|
41
+ hash = w.to_hash
42
+ hash[:file] = warning_file w
43
+ hash
44
+ end.sort_by { |w| w[:file] }
45
+ end
46
+ end
@@ -0,0 +1,109 @@
1
+ Brakeman.load_dependency 'terminal-table'
2
+
3
+ class Brakeman::Report::Table < Brakeman::Report::Base
4
+ def generate_report
5
+ out = text_header <<
6
+ "\n\n+SUMMARY+\n\n" <<
7
+ truncate_table(generate_overview.to_s) << "\n\n" <<
8
+ truncate_table(generate_warning_overview.to_s) << "\n"
9
+
10
+ #Return output early if only summarizing
11
+ return out if tracker.options[:summary_only]
12
+
13
+ if tracker.options[:report_routes] or tracker.options[:debug]
14
+ out << "\n+CONTROLLERS+\n" <<
15
+ truncate_table(generate_controllers.to_s) << "\n"
16
+ end
17
+
18
+ if tracker.options[:debug]
19
+ out << "\n+TEMPLATES+\n\n" <<
20
+ truncate_table(generate_templates.to_s) << "\n"
21
+ end
22
+
23
+ res = generate_errors
24
+ out << "+Errors+\n" << truncate_table(res.to_s) if res
25
+
26
+ res = generate_warnings
27
+ out << "\n\n+SECURITY WARNINGS+\n\n" << truncate_table(res.to_s) if res
28
+
29
+ res = generate_controller_warnings
30
+ out << "\n\n\nController Warnings:\n\n" << truncate_table(res.to_s) if res
31
+
32
+ res = generate_model_warnings
33
+ out << "\n\n\nModel Warnings:\n\n" << truncate_table(res.to_s) if res
34
+
35
+ res = generate_template_warnings
36
+ out << "\n\nView Warnings:\n\n" << truncate_table(res.to_s) if res
37
+
38
+ out << "\n"
39
+ out
40
+ end
41
+
42
+ def generate_overview
43
+ num_warnings = all_warnings.length
44
+
45
+ Terminal::Table.new(:headings => ['Scanned/Reported', 'Total']) do |t|
46
+ t.add_row ['Controllers', tracker.controllers.length]
47
+ t.add_row ['Models', tracker.models.length - 1]
48
+ t.add_row ['Templates', number_of_templates(@tracker)]
49
+ t.add_row ['Errors', tracker.errors.length]
50
+ t.add_row ['Security Warnings', "#{num_warnings} (#{warnings_summary[:high_confidence]})"]
51
+ t.add_row ['Ignored Warnings', ignored_warnings.length] unless ignored_warnings.empty?
52
+ end
53
+ end
54
+
55
+ #Generate listings of templates and their output
56
+ def generate_templates
57
+ out_processor = Brakeman::OutputProcessor.new
58
+ template_rows = {}
59
+ tracker.templates.each do |name, template|
60
+ unless template[:outputs].empty?
61
+ template[:outputs].each do |out|
62
+ out = out_processor.format out
63
+ template_rows[name] ||= []
64
+ template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
65
+ end
66
+ end
67
+ end
68
+
69
+ template_rows = template_rows.sort_by{|name, value| name.to_s}
70
+
71
+ output = ''
72
+ template_rows.each do |template|
73
+ output << template.first.to_s << "\n\n"
74
+ table = Terminal::Table.new(:headings => ['Output']) do |t|
75
+ # template[1] is an array of calls
76
+ template[1].each do |v|
77
+ t.add_row [v]
78
+ end
79
+ end
80
+
81
+ output << table.to_s << "\n\n"
82
+ end
83
+
84
+ output
85
+ end
86
+
87
+ def render_array template, headings, value_array, locals
88
+ return if value_array.empty?
89
+
90
+ Terminal::Table.new(:headings => headings) do |t|
91
+ value_array.each { |value_row| t.add_row value_row }
92
+ end
93
+ end
94
+
95
+ #Generate header for text output
96
+ def text_header
97
+ <<-HEADER
98
+
99
+ +BRAKEMAN REPORT+
100
+
101
+ Application path: #{File.expand_path tracker.options[:app_path]}
102
+ Rails version: #{rails_version}
103
+ Brakeman version: #{Brakeman::Version}
104
+ Started at #{tracker.start_time}
105
+ Duration: #{tracker.duration} seconds
106
+ Checks run: #{checks.checks_run.sort.join(", ")}
107
+ HEADER
108
+ end
109
+ end
@@ -0,0 +1,17 @@
1
+ #Generated tab-separated output suitable for the Jenkins Brakeman Plugin:
2
+ #https://github.com/presidentbeef/brakeman-jenkins-plugin
3
+ class Brakeman::Report::Tabs < Brakeman::Report::Base
4
+ def generate_report
5
+ [[:warnings, "General"], [:controller_warnings, "Controller"],
6
+ [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
7
+
8
+ checks.send(meth).map do |w|
9
+ line = w.line || 0
10
+ w.warning_type.gsub!(/[^\w\s]/, ' ')
11
+ "#{warning_file(w, :absolute)}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
12
+ end.join "\n"
13
+
14
+ end.join "\n"
15
+
16
+ end
17
+ end